Трюки (и не только) в Python 3

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

Python – язык программирования с ясным синтаксисом, и многие удобные вещи в силу простоты часто не задерживаются в памяти. При этом самые краткие и красивые решения обычно оказываются наиболее быстрыми. В представленной ниже подборке из 15 трюков в Python вы наверняка встретите приемы, знаний о которых не хватало в определенный момент в вашей практике.

Справочник Python

1. Объединение списков без цикла

Как бы вы решили задачу объединения списков разной длины без обхода элементов цикла? Вот как это можно сделать с помощью стандартной функции sum:

L = [[1, 2, 3], [4, 5], [6], [7, 8, 9]]
print(sum(L, []))
[1, 2, 3, 4, 5, 6, 7, 8, 9]

Пусть и менее краткий, но более эффективный способ – применение модуля itertools:

import itertools

L = [[1, 2, 3], [4, 5], [6], [7, 8, 9]]

print(list(itertools.chain.from_iterable(L)))

Заметим, что при работе с последовательностями многие полезные решения находятся в модулях стандартной библиотеки collections (контейнерные структуры данных) и itertools (операции над последовательностями). Внимательное прочтение документации модулей освободит вас от многих часов придумывания собственных «велосипедов».

2. Обмен значениями при помощи кортежей

Один из популярных трюков в Python – обмен значениями без создания временной переменной. Способ применим для любого числа переменных.

a, b = 1, 2
print(a, b)
a, b = b, a
print(a, b)
1 2
2 1

В правой части инструкции присваивания последовательностей допускается указывать любые итерируемые объекты.

Главное, чтобы число элементов слева равнялось числу элементов справа. Такое присваивание применяется и для сложных вложенных конструкций:

for ((a, b), c) in [((1, 2), 3), ((4, 5), 6)]:
    print(a, b, c)
1 2 3
4 5 6

3. Распаковывание последовательностей при неизвестном числе элементов

Для указанного в подзаголовке случая в Python 3 есть оператор звездочки – расширенная операция распаковывания последовательности.

Переменной со звездочкой присваивается часть списка, содержащая все неприсвоенные элементы, соответствующие этой позиции:

seq = [1, 2, 3, 4]
*a, b, c = seq
print(a, b, c)
a, *b, c = seq
print(a, b, c)
a, b, *c = seq
print(a, b, c)
a, b, c, *d = seq
print(a, b, c, d)
a, b, c, d, *e = seq
print(a, b, c, d, e)
[1, 2] 3 4
1 [2, 3] 4
1 2 [3, 4]
1 2 3 [4]
1 2 3 4 []

Подобные операции можно осуществить и при помощи срезов, но такой код выглядит естественнее. 

Расширенную операцию распаковывания используют и в циклах, когда длина вложенных последовательностей варьируется:

for (a, *b, c) in [(1, 2, 3), (4, 5, 6, 7)]:
    print(a, b, c)
1 [2] 3
4 [5, 6] 7

4. Объединение строк

В программном коде нередко приходится сталкиваться с конкатенацией строк при помощи знака сложения. Создание строки из списка нескольких подстрок удобнее осуществить при помощи строкового метода join:

a = [“Python”, “-“, “прекрасный”, “язык.”]

print(” “.join(a))

Пример посложнее с методом join – конвертирование списка чисел в строку:

numbers = [1, 2, 3, 4, 5]
print(', '.join(map(str, numbers)))
1, 2, 3, 4, 5

5. Проверка на анаграммность

Проверить, являются ли строки анаграммами (например, в результате случайной перестановки букв) поможет класс Counter модуля collections:

from collections import Counter

str1 = 'proglib'
str2 = 'prgolib'

print(Counter(str1) == Counter(str2))
True

6. Транспонирование двумерного массива данных

Чтобы поменять местами строки и столбцы матрицы, созданной с помощью встроенных типов данных, воспользуйтесь функцией zip:

