Сделать ваше программное обеспечение более быстрым, читаемым и удобным в обслуживании не должно быть так сложно

Несмотря на свои недостатки, Python остается королем современного мира программирования. Его универсальность, дружелюбие к новичкам и огромное сообщество - вот лишь несколько факторов, которые способствовали его огромному росту за последние несколько лет. И хотя новые языки, такие как Rust, Go и Julia, набирают обороты, правление Python, вероятно, продолжится в течение следующих нескольких лет.

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

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

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



Использование более эффективных функций и структур

Избегайте икоты струн с f-струнами

От обработки ввода до вывода сообщений на экран - строки являются жизненно важной частью любого языка программирования. В Python есть три способа форматирования строк: исходный %s-метод, str.format() и, начиная с Python 3.6, f-строки.

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

>>> name = "Monty"
>>> day = "Tuesday"
>>> "Happy %s, %s. Welcome to Python!" % (day, name)
'Happy Tuesday, Monty. Welcome to Python!'

Это все хорошо, но когда вы имеете дело с большим количеством строк одновременно, становится сложно. Вот почему Python представил str.format() начиная с версии 2.6.

По сути, символы %s заменяются фигурными скобками. Вот так:

>>> "Happy {}, {}. Welcome to Python!".format(day, name)

Настоящее преимущество str.format() заключается в том, что теперь вы можете определять объекты, а затем ссылаться на них в своем операторе форматирования:

>>> greeting = {'name': 'Monty', 'day': 'Tuesday'}
>>> "Happy {day}, {name}. Welcome to Python!".format(day=greeting['day'], name=greeting['name']) 

Это огромное улучшение, но струны f еще проще. Учти это:

>>> name = "Monty"
>>> day = "Tuesday"
>>> f"Happy {day}, {name}. Welcome to Python!"

Это еще проще! Что еще приятнее в f-строках, так это то, что в них можно передавать функции:

>>> f"Happy {day}, {name.upper()}. Welcome to Python!"
'Happy Tuesday, MONTY. Welcome to Python!'

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

Используйте составные части списка

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

>>> square_numbers = [0, 1, 2, 3, 4]
>>> for x in range(5):
...     square_numbers.append(x**2)
...
>>> square_numbers
[0, 1, 2, 3, 4, 0, 1, 4, 9, 16]

Это довольно неуклюже, и нам придется снова удалить первые несколько элементов из списка. Кроме того, у нас все еще есть переменная x, плавающая и занимающая память. С пониманием списка это становится намного более кратким:

>>> square_numbers = [x**2 for x in range(5)]
>>> square_numbers
[0, 1, 4, 9, 16]

Синтаксис довольно прост:

new_list = [expression for item in iterable (if conditional)]

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

Быстрое создание списков длины N

Инициализировать списки в Python просто. Но вы можете улучшить свою игру, используя оператор *, например вот так:

>>> four_nones = [None] * 4
>>> four_nones
[None, None, None, None]

Если вы хотите создать список списков, вы можете использовать объединение списков:

>>> four_lists = [[] for __ in range(4)]
>>> four_lists
[[], [], [], []]

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

Будьте осторожны с удалением элементов списков

Рассмотрим список a, из которого мы хотим удалить все элементы, равные bar. Для этого мы можем использовать цикл for:

>>> a = ['foo', 'foo', 'bar', 'bar']
>>> for i in a:
...     if i == 'bar':
...             a.remove(i)
...
>>> a
['foo', 'foo', 'bar']

Но остался один bar! Что случилось?

Ну, когда я был в позициях 0 и 1, он правильно понял, что эти элементы не нужно удалять. На i = 2 он удалил bar. Теперь второй bar переместился на позицию 2. Но теперь цикл уже закончился, потому что больше нет элемента в i = 3, поэтому второй bar остается.

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

>>> foos = [value for value in a if value != 'bar']
>>> foos
['foo', 'foo']

Элегантный доступ к элементам словаря

Использование dict.get() для доступа к элементам словаря неудобно и чревато ошибками. Рассмотрим этот пример:

>>> d = {'hello': 'world'}
>>> if d.has_key('hello'):
...    print d['hello']    # prints 'world'
>>> else:
...    print 'default_value'

