Лучше Python

Как язык программирования общего назначения Python проник почти во все промышленные и академические области. Основываясь на своих наблюдениях за программированием на Python в области биомедицинских наук, я понимаю, что значительное количество программистов Python, включая меня, имеют разный опыт программирования, такие как Matlab, C, C ++, Java, JavaScript и Swift, а не упомяните некоторых, не имеющих опыта программирования.

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

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

Однако так же, как я всегда работаю над улучшением своего английского акцента как иностранец в Соединенных Штатах, я хочу, чтобы мой код Python был как можно более идиоматическим. В этой статье я поделюсь некоторыми идиоматическими примерами, которые я накопил за последние несколько лет и которые, я надеюсь, помогут выровнять ваш код Python.

1. Нарезать последовательность

Некоторые общие типы последовательностей - это списки, кортежи и строки. Мы можем создать последовательность, нарезав другую последовательность. Следующие функции используют список в качестве примера, но они также могут применяться к кортежам, строкам и другим типам последовательностей (например, байтам).

>>> a = [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
>>> # Using a range, [start, end)
>>> a[1:3]
[2, 4]
>>> # Using a range with a step
>>> a[1:9:2]
[2, 6, 10, 14]
>>> # Leave out the start = an implicit start of 0
>>> a[:5]
[0, 2, 4, 6, 8]
>>> # Leave out the stop = an implicit end to the very last item
>>> a[9:]
[18, 20]
>>> # Entire list
>>> a[:]
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

2. Инвертировать последовательность

Иногда мы хотим перевернуть последовательность. Хотя для этого можно использовать цикл for, есть простой способ. Точно так же, как указано выше, когда функциональность доступна для последовательности, это обычно означает, что все строки, кортежи и списки поддерживают эту функциональность.

>>> a = (1, 2, 3, 4, 5)
>>> a[::-1]
(5, 4, 3, 2, 1)
>>> b = 'start'
>>> b[::-1]
'trats'

3. Доступ к элементу в последовательности с помощью обратного индекса.

Если мы хотим получить доступ к некоторым элементам ближе к концу последовательности, будет легче вести обратный отсчет. В последовательности Python последний элемент имеет индекс -1, элемент перед ним имеет индекс -2 и так далее.

>>> a = 'Hello World!'
>>> # instead of using a[len(a)-1]
>>> a[-1]
'!'
>>> # in combination with slicing
>>> a[-5:-1]
'orld'

4. Множественные назначения

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

>>> # instead of doing a = 8; b = 5
>>> a, b = 8, 5
>>> print(f'a is {a}; b is {b}')
a is 8; b is 5
>>> # Swap two variables
>>> a, b = b, a
>>> print(f'a is {a}; b is {b}')
a is 5; b is 8
>>> # Swap the first and last elements in a list
>>> numbers = [1, 2, 3, 4, 5]
>>> numbers[0], numbers[-1] = numbers[-1], numbers[0]
>>> numbers
[5, 2, 3, 4, 1]

5. Убедитесь, что последовательность пуста.

Некоторые операции имеют смысл только тогда, когда последовательность (например, список, кортеж) не пуста, и поэтому нам нужно проверить это перед применением правильных операций. Для этого мы можем просто использовать ключевое слово not, чтобы инвертировать последовательность (например, not []), которая будет оцениваться как True, если последовательность пуста. Кроме того, мы можем сделать то же самое с двумя другими распространенными типами данных: dict и set.

>>> empty_list = [(), '', [], {}, set()]
>>> for item in empty_list:
...     if not item:
...         print(f'Do something with the {type(item)}')
... 
Do something with the <class 'tuple'>
Do something with the <class 'str'>
Do something with the <class 'list'>
Do something with the <class 'dict'>
Do something with the <class 'set'>

6. Составьте список понятий

Удобной функцией в Python является понимание списка, с помощью которого мы можем очень удобно составлять список. Общий формат списка - [some_expression for element in iterable if some_condition].

>>> a = [1, 2, 3, 4, 5]
>>> [x*2 for x in a]
[2, 4, 6, 8, 10]
>>> [x*3 for x in a if x%2 == 1]
[3, 9, 15]

7. Установки понимания

Использование понимания набора аналогично пониманию списка, как указано выше. Разница в том, что мы будем использовать фигурные скобки вместо квадратных. Кроме того, повторяющиеся элементы будут удалены определением типа данных set.

>>> a = [1, -2, 2, -3, 3, 4, 4, 5, 5, 5]
>>> {x*x for x in a}
{1, 4, 9, 16, 25}

8. Понимание диктовки

Помимо списков и наборов, функция понимания также доступна для создания типа данных словаря. dict состоит из пар ключ-значение, поэтому понимание dict включает в себя спецификацию ключей и значений, разделенных двоеточием.

>>> a = [1, 2, 3, 4, 5]
>>> {x: x*x for x in a}
{1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

9. Выражение генератора

Генераторы в Python - удобный способ создания итераторов. Поскольку генераторы «ленивы» (т. Е. Выдают нужный элемент по запросу), они очень эффективны с точки зрения памяти. Один из конкретных способов создания генераторов - это выражение генератора, которое синтаксически похоже на понимание списка, за исключением использования круглых скобок вместо квадратных.

В приведенных ниже примерах круглые скобки необязательны, если генераторы напрямую используются в функциях, которые могут принимать итерации.

>>> sum(x**2 for x in range(100))
328350
>>> max((x*x for x in range(100)))
9801

10. Распаковать кортеж

Кортежи - это очень распространенная структура данных в Python. Это просто группы связанных значений, и обычное использование кортежей предполагает доступ к их элементам. Мы можем получить доступ к этим элементам с помощью индексов, но распаковка - более удобный способ. В связи с его использованием мы можем использовать подчеркивание для обозначения элементов, которые нам не нужны, и использовать звездочку для назначения остальных элементов, отличных от названных.

>>> items = (0, 'b', 'one', 10, 11, 'zero')
>>> a, b, c, d, e, f = items
>>> print(f)
zero
>>> a, *b, c = items
>>> print(b)
['b', 'one', 10, 11]
>>> *_, a, b = items
>>> print(a)
11

11. Используйте Enumerate () In для циклов

Функция enumerate() использует итерацию для создания итератора. Кроме того, он может отслеживать количество итераций. При желании мы можем установить начало подсчета. Подсчет по умолчанию начинается с 0.

>>> students = ('John', 'Mary', 'Mike')
>>> for i, student in enumerate(students):
...     print(f'Iteration: {i}, Student: {student}')
... 
Iteration: 0, Student: John
Iteration: 1, Student: Mary
Iteration: 2, Student: Mike
>>> for i, student in enumerate(students, 35001):
...     print(f'Student Name: {student}, Student ID #: {i}')
... 
Student Name: John, Student ID #: 35001
Student Name: Mary, Student ID #: 35002
Student Name: Mike, Student ID #: 35003

12. Используйте обратный вход () для циклов.

Функция reversed() часто используется в циклах for как способ создания итератора в обратном порядке исходной итерации.

>>> tasks = ['laundry', 'picking up kids', 'gardening', 'cooking']
>>> for task in reversed(tasks):
...     print(task)
... 
cooking
gardening
picking up kids
laundry

13. Функция Zip ()

Функция zip() полезна для объединения нескольких итераций на основе соответствия один к одному. Если определенные итерации превышают самый короткий, они усекаются. Эта функция возвращает итератор, поэтому она часто используется в итерациях. Мы также можем использовать функцию zip(), чтобы распаковать итератор, используя знак звездочки, и назначить разархивированные элементы переменным.

>>> students = ('John', 'Mary', 'Mike')
>>> ages = (15, 17, 16)
>>> scores = (90, 88, 82, 17, 14)
>>> for student, age, score in zip(students, ages, scores):
...     print(f'{student}, age: {age}, score: {score}')
... 
John, age: 15, score: 90
Mary, age: 17, score: 88
Mike, age: 16, score: 82
>>> zipped = zip(students, ages, scores)
>>> a, b, c = zip(*zipped)
>>> print(b)
(15, 17, 16)

14. Лямбды для сортировки.

Лямбды - это анонимные функции, которые могут принимать несколько аргументов в однострочном выражении. Одно из его распространенных применений - установить в качестве аргумента key в функции sorted(). Помимо этого, лямбды часто используются в некоторых функциях (например, max(), map()), где однострочное выражение применимо для замены обычной функции с использованием ключевого слова def.

>>> students = [{'name': 'John', 'score': 98}, {'name': 'Mike', 'score': 94}, {'name': 'Jennifer', 'score': 99}]
>>> sorted(students, key=lambda x: x['score'])
[{'name': 'Mike', 'score': 94}, {'name': 'John', 'score': 98}, {'name': 'Jennifer', 'score': 99}]

15. Сокращенное условное присвоение

Эта функция в основном является синтаксическим сахаром. Когда вам нужно присвоить значение переменной на основе определенного условия, мы можем использовать сокращенное присвоение, используя эту общую форму: y = x if condition_met else another_x.

>>> some_condition = True
>>> # the expanded format
>>> if some_condition:
...     x = 5
... else:
...     x = 3
>>> print(f'x is {x}')
x is 5
>>> # the shorthand way
>>> x = 5 if some_condition else 3
>>> print(f'x is {x}')
x is 5

16. Тестирование членства в коллекции

Иногда нам нужно проверить, существует ли определенный элемент в коллекции, прежде чем мы захотим применить операции к коллекции или сопоставленному элементу. Идиоматический способ - использовать ключевое слово in.

>>> a = ('one', 'two', 'three', 'four', 'five')
>>> if 'one' in a:
...     print('The tuple contains one.')
... 
The tuple contains one.
>>> b = {0: 'zero', 1: 'one', 2: 'two', 3: 'three'}
>>> if 2 in b.keys():
...     print('The dict has the key of 2.')
... 
The dict has the key of 2.

17. Используйте Get () для получения значения в словаре.

Обычно мы можем указать ключ в квадратных скобках, чтобы получить значение ключа. Однако это вызовет ошибку, если ключа нет в словаре. Конечно, мы можем использовать try / except для решения этой проблемы. Вместо этого мы можем использовать метод get(), который позволяет нам использовать значение по умолчанию, когда ключа нет в словаре.

>>> number_dict = {0: 'zero', 1: 'one', 2: 'two', 3: 'three'}
>>> number_dict[5]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 5
>>> number_dict.get(5, 'five')
'five'

18. Найдите ключ, значение которого в словаре является максимальным.

Для словаря иногда требуется найти ключ, значение которого является максимальным. Сначала мы можем узнать индекс максимального значения в списке всех значений, а затем найти соответствующий ключ из другого списка, в котором хранятся все ключи. В качестве альтернативы более простой способ - указать аргумент key в функции max().

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

>>> model_scores = {'model_a': 100, 'model_z': 198, 'model_t': 150}
>>> # workaround
>>> keys, values = list(model_scores.keys()), list(model_scores.values())
>>> keys[values.index(max(values))]
'model_z'
>>> # one-line
>>> max(model_scores, key=model_scores.get)
'model_z'

19. Отладка с помощью функции Print ()

Для небольших проектов мы всегда можем использовать функцию print(), которая поможет нам отладить. Мы также часто используем эту функцию в учебных целях. Есть несколько приемов, которые мы часто используем с функцией print(). Первый - завершить строку, отличную от новой строки по умолчанию, а второй - использовать f-строку, которая позволяет нам создать строку, содержащую некоторые выражения.

>>> for i in range(5):
...     print(i, end=', ' if i < 4 else '\n')
... 
0, 1, 2, 3, 4
>>> for i in range(5):
...     print(f'{i} & {i*i}', end=', ' if i < 4 else '\n')
... 
0 & 0, 1 & 1, 2 & 4, 3 & 9, 4 & 16

20. Оператор моржа

Оператор моржа (:=) - это новая функция, доступная в Python 3.8+. Это просто еще одно название выражения присваивания - присваивание переменной внутри выражения. Обычно, когда в выражении используется переменная, переменная должна быть объявлена ​​раньше. С помощью оператора моржа присвоение переменной может быть включено в выражение, и переменная сразу становится доступной для использования.

>>> a = ['j', 'a', 'k', 'd', 'c']
>>> if (n := len(a))%2 == 1:
...     print(f'The number of letters is {n}, which is odd.')
...
The number of letters is 5, which is odd.

21. Разделить строку

Когда мы работаем со строками, это обычная задача - разделить строки на список слов. В этом случае мы можем использовать функцию split(), которая принимает разделитель и, возможно, максимальное разбиение. Связанная функция - это функция rsplit(), которая имеет аналогичную функциональность, за исключением того, что она запускает разделение справа, чтобы удовлетворить требованию максимального разделения, когда оно установлено.

>>> sentence = 'this is, a python, tutorial, about, idioms.'
>>> sentence.split(', ')
['this is', 'a python', 'tutorial', 'about', 'idioms.']
>>> sentence.split(', ', 2)
['this is', 'a python', 'tutorial, about, idioms.']
>>> sentence.rsplit(', ')
['this is', 'a python', 'tutorial', 'about', 'idioms.']
>>> sentence.rsplit(', ', 2)
['this is, a python, tutorial', 'about', 'idioms.']

22. Соединение строк в итерабельном объекте

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

>>> words = ('Hello', 'Python', 'Programmers')
>>> '!'.join(words)
'Hello!Python!Programmers'
>>> words_dict = {0: 'zero', 1: 'one', 2: 'two', 3: 'three'}
>>> '&'.join(words_dict.values())
'zero&one&two&three'

23. Функция Map ()

Функция map() - это функция высшего порядка (т. Е. Функция, которая использует функцию в качестве аргумента или возвращает функцию в качестве вывода). Он имеет общий формат map(function, iterables), который применяет функцию к итерациям и возвращает объект map, который является итератором. Количество итераций должно соответствовать количеству необходимых аргументов функции.

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

>>> numbers = (1, 2, 4, 6)
>>> indices = (2, 1, 0.5, 2)
>>> # use map()
>>> list(map(pow, numbers, indices))
[1, 2, 2.0, 36]
>>> # list comprehensions
>>> [pow(x, y) for x, y in zip(numbers, indices)]
[1, 2, 2.0, 36]

24. Функция Filter ()

Функция filter() предназначена для фильтрации последовательности с использованием указанной функции или лямбда-функции. Эта функция возвращает объект фильтра, который является итератором. В целом, ее использование очень похоже на функцию map().

>>> def good_word(x: str):
...     has_vowels = not set('aeiou').isdisjoint(x.lower())
...     long_enough = len(x) > 7
...     good_start = x.lower().startswith('pre')
...     return has_vowels & long_enough & good_start
... 
>>> words = ['Good', 'Presentation', 'preschool', 'prefix']
>>> list(filter(good_word, words))
['Presentation', 'preschool']

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

Когда мы используем список для записи чего-то, что может иметь повторяющиеся элементы, например, отслеживая победителей в серии игр, уместной задачей является выяснить, кто выигрывал чаще всего. Это можно сделать с помощью функции max(), указав аргумент key, который определит максимальное значение по количеству элементов в наборе.

>>> winnings = ['John', 'Billy', 'Billy', 'Sam', 'Billy', 'John']
>>> max(set(winnings), key = winnings.count)
'Billy'

26. Отслеживайте частоты элементов в списке.

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

>>> winnings = ['John', 'Billy', 'Billy', 'Sam', 'Billy', 'John']
>>> tracked = {item: winnings.count(item) for item in set(winnings)}
>>> sorted(tracked.items(), key=lambda x: x[1], reverse=True)
[('Billy', 3), ('John', 2), ('Sam', 1)]

27. Проверьте тип объекта.

Проверка типа объекта является частью темы самоанализа в Python. Иногда нам нужно знать, относится ли объект к определенному типу, прежде чем применять соответствующую функцию. Для этого мы можем использовать функцию type() или isinstance(), причем последняя является более гибким методом, позволяющим проверять "один ко многим".

>>> def check_type(number):
...     if type(number) == int:
...         print('do something with an int')
...     if isinstance(number, (int, float)):
...         print('do something with an int or float')
... 
>>> check_type(5)
do something with an int
do something with an int or float
>>> check_type(4.2)
do something with an int or float

28. Функция Any ()

Предположим, у нас есть список записей, отслеживающих время прибытия Джона на работу. Один из вариантов использования состоит в том, что мы хотим знать, не опаздывает ли он на этой неделе, и в этом случае функция any() очень удобна. Эта функция возвращает True, если какой-либо из элементов в логическом списке равен True.

>>> arrival_hours = {'Mon': 8.5, 'Tue': 8.75, 'Wed': 9, 'Thu': 8.5, 'Fri': 8.5}
>>> arrival_checks = [x>8.75 for x in arrival_hours.values()]
>>> any(arrival_checks)
True

29. Функция All ()

Следуя тому же примеру, приведенному выше, мы также хотим знать, приходил ли он на работу всегда до 9:30 в течение всей недели. Чтобы проверить, так ли это, мы можем использовать функцию all(), которая возвращает True, только если все элементы в логическом списке равны True.

>>> arrival_checks_all = [x<9.5 for x in arrival_hours.values()]
>>> all(arrival_checks_all)
True

30. Используйте с ключевым словом в файле

Когда мы имеем дело с файлом, нам нужно открыть его, обработать содержимое, а затем закрыть. Если вы не закроете файл после использования, он может быть недоступен в течение некоторого времени. В этом случае очень полезно ключевое слово with. Как показано ниже, файл будет автоматически закрыт после использования.

>>> with open('a_file.txt') as file:
...     pass
...
>>> file.closed
True

Заключительные мысли

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

Должно быть, я пропустил некоторые идиомы в кодировании Python в этой статье. Поэтому вы можете оставлять свои отзывы, если вы думаете о чем-нибудь, чем можно поделиться с другими программистами Python.