Python — потрясающий язык программирования! Это один из самых популярных языков для разработки приложений искусственного интеллекта и машинного обучения. Обладая очень простым для изучения синтаксисом, Python имеет некоторые особенности, которые отличают его от других языков. И в этом руководстве мы как раз поговорим об этих особых атрибутах языка программирования Python.
Прочитав данное руководство, вы узнаете:
zip() и enumerate();Итак, приступим!
Данное руководство разбито на четыре части. Вот они:
Ниже приведен код для импорта необходимых нам библиотек.
from tensorflow import keras from tensorflow.keras.preprocessing.image import ImageDataGenerator import numpy as np import matplotlib.pyplot as plt import math
Синтаксис представления списков обеспечивает простой и надежный способ для создания новых списков из уже существующих. Допустим, нам нужен новый список, в котором каждый элемент старого списка будет умножен на 3. При помощи цикла for мы можем это сделать следующим образом:
original_list = [1, 2, 3, 4]
times3_list = []
for i in original_list:
times3_list.append(i*3)
print(times3_list) Результат:
[3, 6, 9, 12]
Более короткий метод, использующий представление списков, укладывается в одну строчку кода:
time3_list_awesome_method = [i*3 for i in original_list] print(time3_list_awesome_method)
Результат:
[3, 6, 9, 12]
Таким образом вы даже можете создать список, отвечающий определенным условиям. Предположим, мы хотим добавлять в новый список только четные числа.
even_list_awesome_method = [i for i in original_list if i%2==0] print(even_list_awesome_method)
Результат:
[2, 4]
Также в подобной конструкции можно использовать ключевое слово else. Допустим, мы хотим оставить четные числа как есть, а нечетные заменить нулями.
new_list_awesome_method = [i if i%2==0 else 0 for i in original_list] print(new_list_awesome_method)
Результат:
[0, 2, 0, 4][python_ad_block]
С помощью представления списков также можно заменить вложенные циклы for. Рассмотрим следующий пример:
colors = ["red", "green", "blue"]
animals = ["cat", "dog", "bird"]
newlist = []
for c in colors:
for a in animals:
newlist.append(c + " " + a)
print(newlist) Результат:
['red cat', 'red dog', 'red bird', 'green cat', 'green dog', 'green bird', 'blue cat', 'blue dog', 'blue bird']
Тот же результат может быть достигнут при помощи представления списков:
colors = ["red", "green", "blue"] animals = ["cat", "dog", "bird"] newlist = [c+" "+a for c in colors for a in animals] print(newlist)
Синтаксис для представления списков имеет следующий вид:
newlist = [expression for item in iterable if condition == True]
или:
newList = [expression if condition == True else expression for item in iterable]
Представление словарей похоже на представление списков за исключением того, что в качестве элементов у нас теперь пары ключ-значение. Рассмотрим пример. Мы изменим каждое значение словаря, объединив его со строкой «number«.
original_dict = {1: 'one', 2: 'two', 3: 'three', 4: 'four'}
new_dict = {key:'number ' + value for (key, value) in original_dict.items()}
print(new_dict) Результат:
Output
{1: 'number one', 2: 'number two', 3: 'number three', 4: 'number four'} И опять же возможны условные выражения. Мы можем добавлять пары ключ-значения в новый словарь на основании определенных критериев.
#Only add keys which are greater than 2
new_dict_high_keys = {key:'number ' + value for (key, value) in original_dict.items() if key>2}
print(new_dict_high_keys)
# Only change values with key>2
new_dict_2 = {key:('number ' + value if key>2 else value) for (key, value) in original_dict.items() }
print(new_dict_2) Результат:
{3: 'number three', 4: 'number four'}
{1: 'one', 2: 'two', 3: 'number three', 4: 'number four'} В Python итерируемый объект определяется как любая структура данных, способная возвращать все свои элементы по одному. То есть вы можете использовать цикл for для дальнейшей обработки всех элементов один за другим. В Python есть две дополнительные конструкции, которые упрощают использование циклов for. Это функции enumerate() и zip().
От редакции Pythonist. По теме итерируемых объектов и их перебора рекомендуем почитать статью «Встроенные функции для перебора последовательностей в Python».
В традиционных языках программирования вам нужна переменная цикла для перебора различных значений контейнера. В Python это упростили: вы можете получить доступ к переменной цикла вместе со значением итерируемого объекта. Функция enumerate(x) возвращает две итерируемые переменные. Одна из них изменяется в диапазоне от 0 до len(x) - 1, а другая представляет собой элементы x.
Пример ее использования enumerate():
name = ['Triangle', 'Square', 'Hexagon', 'Pentagon']
# enumerate returns two iterables
for i, n in enumerate(name):
print(i, 'name: ', n) Результат:
0 name: Triangle 1 name: Square 2 name: Hexagon 3 name: Pentagon
По умолчанию функция enumerate стартует со значения 0, но мы можем задать и другое начальное значение. Иногда это может быть удобным:
data = [1,4,1,5,9,2,6,5,3,5,8,9,7,9,3]
for n, digit in enumerate(data[5:], 6):
print("The %d-th digit is %d" % (n, digit)) Результат:
The 6-th digit is 2 The 7-th digit is 6 The 8-th digit is 5 The 9-th digit is 3 The 10-th digit is 5 The 11-th digit is 8 The 12-th digit is 9 The 13-th digit is 7 The 14-th digit is 9 The 15-th digit is 3
Функция zip() позволяет создавать итерируемый объект, состоящий из кортежей. Zip принимает в качестве аргумента несколько последовательностей (m1, m2,…, mn), в результате чего создается итерируемый объект, состоящий из i кортежей. В каждом кортеже содержится по одному элементу из каждого контейнера. Таким образом, i-й кортеж представляет собой(m1i, m2i,…, mni).
Если переданные объекты имеют разную длину, тогда длина сформированного итерируемого объекта (т.е. количество кортежей) будет равна длине минимального объекта.
От редакции Pythonist. Подробнее о функции zip() и ее применении можно почитать в статье «Используем zip() для парной итерации».
Ниже дан пример совместного применения функций zip() и enumerate().
sides = [3, 4, 6, 5]
colors = ['red', 'green', 'yellow', 'blue']
shapes = zip(name, sides, colors)
# Tuples are created from one item from each list
print(set(shapes))
# Easy to use enumerate and zip together for iterating through multiple lists in one go
for i, (n, s, c) in enumerate(zip(name, sides, colors)):
print(i, 'Shape- ', n, '; Sides ', s) Результат:
{('Triangle', 3, 'red'), ('Square', 4, 'green'), ('Hexagon', 6, 'yellow'), ('Pentagon', 5, 'blue')}
0 Shape- Triangle ; Sides 3
1 Shape- Square ; Sides 4
2 Shape- Hexagon ; Sides 6
3 Shape- Pentagon ; Sides 5 Язык Python допускает применение вложенных функций, в которых вы можете определить внутреннюю функцию внутри внешней. В Python есть несколько замечательных свойств, связанных со вложенными функциями.
Ниже дан пример применения этих возможностей.
def circle(r):
area = 0
def area_obj():
nonlocal area
area = math.pi * r * r
print("area_obj")
return area_obj
def circle(r):
area_val = math.pi * r * r
def area():
print(area_val)
return area
# returns area_obj(). The value of r passed is retained
circle_1 = circle(1)
circle_2 = circle(2)
# Calling area_obj() with radius = 1
circle_1()
# Calling area_obj() with radius = 2
circle_2() Результат:
3.141592653589793 12.566370614359172
Декораторы — мощный инструмент в Python. Вы можете их использовать для тонкой настройки работы класса или функции. Декораторы можно считать функцией, которая применена к другой функции. Чтобы определить функцию-декоратор для декорируемой функции, используется знак @ и после него название функции. Из этого следует, что декоратор принимает в качестве аргумента функцию, которою он декорирует.
Рассмотрим функцию square_decorator(), которая в качестве аргумента принимает функцию и в результате также выдают функцию.
square_it() принимает аргумент arg.square_it() применяет функцию к arg и возводит результат в квадрат.sin, в функцию square_decorator(), которая, в свою очередь, вернет sin2(x).square_decorator() в качестве декоратора, используя для этого специальный символ @, как показано ниже. Функция plus_one(x) возвращает x + 1. Эта функция декорирована функцией square_decorator() и, следовательно, мы получаем (x + 1)2.def square_decorator(function):
def square_it(arg):
x = function(arg)
return x*x
return square_it
size_sq = square_decorator(len)
print(size_sq([1,2,3]))
sin_sq = square_decorator(math.sin)
print(sin_sq(math.pi/4))
@square_decorator
def plus_one(a):
return a+1
a = plus_one(3)
print(a) Результат:
9 0.4999999999999999 16
Генераторы в Python позволяют создавать последовательности. Вместо выражения return генераторы возвращают значения путем многократного применения выражения yield. При каждом новом вызове функции возвращается следующее значение итерируемой последовательности.
Генератор может быть вызван через функцию next(). При каждом вызове next() возвращается следующее значение генератора. Ниже приведен пример создания последовательности Фибоначчи до числа x.
def get_fibonacci(x):
x0 = 0
x1 = 1
for i in range(x):
yield x0
temp = x0 + x1
x0 = x1
x1 = temp
f = get_fibonacci(6)
for i in range(6):
print(next(f)) Результат:
0 1 1 2 3 5
Одно из типичных применений генераторов — это использование генератора данных в библиотеке Keras. Причина, по которой он полезен, заключается в том, что мы не хотим хранить все данные в памяти, а хотим создавать их на лету, когда это необходимо в процессе обучения. Дело в том, что в Keras модель нейронной сети обучается батчами, поэтому генератор должен выдавать строго определенные пакеты данных.
def datagen(data, seq_len, batch_size, targetcol, kind):
"As a generator to produce samples for Keras model"
batch = []
while True:
# Pick one dataframe from the pool
key = random.choice(list(data.keys()))
df = data[key]
input_cols = [c for c in df.columns if c != targetcol]
index = df.index[df.index < TRAIN_TEST_CUTOFF]
split = int(len(index) * TRAIN_VALID_RATIO)
if kind == 'train':
index = index[:split] # range for the training set
elif kind == 'valid':
index = index[split:] # range for the validation set
# Pick one position, then clip a sequence length
while True:
t = random.choice(index) # pick one time step
n = (df.index == t).argmax() # find its position in the dataframe
if n-seq_len+1 < 0:
continue # can't get enough data for one sequence length
frame = df.iloc[n-seq_len+1:n+1]
batch.append([frame[input_cols].values, df.loc[t, targetcol]])
break
# if we get enough for a batch, dispatch
if len(batch) == batch_size:
X, y = zip(*batch)
X, y = np.expand_dims(np.array(X), 3), np.array(y)
yield X, y
batch = [] Данная функция берет случайную строку из датафрейма pandas и вырезает несколько следующих за ней строк в качестве выборки временного интервала. Этот процесс повторяется несколько раз, чтобы собрать достаточно временных интервалов для батча. Когда мы собрали достаточно данных, пакет отправляется на обучение при помощи выражения yield. Как вы уже заметили, в генераторах нет выражения return и в данном примере эта функция будет работать все время. Это полезно и удобно, потому что позволяет нашему процессу обучения проходить любое количество эпох.
Без генератора нам постоянно приходилось бы конвертировать наш датафрейм и хранить в памяти все возможные интервалы времени в процессе всего цикла обучения. Это даст нам весьма много повторяющихся данных (так как временные интервалы пересекаются) и займет очень много памяти.
Именно поэтому в Keras уже есть предопределенная функция генератора. Ниже приведен пример использования функции ImageDataGenerator().
Мы загрузили набор данных cifar10 с изображениями 32×32 в переменную x_train. Данные подключаются к генератору через метод flow(). Функция next() возвращает следующий батч данных. В приведенном ниже примере есть четыре вызова функции next(). В каждом случае возвращается 8 изображений, так как размер батча равен 8.
Ниже приведен код, который отображает все картинки после каждого вызова функции next().
(x_train, y_train), _ = keras.datasets.cifar10.load_data()
datagen = ImageDataGenerator()
data_iterator = datagen.flow(x_train, y_train, batch_size=8)
fig,ax = plt.subplots(nrows=4, ncols=8,figsize=(18,6),subplot_kw=dict(xticks=[], yticks=[]))
for i in range(4):
# The next() function will load 8 images from CIFAR
X, Y = data_iterator.next()
for j, img in enumerate(X):
ax[i, j].imshow(img.astype('int')) Результат:
В этом руководстве вы открыли для себя некоторые специальные возможности языка Python.
А именно, мы изучили:
Перевод статьи Mehreen Saeed «More special features in Python«.
Если вы ищете работу на Python, на Talanto.work сейчас есть варианты для junior, middle, senior…
Рынок Python-вакансий большой, но удобным его назвать сложно. Формально вакансий много, однако в реальном поиске…
Срезы в Python — это больше, чем a[2:7]. В статье — быстрый разбор полезных приёмов:…
В этой статье мы рассмотрим возведение в степень в Python с помощью различных методов, от…
Дубликаты в списке Python — частая проблема. В статье показано, как быстро убрать повторы, в…
Услуга виртуального хостинга на SpaceWeb позволяет арендовать часть сервера (shared-сервера) и разместить на нём свой…