Як дзен Python може допомогти вам написати кращий код

Хочете навчитися краще писати код Python? Ось як дзен Python може допомогти вам зробити перші кроки до цього.

Python надзвичайно простий у вивченні. Але написання ідіоматичного коду та коду Pythonic, який легко підтримувати, може бути складним завданням, особливо для програмістів-початківців. PEP-20 представив «Дзен Python», вірш Тіма Пітерса, який окреслює важливість написання коду на Python, який відповідає найкращим практикам.

Щоб прочитати дзен Python, ви можете запустити Python REPL і виконати:

>>> 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!

Як бачимо, більшість афоризмів у Дзен Python не мають пояснень. Деякі афоризми слід поєднувати з наступними при тлумаченні, тоді як інші суперечать попередньому афоризму. Незважаючи на це, Zen of Python — це веселе, привабливе та практичне читання!

Інтерпретація дзен Python

Було запропоновано, що Zen of Python містить 20 керівних принципів програмування на Python. Проте афоризмів поки що лише 19. Перейдемо до них.

Красиве краще, ніж потворне.

Цей афоризм підкреслює важливість написання елегантного та Pythonic коду.

Наступний фрагмент коду має запах коду:

def square(num):
    squares = []
    for i in range(num):
        squares.append(i*i)
    return squares

Функція:

  • Ініціалізує порожній список
  • У функції є цикл, який додає елементи в кінець списку та
  • Нарешті повертає список
  IBM WebSphere Application Server Посібник для початківців

Хоча це функціонально правильно — це не Pythonic — і його важко підтримувати.

Ви можете написати це набагато елегантніше, використовуючи генератори. Ось функція генератора, еквівалентна наведеній вище функції:

def square(num):
    for i in range(num):
        yield i*i

Або навіть краще, ви можете мати наступний вираз розуміння генератора:

num = ...
squares = (i*i for i in range(num))

Явне краще, ніж неявне.

Під час написання коду не залишайте інших розробників і користувачів здогадуватися про неявну або типову поведінку коду. Будьте явними. Розглянемо приклад імпорту символів підстановки:

from some_module import * # wildcard import
from some_other_module import *

result = some_function() # where did this come from?

Наскільки це можливо, уникайте імпорту символів підстановки. Тому що це не явно і неефективно. Будьте конкретними, імпортуючи функції та класи з інших модулів:

from some_module import this_function # explicit import

result = this_function() # we now know.

Просте краще, ніж складне.

Цей афоризм стверджує, що ми повинні зберігати простий код і уникати непотрібної складності. Наприклад: ви можете перевернути рядок і реалізувати таке рекурсивне рішення:

def reverse_string(my_string):
  if my_string == "":
    return my_string
  else:
    return reverse_string(my_string[1:]) + my_string[:1]

Хоча це працює, це, ймовірно, надмірно розроблене рішення цієї проблеми, враховуючи, що існують простіші та більш пітонічні способи зробити це.

Ось підхід до нарізки рядків:

>>> rev_string = my_string[::-1]
>>> rev_string
'nohtyP'

А ось підхід із використанням вбудованих методів і функцій:

>>> rev_string = ''.join(reversed(my_string))
>>> rev_string
'nohtyP'

Складне краще, ніж складне.

Отже, що передає цей наступний афоризм у Zen of Python?

Перевертання рядків у Python — надзвичайно проста операція. Однак на практиці нам може знадобитися більш складна логіка. Ось досить простий приклад:

Скажімо, вам потрібно підключитися до бази даних:

  • Ви повинні спочатку проаналізувати конфігураційний файл toml, щоб отримати інформацію про конфігурацію бази даних.
  • Слід встановити з’єднувач бази даних.
  • Потім ви можете визначити функцію для підключення до бази даних, передбачити помилки підключення, реалізувати обробку помилок і багато іншого.
  • Нарешті, після підключення до бази даних ви можете запитати її.

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

Плоский краще, ніж вкладений.

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

Тим не менш, вам може часто знадобитися вийти за межі плоскої конструкції. Але навіть якщо вам знадобиться гніздування, зведіть його до мінімуму.

  13 інструментів, які допоможуть вам керувати вашою партнерською програмою

Ось приклад:

from db_info.config.actions.parse.parse_config import parse_toml # too difficult to parse!
...

from db_config.parse_config import parse_toml # much better!
...

Рідкий краще, ніж щільний.

Якщо ви тільки починаєте свій шлях розробника, у вас може виникнути спокуса надмірно використовувати деякі функції мови. Розуміння списків, наприклад, є Pythonic, але лише тоді, коли ви використовуєте їх там, де вони потрібні.

Подивіться на наступне розуміння:

prices_dict = {'melons':40,'apples':70,'berries':55}
items = [(fruit,price) for fruit in prices_dict.keys() if fruit.startswith('m') for price in prices_dict.values() if price < 50]
print(items)
# Output: [('melons', 40)]

Розуміння списку занадто щільне, і його важко розібрати. У цьому випадку використання еквівалента циклу for з умовними виразами буде легшим для читання. Це означає, що розуміння важко зрозуміти. 🙂

Читабельність має значення.

Ви завжди повинні писати читабельний код. Ось кілька простих способів покращити читабельність коду:

  • Використання описових імен змінних
  • Додавання рядків документів для функцій і класів
  • Коментування коду, де потрібно
  • Додавання підказок типу для аргументів і типів повернення функцій

Особливі випадки не настільки особливі, щоб порушувати правила.

Ви повинні — наскільки це можливо — дотримуватися правил мови та рекомендованих найкращих практик.

Але чи завжди це можливо? Ні, і тому у нас наступний афоризм.

