ТОП-15 трюков в Python 3, делающих код понятнее и быстрее

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

ТОП-15 трюков в Python 3, делающих код понятнее и быстрее

Python – язык программирования с ясным синтаксисом, и многие удобные вещи в силу простоты часто не задерживаются в памяти. При этом самые краткие и красивые решения обычно оказываются наиболее быстрыми. В представленной ниже подборке из 15 трюков в 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 выводит список кортежей вида (элемент, число повторений). Аргумент соответствует желаемому числу кортежей. По умолчанию выводится список кортежей для всех элементов переданного списка.

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

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

import antigravity
import __hello__

Кроме трюков в Python вас также могут заинтересовать:

МЕРОПРИЯТИЯ

Комментарии

ВАКАНСИИ

Добавить вакансию
Golang разработчик (middle)
от 230000 RUB до 300000 RUB
Продуктовый аналитик
Екатеринбург, по итогам собеседования
DevOps
Санкт-Петербург, от 150000 RUB до 400000 RUB

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