original = [('a', 'b'), ('c', 'd'), ('e', 'f')]
transposed = zip(*original)
print(list(transposed))
[('a', 'c', 'e'), ('b', 'd', 'f')]

Если вы регулярно сталкиваетесь с подобными задачами, вместо таких трюков в Python принято использовать библиотеку NumPy.

7. Удаление дубликатов в списке

Среди регулярно используемых трюков в Python – преобразование списка во множество и обратно в список для удаления повторяющихся элементов списка:

items = [2, 2, 3, 3, 1]
print(list(set(items)))
[1, 2, 3]

Но множества – это неупорядоченные последовательности. Часто стоит задача сохранить порядок следования элементов. Для этого удобно воспользоваться типом данных OrderedDict из модуля collections:

items = [2, 2, 3, 3, 1]

from collections import OrderedDict
print(list(OrderedDict.fromkeys(items).keys()))
[2, 3, 1]

8. Назначение переменных и функций по условию

Иногда элементы if настолько просты, что кажется излишним тратить на них строки. В этом случае имеет смысл применить тернарный оператор if/else:

A = Y if X else Z

Интерпретатор выполняет выражение Y, если объект X – истина, и Z, если X – ложь. Не злоупотребляйте этим выражением, если X, Y, Z имеют сложную форму записи.

Тернарный оператор можно использовать не только для переменных, но и для функций:

def product(a, b):
    return a * b

def summarize(a, b):
    return a + b

c = True

print((product if c else summarize)(3, 4))
12

9. Присвоение первого непустого значения из ряда

Следующая инструкция

X = A or B or C or None

присвоит переменной X первый непустой (имеющий истинное значение) объект из множества объектов A, B и С или None, если все предыдущие объекты окажутся пустыми. В простейшем виде эту особенность используют для задания значения по умолчанию:

X = A or default

Аналогичным образом логический оператор and можно применять для нахождения первого ложного значения.

10. Вывод значения по умолчанию для отсутствующего ключа словаря

Обращение к несуществующему ключу словаря вызывает исключение. Избежать этого можно, вызывая метод get. В указанном случае метод выдает None (по умолчанию) или заданное значение аргумента.

d = {'a':1, 'b':2}

print(d.get('c'))
print(d.get('c', 3))
None
3

При создании собственного типа данных на основе словарей обратите внимание на метод __missing__ для возвращения аргумента при отсутствии ключа:

class MyDict(dict):
    def __missing__(self, key):
        return key

D = MyDict(a=1, b=2)
print(D)
print(D['a'])
print(D['c'])
{'a': 1, 'b': 2}
1
c

11. Вывод при помощи print

Часто указывается, что основное различие Python 2-й и 3-й версий – это скобки после инструкции print. Это же означает, что инструкция print стала функцией, а значит, скобки могут включать какие-то дополнительные аргументы.

Так и есть. В print имеются следующие аргументы:

  • строка sep (по умолчанию один пробел), вставляемая между объектами при выводе;
  • строка end (по умолчанию \n), добавляемая в конец выводимого текста;
  • file (по умолчанию sys.stdout) – любой объект, поддерживающий метод файлов write(string), то есть стандартный поток, файл и др.

Например, если нам не нужно объединять подстроки, а лишь напечатать суммарную строку:

for part in ["prog", "lib", ".io", "\n"]:
    print(part, end='')
proglib.io

Тот же подход можно практиковать для чтения файлов:

for line in open(‘script.py’):

print(line, end=”)

Присвоение аргументу end пустой строки приводит к тому, что строки файла не перемежаются пустыми строками. Иначе при чтении строк файла и использовании end по умолчанию символ окончания строки \n повторялся бы два раза.

12. Нумерованные списки

Задача нумерации элементов последовательности настолько распространена, что в Python есть соответствующая встроенная функция enumerate:

for i, item in enumerate(['a', 'b', 'c']):
    print(i, item)
0 a
1 b
2 c

Для тех, кто уже знаком с enumerate, может оказаться новостью, что у функции есть второй аргумент, задающий начальное число:

for i, item in enumerate(['a', 'b', 'c'], 1):
    print(i, item)
1 a
2 b
3 c

13. Сортировка словаря по значениям

Распространена практика использования словарей в качестве таблиц для хранения данных. Сортировка данных словаря по значениям ключей, а не самим ключам, нередко ставит в тупик. Задача решается довольно просто при помощи соответствующего аргумента функции сортировки:

d = {'яблоки':40, 'апельсины':80, 'бананы':70}
print(sorted(d, key=d.get))
['яблоки', 'бананы', 'апельсины']

14. Генераторы словарей и множеств

Вы, конечно, пользовались генераторами списков. Но знаете ли вы о генераторах множеств и словарей?

S = {i**2 for i in range(10)}
D = {i: i**2 for i in range(10)}
print(S)
print(D)
{0, 1, 64, 4, 36, 9, 16, 49, 81, 25}
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}

В случае словарей отличие только в парах ключ-значение. Такие генераторы удобны для начальной инициализации значений последовательностей.

15. Нахождение наиболее часто повторяющихся элементов списка

Найти самый часто повторяющийся элемент можно с помощью встроенной функции max. Функция max умеет искать наибольшее значение не только для самого итерируемого объекта, но и основываясь на результах применения к нему функции. Преобразовав список во множество (см. трюк 7) и использовав метод count для нахождения числа вхождений элемента в список, получаем:

a = [1, 2, 3, 1, 2, 3, 2, 2, 4, 5, 1]
print(max(set(a), key=a.count))
2

Если необходимо найти несколько наиболее часто повторяющихся значений, воспользуйтесь счетчиком Counter из библиотеки collections:

from collections import Counter

a = [1, 2, 3, 1, 2, 3, 2, 2, 4, 5, 1]
cnt = Counter(a)
print(cnt.most_common(3))
[(2, 4), (1, 3), (3, 2)]

Метод most_common выводит список кортежей вида (элемент, число повторений). Аргумент соответствует желаемому числу кортежей. По умолчанию выводится список кортежей для всех элементов переданного списка.

16. from __future__ import

Одним из последствий популярности Python является то, что постоянно разрабатываются и выходят новые версии. Новые версии — новые возможности, но только не для вас, если вы пользуетесь устаревшей.

Впрочем, не всё так плохо. Модуль __future__ даёт возможность импортировать функциональность будущих версий Python. Это прямо как путешествие во времени, или магия:

from future import print_function

print(“Hello World!”)

17. pprint

Стандартная функция Python print() делает своё дело. Но если попытаться вывести какой-нибудь большой вложенный объект, результат будет выглядеть не очень приятно.

Здесь на помощь приходит модуль из стандартной библиотеки pprint (pretty print). С его помощью можно выводить объекты со сложной структурой в читабельном виде.

Мастхэв для любого Python-разработчика, работающего с нестандартными структурами данных:

import requests
import pprint

url = ‘https://randomuser.me/api/?results=1’
users = requests.get(url).json()

pprint.pprint(users)

18. zip

Ещё одна клёвая штука. Когда-нибудь возникала необходимость создать словарь из двух списков?

keys = [‘a’, ‘b’, ‘c’]
vals = [1, 2, 3]
zipped = dict(zip(keys, vals))
print(zipped)

Встроенная функция zip() принимает несколько итерируемых объектов и возвращает последовательность  кортежей. Каждый кортеж группирует элементы объектов по их индексу.

Можно провести операцию, обратную zip(), с помощью zip(*).

19. Просмотр встроенных функций

В стандартную библиотеку Python входит множество встроенных функций и классов. Все встроенные объекты можно посмотреть следующим образом:

for e in __builtins__.__dict__:
    print(e)

 20. Избегайте вложенных циклов с помощью product

Когда программа становится сложной, неизбежно приходится писать вложенные циклы. Однако вложенные циклы делают программы более сложными для чтения и сопровождения.