Хоча практичність перемагає чистоту.

Це продовження попереднього афоризму. Хоча рекомендується дотримуватися правил мови, у деяких випадках цілком нормально не дотримуватися деяких принципів.

Помилки ніколи не повинні проходити мовчки.

У програмі Python помилки досить поширені. Як хороша практика, ви повинні завжди обробляти помилки, а не замовчувати їх як швидке вирішення.

Ви можете передбачити та застосувати відповідну обробку помилок — для різних типів помилок:

try:  
    # doing this
except ErrorType1:
    # do something
except ErrorType2:
    # do something else
...

Слід уникати простих і загальних винятків. Новіші версії Python (починаючи з Python 3.11) підтримують ланцюжок винятків і групи винятків для більш складної обробки винятків.

Якщо явно не замовчувати.

Це слідує за попереднім афоризмом. Якщо дизайн вимагає або дозволяє заглушити помилку, то це має бути зроблено явно.

Наприклад: під час підключення до бази даних ви можете зіткнутися з OperationalError через неправильну конфігураційну інформацію. Спробуйте підключитися за допомогою спеціальної конфігурації. Якщо виникає OperationalError, використовуйте конфігурацію за замовчуванням і спробуйте підключитися до бази даних.

try:
   # connecting using custom config
except OperationalError:
   # connect using default config

Зіткнувшись з двозначністю, відмовтеся від спокуси вгадати.

Цей афоризм у Zen of Python не потребує пояснень. Коли сумніваєтеся, не гадайте. Але запустіть код і перевірте результат. Тоді, залежно від того, чи є у вас бажана поведінка, покращте читабельність або змініть логіку за потреби.

Розглянемо такий простий приклад із кортежем булевих значень:

>>> True, True == (True, True)
(True, False)
>>> True, (True == (True, True))
(True, False)
>>> (True, True) == (True, True)
True

Має бути один — і бажано тільки один — очевидний спосіб зробити це.

Щоб виконати певне завдання, повинен існувати один і тільки один рекомендований пітонічний спосіб це зробити. Однак для будь-якої проблеми ми можемо мати кілька рішень.

  Чи можете ви отримати кілька облікових записів користувачів на iPad?

Навіть у простому прикладі інверсії рядка ми розглянули рекурсивне рішення, нарізку рядка та метод join().

Це також внутрішній жарт, враховуючи непослідовне використання тире. Зазвичай ми використовуємо тире без пробілів на початку та в кінці. Або ми використовуємо його з пробілами на початку та в кінці.

Отже, ось що ми можемо зробити висновок. Афоризм, який наголошує на тому, що має бути один — і тільки один — Pythonic спосіб робити речі, сам по собі може бути написаний більш ніж двома способами.

Хоча спочатку цей шлях може бути неочевидним, якщо ви не голландець.

Написано на легкій ноті, це відноситься до Гвідо Ван Россума, творця Python (який є голландцем). (Найбільш) Python спосіб виконання конкретного завдання — природний лише для творців Python.

Тому розробникам потрібен досвід — і навчання на досвіді — щоб краще використовувати особливості мови.

Зараз краще, ніж ніколи.

Як і кілька інших афоризмів у Дзен Python, цей також можна інтерпретувати кількома різними способами.

Одна інтерпретація полягає в тому, що для розробника досить часто зволікати з початком кодування проекту. Замість того, щоб чекати, щоб спланувати найдрібніші деталі проекту, краще почати зараз.

Інша можлива інтерпретація така: код, який виконується в кінцеву кількість кроків і завершується, часто кращий, ніж код, який має помилки і застряє в нескінченному циклі.

Хоча ніколи не буває краще, ніж зараз.

Цей афоризм ніби суперечить попередньому. Хоча краще не зволікати, ми все ж повинні продумати проблему та розробити код відповідно.

Кодувати модуль — без належного обмірковування — насичений кодовими запахами та антишаблонами — погана ідея. Оскільки такий код важко рефакторингувати та впроваджувати коригувальні заходи.

Якщо реалізацію важко пояснити, це погана ідея.

Будь-яка логіка — якою б складною вона не була — завжди може бути реалізована у формі, яку легко пояснити та зрозуміти.

Якщо реалізацію важко пояснити, ймовірно, є непотрібна складність. Код можна змінювати або переробляти, щоб його було легше слідувати.

Якщо реалізацію легко пояснити, це може бути хорошою ідеєю.

Це пов’язано з попереднім афоризмом і також не потребує пояснень. Якщо реалізацію можна пояснити простими словами, то це, швидше за все, хороша ідея.

Тому що такий код, реалізацію якого можна описати простими словами, дуже ймовірно, буде читабельним і легким для дотримання — з мінімальною складністю.

Простори імен — це одна чудова ідея — давайте робити більше таких!

У Python до об’єктів у певній області можна отримати доступ, використовуючи їхні імена в просторі імен. Наприклад, ви можете створити клас і використовувати його як шаблон для створення екземплярів класу. Тепер усі змінні екземпляра будуть у просторі імен екземпляра.

Це дозволяє нам використовувати об’єкти з однаковими іменами — без конфліктів — оскільки вони знаходяться в різних просторах імен. Однак ви повинні використовувати їх лише за потреби та стежити, щоб простота та читабельність коду не були скомпрометовані.

Висновок

Це все для цього підручника! Сподіваюся, цей посібник допоміг вам зрозуміти, як Zen of Python наголошує на стилі коду та хороших практиках кодування в Python. Чим більше ви кодуєте, тим краще у вас це вийде.

Якщо вам цікаво навчитися писати стислий і читабельний код, прочитайте цю статтю про Python one-liners.