5 фич, о которых я бы хотел знать раньше

Фичи поинтереснее, чем lambda, map и filter.

Python, несомненно, самый быстрорастущий язык программирования этого десятилетия — он очень мощный. Я писал на нем множество проектов — от интерактивных карт до блокчейна. В Python настолько много фич, что новичку порой тяжело освоить все сразу.

Python — язык с высоким уровнем абстракции. Это совершенно новый опыт — даже для тех программистов, которые переходят с Си или MATLAB. Я очень жалею, что не узнал о некоторых фичах Python раньше. Именно поэтому я выделил пять самых интересных из них.

1. Генератор списков — залог компактного кода

Много кто выделил бы лямбда-функции, map, filter и назвал их полезными «хитростями», о которых должен знать каждый новичок. Хоть я и уверен, что знать эти функции необходимо, но я бы не сказал, что они полезны — им не хватает гибкости. 

Lambda — способ написать однострочную функцию для одноразового использования. Чрезмерное использование функций отрицательно сказывается на производительности. Но существует map: она применяет функцию ко всем элементам итерируемого объекта. Filter же применяет функцию только к тем элементам , которые соответствуют условиям, определенным пользователем.

add_func = lambda z: z ** 2
is_odd = lambda z: z%2 == 1
multiply = lambda x,y: x*y

aList = list(range(10))
print(aList)
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Генератор списков — лаконичный и гибкий способ создания списков из списков с помощью выражений и условий. Синтаксис простой — квадратные скобки. Внутри — выражение или функция. Применяются они к каждому элементу списка, который удовлетворяет заранее заданным условиям. Для обработки вложенных списков существуют вложенные генераторы — они гораздо удобнее, чем map и filter.

# Синтаксис генератора списков
[ expression(x) for x in aList if optional_condition(x) ]
print(list(map(add_func, aList)))
print([x ** 2 for x in aList])
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

print(list(filter(is_odd, aList)))
print([x for x in aList if x%2 == 1])
# [1, 3, 5, 7, 9]
# [1, 3, 5, 7, 9]

2. Грамотная манипуляция со списками — круговые списки

Python позволяет использовать отрицательную индексацию — aList[-1] == aList[len(aList)-1]. То есть, мы можем получить второй с конца элемент с помощью конструкции aList[-2].

Также мы можем использовать оператор среза с помощью синтаксиса aList[начало:конец:шаг]. То есть, вызов aList[2:5] возвращает [2, 3, 4]. Мы можем перевернуть список, вызвав aList[::-1]. Мне кажется, это очень элегантно. 

Список может быть разделен на отдельные элементы или набор элементов и подсписки с помощью оператора *

aList = range(10)
a, b, c, d = aList[0:4]
print(f'a = {a}, b = {b}, c = {c}, d = {d}')
# a = 0, b = 1, c = 2, d = 3

a, *b, c, d = aList
print(f'a = {a}, b = {b}, c = {c}, d = {d}')
# a = 0, b = [1, 2, 3, 4, 5, 6, 7], c = 8, d = 9

3. Zipping и enumerate в циклах for

Функция zip создает итератор, который объединяет элементы из нескольких списков. Он позволяет параллельно просмотреть несколько итерируемых объектов в цикле for и и выполнить их сортировку. Распаковка осуществляется с помощью оператора *.

numList = [0, 1, 2]
engList = ['zero', 'one', 'two']
espList = ['cero', 'uno', 'dos']
print(list(zip(numList, engList, espList)))
# [(0, 'zero', 'cero'), (1, 'one', 'uno'), (2, 'two', 'dos')]

for num, eng, esp in zip(numList, engList, espList):
    print(f'{num} на английском {eng} и {esp} — на испанском.')
# 0 на английском zero и cero — на испанском.
# 1 на английском one и uno — на испанском.
# 2 на английском two и dos — на испанском.
Eng = list(zip(engList, espList, numList))
Eng.sort() # sort by engList
a, b, c = zip(*Eng)

print(a)
print(b)
print(c)
# ('one', 'two', 'zero')
# ('uno', 'dos', 'cero')
# (1, 2, 0)

Поначалу Enumerate кажется довольно сложной, но во многих сценариях она может оказаться очень полезной. У этой функции есть автоматический счетчик — порой используется в циклах for. То есть, благодаря этой функции пропадает нужда в объявлении и инициализации переменных-счетчиков вроде counter = 0 и counter += 1. Enumerate и zip — два мощнейших инструмента, помогающих при создании циклов for. 

upperCase = ['А', 'Б', 'В', 'Г', 'Д', 'Е']
lowerCase = ['а', 'б', 'в', 'г', 'д', 'е']
for i, (upper, lower) in enumerate(zip(upperCase, lowerCase), 1):
    print(f'{i}: {upper} и {lower}.')
# 1: А и а.
# 2: Б и б.
# 3: В и в.
# 4: Г и г.
# 5: Д и д.
# 6: Е и е.

4. Эффективное управление памятью — генераторы

Генераторы используются в случаях, когда необходимо вычислить большой набор данных, но нет возможности выделить память для всех результатов одновременно. Другими словами, они генерируют значения динамически и не хранят в памяти предыдущие значения. Следовательно, обход этой последовательности можно провести лишь раз. 

Полезны генераторы при работе с большими данными или генерации бесконечных последовательностей с помощью ключевого слова yield. Я очень часто пользуюсь генераторами при работе с моими проектами по data science. 

5. Изоляция — виртуальные окружения

Если бы вы могли вспомнить из этой статьи только один абзац, то им должен стать этот.

Приложение Python — сложная зависимость разных библиотек. И ведь все они от разных разработчиков. Приложения создаются с помощью определенных настроек определенных библиотек. То есть, результаты одной версии библиотеки не могут воспроизвести результаты другой. Не существует унифицированной версии библиотеки, которая подойдет под любой проект.

conda create -n venv pip python=3.7  # select python version
source activate venv
...
source deactivate

Следовательно, для каждого приложения чрезвычайно важно создавать отдельные автономные виртуальные окружения venv. Делается это с помощью pip или conda.