Course

Что такое «Дзен Python»?
«Дзен Python» — это набор из девятнадцати афоризмов, которые служат руководящими принципами проектирования Python. Изначально их должно было быть двадцать, но Гвидо ван Россум, создатель Python, так и не добавил финальный афоризм, как задумывал Тим Питерс, автор «Дзена Python». Как сообщают, Гвидо сказал, что недостающий двадцатый афоризм — «какая-то странная внутренняя шутка Тима Питерса».
Несмотря на отсутствие двадцатого афоризма, «Дзен Python» был стандартизирован как PEP 20 в 2004 году благодаря сильному влиянию на процесс разработки у программистов Python. Следовать «Дзену Python» не обязательно, но знать его и держать в уме — рекомендуется. Если вы когда-нибудь забудете эти уроки, легко освежить память, выполнив в интерпретаторе Python команду import this.
>>> import this
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
В этой статье мы разберём каждый из уроков подробнее.
#1 Прекрасное лучше безобразного
Если вы программист, сомнений нет — вы обладаете высокой квалификацией. Умение решать задачи с помощью кода — непростая вещь, но от Python-разработчика ожидают большего. Красивый код лучше некрасивого. Стремительный взлёт популярности Python отчасти объясняется его доступностью: он прост, читаем и элегантен. Как разработчик на Python, вы несёте ответственность за поддержание «питоничного» стандарта, то есть за написание красивого кода.
Что такое красивый код? Вещь субъективная — «у каждого свой вкус» и всё такое. Но есть простое эмпирическое правило: пишите чистый, читаемый код, который другим разработчикам легко понять. Например:
"""
Initial code was created by Vishal Sharma
See: https://towardsdatascience.com/the-zen-of-python-a-guide-to-pythons-design-principles-93f3f76d088a
"""
def collatz(num):
if num % 2 == 0:
return num // 2
else:
return 3 * num + 1
number = input("Enter a number: ")
while number != 1:
number = collatz(int(number))
print(number)
"""
Enter a number: 5
16
8
4
2
1
"""
Этот код работает безупречно, но его нельзя назвать красивым. В функции collatz есть лишнее условие else, которое можно убрать так:
def collatz(num):
if num % 2 == 0:
return num // 2
return 3 * num + 1
Итак, когда у вас есть два рабочих варианта кода, выбирайте тот, что проще, читабельнее и понятнее.
#2 Явное лучше неявного
Называть любой афоризм «самоочевидным» — большая ошибка, но так всё равно иногда делают. Суть принципа «явное лучше неявного» в том, что развёрнутый код предпочтительнее намёков и скрытых допущений. Сделайте всё возможное, чтобы функциональность вашего кода не пряталась за неочевидными конструкциями. Человек, впервые видящий вашу программу, должен понимать, что в ней происходит.
#3 Простое лучше сложного
Вы бы, наверное, улыбнулись, если бы ваш приятель достал мини-мойку, чтобы помыть посуду после ужина. Это не только нелепое решение, но и тарелки можно повредить. Точно так же применение чрезмерно сложного решения к простой задаче программирования — крайне непрактично. Не усложняйте, даже если кажется, что так вы производите впечатление. В итоге выйдет больше вреда, чем пользы, и в мире Python это не приветствуется.
Посмотрите на эту функцию для разворота строки:
def reverse_string(string):
if len(string) == 1:
return string
else:
return reverse_string(string[1:]) + string[0]
string = "kurtis"
print(reverse_string(string))
"""
sitruk
"""
Этот код решает задачу, но излишне усложнён. Если вы хорошо знаете Python, вы знаете, что строку можно развернуть индексированием.
string = "kurtis"
# annonymous function solution
reverse_string = lambda x: x[::-1]
# function solution
def reverse(string):
return string[::-1]
print(reverse_string(string))
print(reverse(string))
"""
sitruk
sitruk
"""
#4 Сложное лучше запутанного
Мыть посуду мини-мойкой непрактично: лучше подойдёт простое решение — проточная вода. Но что, если нужно отмыть 4x4 Range Rover? Два соседних афоризма напоминают, что для одной задачи могут подойти и простые, и сложные техники. Мыть Range Rover — задача не из простых, и попытка использовать те же приёмы, что и для тарелок, может сработать, но это будет запутаннее, чем взять мини-мойку. Поэтому стоит отдавать предпочтение простоте, но там, где простота неприменима, лучше выбрать сложность — знайте пределы простоты.
#5 Плоская структура лучше вложенной
Программисты любят раскладывать всё по категориям, подкатегориям и подподкатегориям, чтобы разделять функциональность. Звучит упорядоченно, но такая организация кода часто мешает больше, чем помогает.
Нет ничего плохого в том, чтобы держать весь код в одном модуле верхнего уровня: тогда вам не придётся писать что-то вроде from spam.foo.bar.john.doe import chicken , чтобы добраться до нужной функции. Чем больше подкатегорий вы добавляете, тем запутаннее становится код. По возможности придерживайтесь плоской структуры.
#6 Разреженное лучше плотного
Программисты считаются одними из самых умных людей, поэтому нет нужды подчёркивать свой интеллект вычурными трюками. Популярный жанр — «как сделать [сложную задачу] в 1 строку кода». Иногда однострочники уместны, но избегайте ситуаций, когда вы жертвуете читаемостью ради размещения всей логики в одной строке.
Например, такой код:
"""
Code source Al Sweigart
See: https://inventwithpython.com/blog/author/al-sweigart.html0
"""
print('\n'.join("%i bytes = %i bits which has %i possible values." % (j, j*8, 256**j-1) for j in (1 << i for i in range(8))))
"""
1 bytes = 8 bits which has 255 possible values.
2 bytes = 16 bits which has 65535 possible values.
4 bytes = 32 bits which has 4294967295 possible values.
8 bytes = 64 bits which has 18446744073709551615 possible values.
16 bytes = 128 bits which has 340282366920938463463374607431768211455 possible values.
32 bytes = 256 bits which has 115792089237316195423570985008687907853269984665640564039457584007913129639935 possible values.
64 bytes = 512 bits which has 13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084095 possible values.
128 bytes = 1024 bits which has 179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137215 possible values.
"""
Код, безусловно, работает, но читать его — сущий кошмар: он тесный и трудночитаемый.
Посмотрим, как будет выглядеть тот же код в более разреженном варианте:
# Sparse example
bytes_and_bits = {j: j*8 for j in (1 << i for i in range(8))}
for bytes, bits in bytes_and_bits.items():
print(f"{bytes} bytes = {bits} which has {256**bytes-1} possible values")
"""
1 bytes = 8 which has 255 possible values
2 bytes = 16 which has 65535 possible values
4 bytes = 32 which has 4294967295 possible values
8 bytes = 64 which has 18446744073709551615 possible values
16 bytes = 128 which has 340282366920938463463374607431768211455 possible values
32 bytes = 256 which has 115792089237316195423570985008687907853269984665640564039457584007913129639935 possible values
64 bytes = 512 which has 13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084095 possible values
128 bytes = 1024 which has 179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137215 possible values
"""
Такой вариант куда проще читать и делает ровно то же самое, что и однострочник.
#7 Читаемость имеет значение
Если вы ещё не догадались: в Python читаемость — ключевой принцип. Пишете вы код один раз. А читать его будут, скорее всего, много раз. Учитывая это, не стоит выкидывать гласные из имён переменных и функций: если можно назвать функцию create_lst или create_list, выбирайте второе.
#8 Частные случаи недостаточно особенные, чтобы нарушать правила
В Python [и в программировании вообще] есть множество лучших практик. Лучше следовать им, чем изобретать свой путь — иначе легко получить непоследовательный и трудночитаемый код.
#9 Хотя на практике польза важнее чистоты
Девятый урок — продолжение восьмого. Да, лучше придерживаться лучших практик, но и чрезмерное усердие в следовании правилам может сделать код трудночитаемым. Значит, у каждого правила могут быть исключения. Если ваш способ решения задачи практичнее, читабельнее и проще для понимания, лучше отойти от установленных рекомендаций.
#10 Ошибки не должны проходить молча
«Молчаливая» ошибка — это когда программа возвращает код ошибки или None вместо возбуждения исключения. Лучше пусть программа упадёт, чем продолжит работу, «проглотив» ошибку. В долгосрочной перспективе замалчивание ошибок приводит к багам, которые труднее отлавливать.
#11 Если только их не подавляют явно
Одиннадцатый урок — продолжение десятого. Бывают случаи, когда вы намеренно хотите игнорировать ошибки, возникающие в программе. В таких ситуациях лучшая практика — явно подавлять ошибку в коде.
#12 Столкнувшись с неоднозначностью, не поддавайтесь искушению угадывать
Компьютеры делают только то, что мы им говорим: если код ведёт себя не так, как вы ожидаете, значит, он делает именно то, что вы ему предписали. Пытаться исправить поведение, наугад перебирая варианты, пока какой-то не сработает, — плохая стратегия: так можно замаскировать проблему, а не решить её. Сдержите порыв. Лучше вдумчиво проанализируйте логику и критически подойдите к поиску корректного решения.
#13 Должен быть один — и желательно только один — очевидный способ сделать это
Девиз языка Perl: «Сделать это можно множеством способов!». Избыток вариантов часто ведёт к параличу выбора. Похожая ситуация возникает, когда есть несколько способов написать код для одной и той же цели. Свободы больше, но чтобы читать такой код, приходится знать все возможные варианты его написания — лишняя работа, которая не нужна.
#14 Хотя этот способ может быть не очевиден сразу, если вы не голландец
Этот афоризм демонстрирует чувство юмора Тима Питерса: Гвидо ван Россум, создатель языка Python и «доброжелательный диктатор пожизненно» (BDFL), — голландец. Это шутка-напоминание, что понять и помнить все правила языка непросто для всех, кроме его создателя.
#15 Сейчас лучше, чем никогда
Этот афоризм говорит нам, что код, застрявший в бесконечном цикле или зависший, хуже, чем код, который этого избежал.
#16 Хотя нередко «никогда» лучше, чем прямо сейчас
Продолжая мысль шестнадцатого урока: лучше дождаться завершения выполнения программы, чем прервать её раньше времени и получить неверные результаты.
#17 Если реализацию трудно объяснить — это плохая идея
Мало того, что вы сами понимаете свою реализацию: «я-то знаю, что имел в виду» — не аргумент. Программирование — командная деятельность, и если вы не можете объяснить решение коллегам, скорее всего, оно слишком усложнено.
#18 Если реализацию легко объяснить — это может быть хорошая идея
Однако то, что код легко объяснить, не делает его автоматически хорошим — это лишь означает, что его просто объяснить. Плохие решения возможны и здесь, но лёгкость объяснения — признак верного направления.
#19 Пространства имён — это потрясающая идея, давайте использовать их больше!
Пространство имён — это абстракция в Python для организации имён, присвоенных объектам программы. Имея конкретное пространство имён и область видимости, Python способен понять, к какому объекту вы обращаетесь по символьному имени. Смысл афоризма в том, что то, как Python под капотом организует символьные имена, — действительно здорово.
Заключение
В этой статье мы изложили своё понимание «Дзена Python» — набора рекомендаций, призванных мотивировать разработчиков на Python писать чистый и читаемый код. Для одних «Дзен Python» — универсальный чертёж безупречного кода, другие относятся к нему проще. Предлагаем попробовать применить принципы к своему коду и на практике увидеть, как они улучшают результат. Если хотите глубже разобраться в оптимизации кода, загляните в курс DataCamp Writing Efficient Python Code.