К счастью, в Python всегда можно избежать вложенных циклов с помощью встроенной функции product().

Например, у нас есть следующая программа, которая содержит трехуровневые вложенные циклы for:

list_a = [1, 2020, 70]
list_b = [2, 4, 7, 2000]
list_c = [3, 70, 7]

for a in list_a:
    for b in list_b:
        for c in list_c:
            if a + b + c == 2077:
                print(a, b, c)
# 70 2000 7

Чтобы сделать код более аккуратным и чистым, мы можем использовать функцию product(), которая находится в модуле itertools, для оптимизации кода:

from itertools import product

list_a = [1, 2020, 70]
list_b = [2, 4, 7, 2000]
list_c = [3, 70, 7]

for a, b, c in product(list_a, list_b, list_c):
    if a + b + c == 2077:
        print(a, b, c)
# 70 2000 7

21. List comprehensions: Генераторы списков

Одна из самых удобных возможностей Python – это генераторы списков. Написанный с их помощью код выглядит очень чистым и легко читается, практически как обычный человеческий язык.

numbers = [1,2,3,4,5,6,7]
evens = [x for x in numbers if x % 2 is 0]
odds = [y for y in numbers if y not in evens]


cities = ['Лондон', 'Дублин', 'Осло']

def visit(city):
   print("Добро пожаловать в " + city)

for city in cities:
   visit(city)

Если вы хотите узнать о генераторах больше, загляните сюда.

List Comprehension — очень мощный инструмент, который создает новый список на основе другого списка в одной удобочитаемой строке.

Например, допустим, нам нужно создать список целых чисел, которые определяют длину каждого слова в определенном предложении, но только если это слово не является словом «the».

sentence = "the quick brown fox jumps over the lazy dog"
words = sentence.split()
word_lengths = []
for word in words:
      if word != "the":
          word_lengths.append(len(word))
print(words)
print(word_lengths)

Результат:

[‘the’, ‘quick’, ‘brown’, ‘fox’, ‘jumps’, ‘over’, ‘the’, ‘lazy’, ‘dog’]
[5, 5, 3, 5, 4, 4, 3]

22. Оператор морж (:=)

или способ записывать данные в переменную о котором вы не знали

Начиная с Python 3.8, появился новый синтаксис под названием «оператор морж» или walrus operator, который может присваивать значения переменным как часть более крупного выражения.

Оператор := получил свое милое название из-за глаз и бивней моржа.

Этот синтаксис очень прост для понимания. Например, если мы хотим написать следующие две строки кода Python в одной строке, как это сделать?

author = "Yang"
print(author)
# Yang

К сожалению, мы не можем напрямую поместить присвоение в функцию print().

Если мы попытаемся это сделать, возникнет ошибка типа TypeError.

Но если мы используем оператор :=, то все получится!

print(author:="Yang")
# Yang

23. Избегайте вложенных циклов с помощью product

Когда программа становится сложной, неизбежно приходится писать вложенные циклы. Однако вложенные циклы делают программы более сложными для чтения и сопровождения.

К счастью, в Python всегда можно избежать вложенных циклов с помощью встроенной функции product().

Например, у нас есть следующая программа, которая содержит трехуровневые вложенные циклы for:

list_a = [1, 2020, 70]
list_b = [2, 4, 7, 2000]
list_c = [3, 70, 7]

for a in list_a:
    for b in list_b:
        for c in list_c:
            if a + b + c == 2077:
                print(a, b, c)
# 70 2000 7

Чтобы сделать код более аккуратным и чистым, мы можем использовать функцию product(), которая находится в модуле itertools, для оптимизации кода:

from itertools import product

list_a = [1, 2020, 70]
list_b = [2, 4, 7, 2000]
list_c = [3, 70, 7]

for a, b, c in product(list_a, list_b, list_c):
    if a + b + c == 2077:
        print(a, b, c)
# 70 2000 7

24. Самый легкий способ мерджить словари