Это будет намного проще, если вы воспользуетесь dict.get():

>>> d.get('hello', 'default_value') # prints 'world'
>>> d.get('thingy', 'default_value') # prints 'default_value'

Или, если хотите еще короче, можете написать:

>>> if 'hello' in d:
...    d['hello']

Распаковать аргументы с помощью *, ** и _

Что вы делаете, когда у вас есть список, но вам не нужны все аргументы? Для этого можно использовать _:

>>> numbers = [1, 2, 3]
>>> a, b, _ = numbers
>>> a
1

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

Если у вас длинный список, но вас интересуют только первые или последние несколько переменных, вы можете сделать это:

>>> long_list = [x for x in range(100)]
>>> a, b, *c, d, e, f = long_list
>>> e
98

Переменная, начинающаяся с *, может содержать любое количество элементов. В этом примере мы создали список от 0 до 99 и распаковали первые два и последние три элемента. В частности, предпоследний элемент e - 98, чего мы и ожидали.

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

>>> def printfunction(*args):
...    print(args)
>>> printfunction(1,2,3,4,5,6)
(1, 2, 3, 4, 5, 6)

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

>>> def myfriendsfunction(name, age, profession):
...     print("Name: ", name)
...     print("Age: ", age)
...     print("Profession: ", profession)
>>> friendanne = {"name": "Anne", "age": 26, "profession": "Senior Developer"}
>>> myfriendsfunction(**friendanne)
Name:  Anne
Age:  26
Profession:  Senior Developer

Это быстрый и простой способ получить доступ к переменным без излишней суеты. Имейте в виду!

Автоматизировать открытие и закрытие файлов

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

>>> newfile = open("file.txt")
>>> data = newfile.read()
>>>     print(data)
>>> newfile.close()

Вы можете сохранить строку и возможность появления ошибок, используя with open:

>>> with open('file.txt') as f:
>>>     for line in f:
>>>         print line

Таким образом, даже если в блоке with возникнет исключение, файл снова закроется.

Получение быстрой информации

Используйте help () перед переходом в StackOverflow

Если вы хотите увидеть, что делает функция, просто используйте help(). Это полезно, если вы в принципе знаете, что делаете, но не слишком уверены в деталях функции.

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

>>> def mynewfunction(arg1, arg2):
...     """
...     This function does something really cool.
...     Hope that helps!
...     """
>>> help(mynewfunction)
This function does something really cool.
Hope that helps!

Это справочное сообщение, строка документации, также полезно в качестве документации вашего кода. В этом примере вам, очевидно, нужно добавить тело функции и немного конкретизировать сообщение. Но вы уловили сообщение.

Используйте dir (), чтобы узнать об атрибутах и ​​методах

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

Функция dir() доступна начиная с Python 2.7. Так что для большинства пользователей Python это может быть хорошим вариантом.

Оптимизируйте свою память с помощью getsizeof ()

Если ваша программа работает медленно и вы думаете, что это связано с проблемами памяти, вы можете проверить свои объекты с помощью getsizeof(), чтобы понять, где виноват. Это довольно просто:

>>> import sys
>>> x = [1, 2, 3, 4, 5]
>>> sys.getsizeof(x)
112

Поскольку x - это вектор, состоящий из пяти 24-битных целых чисел на моей машине, вполне логично, что он занимает 112 бит памяти. Обратите внимание, что это число немного варьируется в зависимости от вашего компьютера, архитектуры и версии Python.

Делаем документацию проще

Выберите свой стиль строки документации и придерживайтесь его

Как упоминалось выше, добавление строк документации ко всем определениям вашего модуля, функции, класса или метода - очень хорошая идея.

Чтобы сделать код максимально удобным для сопровождения и чтения, вам нужно выбрать один стиль строки документации и придерживаться его. Если вы сотрудничаете над проектом, убедитесь, что вы выбрали один стиль с самого начала. Это значительно упрощает чтение и добавление в документацию позже.

Версия вашего кода

Большинство программных проектов имеют трехзначный номер версии, например «Python 3.8.4. Система проста: если вы вносите незначительные изменения или исправления ошибок, вы меняете последнее число, для больших изменений вы меняете среднее число, а для довольно фундаментальных изменений вы меняете первое число.