Слияние словарей – частое действие в программировании на Python. Существует множество способов сделать это. Но все они были уродливы до версии Python 3.9.

Начиная с Python 3.9, мы наконец-то получили самый элегантный способ объединения словарей – использование операторов объединения.

cities_us = {'New York City': 'US', 'Los Angeles': 'US'}
cities_uk = {'London': 'UK', 'Birmingham': 'UK'}

cities = cities_us|cities_uk
print(cities)
# {'New York City': 'US', 'Los Angeles': 'US', 'London': 'UK', 'Birmingham': 'UK'}

Как показано в примере выше, мы можем просто использовать оператор | для слияния двух разных словарей. Более того, он также поддерживает объединение in-place:

cities_us = {'New York City': 'US', 'Los Angeles': 'US'}
cities_uk = {'London': 'UK', 'Birmingham': 'UK'}

cities_us |= cities_uk
print(cities_us)
# {'New York City': 'US', 'Los Angeles': 'US', 'London': 'UK', 'Birmingham': 'UK'}

25. Используем * для мерджа списка, кортежа и множества в одну строчку

Для того, чтобы это сделать самый элегантный способ – использование *:

A = [1, 2, 3]
B = (4, 5, 6)
C = {7, 8, 9}
L = [*A, *B, *C]
print(L)
# [1, 2, 3, 4, 5, 6, 8, 9, 7]

Звездочки можно использовать в качестве префиксов для распаковки их элементов. Но помимо распаковки, звездочки также можно использовать для деструктуризации присваиваний в Python:

a, *mid, b = [1, 2, 3, 4, 5, 6]
print(a, mid, b)
# 1 [2, 3, 4, 5] 6

Как показано выше, с помощью одной звездочки переменная mid получает элементы в середине в виде списка.

26. Используем встроенные функции в Python для написания стандартной логики

В Python есть несколько встроенных функций, которые помогают при написании некоторых стандартных логических операций.

Например, функция map() – известная и часто используемая функция. Она получает два параметра, один из которых – функция, а другой – итератор.

При выполнении функции map функция применяется к каждому элементу в итераторе.

names = ['yAnG', 'MASk', 'thoMas', 'LISA']
names = map(str.capitalize, names)
print(list(names))
# ['Yang', 'Mask', 'Thomas', 'Lisa']

Как показано в примере выше, с помощью функции map() мы можем избежать написания цикла for для выделения заглавными буквами каждого слова в списке имен.

Другая известная функция – reduce(). Как следует из ее названия, она применяет функцию к итератору и выполняет для нее операцию reduce.

Например, в следующем примере список преобразуется в одну строку:

from functools import reduce

city = ['L', 'o', 'n', 'd', 'o', 'n', 2, 0, 2, 0]
city_to_str = reduce(lambda x, y: str(x) + str(y), city)
print(city_to_str)
# London2020

Как преобразовать строку в словарь в Python

Строковые методы split() и join() в Python

Функция sum() в Python: питонический способ суммирования значений

Как исправить ошибку NameError в Python

Однострочники Python для ускорения написания кода

Строки в Python

5 простых способов удалить символ из строки

Задачи по Python для начинающих от Tproger и GeekBrains

Как возвести число в квадрат в Python

Кортежи в Python

Словари в Python: 12 задач для начинающих с решениями

Именованные кортежи в Python

Лямбда-функции в Python: примеры

Добавление функций в классы Python

Встроенные функции для перебора последовательностей в Python

Functools: улучшаем функции Python

Метод __repr__ в Python

22 полезных примера кода на Python

22 сниппета на Python для повседневных задач

Python Traceback — Как правильно исправлять ошибки в коде

Получение текущей даты и времени в Python

Проверить переменные среды в Python


Веселый бонус

Наверняка вы знаете про Дзен Python, выводимый интерпретатором по команде import this. В третьей версии Python спрятаны и другие «пасхалки»:

import antigravity
import __hello__