Даже если ваш проект небольшой, сохранение всех старых копий и их нумерация дает несколько преимуществ. Если вы пытаетесь исправить ошибку, но в итоге что-то ломаете, вы всегда можете вернуться к последней версии. А если ваш проект несовместим с предыдущими версиями, жизненно важно хранить старые копии. Кроме того, эта схема нумерации создает аккуратный журнал изменений, к которому вы можете обратиться позже.

Делайте это регулярно

Если вы сотрудничаете или работаете над проектом с открытым исходным кодом, это необходимо: разместите свой код на Github, Gitlab или где-нибудь еще, где вы хотите разместить свой код. Таким образом, он доступен для всех в любое время.

Кроме того, убедитесь, что вы часто используете его. Это не только держит всех в курсе; это также признак того, что ваш код поддерживается и по-прежнему актуален.

Вести журнал изменений

Даже если вы используете строки документации и последовательно нумеруете свои версии, это не отразит все вносимые вами изменения. Чтобы сохранить детали в безопасности, рекомендуется создать отдельный документ, в котором подробно описаны все изменения.

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

Используйте длинные и описательные имена переменных

Вы заметили что-нибудь в приведенных выше примерах? Имена функций и переменных почти всегда довольно длинные. Это не случайно.

Использование такого количества символов для небольшого фрагмента кода может показаться излишним, но для чтения кода это намного лучше. Кроме того, код не занимает много времени - в большинстве редакторов и так есть автозаполнение.

Используйте PEP 8

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

Так что сделайте себе одолжение и отформатируйте свой код с помощью PEP 8, официального руководства по стилю Python. Не волнуйтесь, вам не нужно изучать каждую деталь и реорганизовывать каждый фрагмент кода. Вы можете автоматически проверить, соответствует ли ваш код pycodestyle. В своей оболочке установите пакет, а затем запустите его поверх своего файла Python:

$ pip install pycodestyle
$ pycodestyle my_file.py

Это сигнализирует о том, что вам нужно внести изменения, чтобы ваш файл соответствовал PEP 8. Если вы хотите, чтобы ваш файл соответствовал автоматически, вы можете использовать autopep8:

$ pip install autopep8
$ autopep8 --in-place my_file.py

Хорошо комментируйте

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

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

Принятие хороших привычек

Сделайте продолжение линий более стабильным

В принципе, вы можете разделить многострочное выражение с \ в конце каждой строки, вот так:

>>> big_string = """This is the beginning of a really long story. \
... It's full of magicians, dragons and fabulous creatures. \
... Needless to say, it's quite scary, too."""

Однако это довольно чревато ошибками, поскольку перестает работать, если вы помещаете символ после \, даже если это просто пробел. Лучше разбить его так:

>>> big_string = (
... "This is the beginning of a really long story. "
... "It's full of magicians, dragons and fabulous creatures. "
... "Needless to say, it's quite scary, too."
... )

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

Не пишите спагетти-код

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

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

Используйте только один оператор в строке

Это идет в том же направлении. Не делайте этого:

print("one"); print("two")

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

Использовать подсказку по типу

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

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

Подсказка типа работает как в следующем примере:

>>> def sayhello(day:str, name:str) -> str:
...     return f"Happy {day}, {name}. Welcome to Python!"

:str указывает, что аргументы функции должны быть строками, а часть -> str указывает, что функция должна возвращать строку.

Итог: Python великолепен. Лучший Python потрясающий

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

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

Хотя этот список содержит для вас множество советов и приемов, он, конечно, не является исчерпывающим. Если я что-то пропустил, дайте мне знать в комментариях. А пока удачного кодирования!

Изменить: как указал Wink Saville, результат getsizeof() зависит от машины, архитектуры и версии Python. Это уже включено. Кроме того, Оливер Фаррен правильно указал, что знак подчеркивания _ действительно является переменной, занимающей память. Теперь текст включает это с использованием символа подчеркивания, что делает его более удобочитаемым. И как Майкл Фредериксон указал в Твиттере, функция dir() доступна начиная с Python 2.7. В статье ошибочно утверждалось, что он доступен только в Python 3.