Как стать автором
Обновить

Книга «Секреты Python Pro»

Время на прочтение7 мин
Количество просмотров8.4K
image Привет, Хаброжители! Код высокого качества — это не просто код без ошибок. Он должен быть чистым, удобочитаемым и простым в сопровождении. Путь от рядового питониста к профи не прост, для этого вам нужно разбираться в стиле, архитектуре приложений и процессе разработки. Книга «Секреты Python Pro» научит проектировать ПО и писать качественный код, то есть делать его понятным, сопровождаемым и расширяемым.

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

image

Разделение ответственности


В этой главе:

  • Использование средств Python для организации и разделения кода.
  • Выбор способа и времени разделения кода на четко различимые фрагменты.
  • Уровни гранулярности в разделении кода.

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

Определение


Зоной ответственности (concern) я называю совокупность правил отдельной области знаний, с которой имеет дело ПО. Эти правила могут варьироваться по сложности от вычисления квадратного корня до управления платежами в системе электронной коммерции.

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

Примечание


Если вы еще не установили Python на своем компьютере, то сделайте это, чтобы отслеживать код из книги (рекомендации по установке — в приложении в конце книги). Я подожду вас здесь. Получить полный исходный код примеров и упражнений можно в репозитории книги на GitHub (https://github.com/daneah/practices-of-the-python-pro).

2.1. Организация пространства имён


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

Пространства имен полезны в нескольких отношениях:

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

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

Пространства имен — отличная штука! Будем делать их больше!
Дзен языка Python

Имена всех переменных, функций и классов, которые вы когда-либо использовали в Python, находились в том или ином пространстве имен. Имена, такие как x, total или EssentialBusinessDomainObject, являются ссылками на что-то. x = 3 означает «назначить значение 3 имени x», то есть можно в дальнешем ссылаться на x. Так называемая «переменная» — это имя, которое ссылается на значение, хотя в Python имена могут ссылаться на функции, классы и др.

2.1.1. Пространства имен и инструкция import


Когда вы впервые открываете интерпретатор Python, встроенное пространство имен заполняется встроенной начинкой, например функциями без префикса, такими как print() и open(), которые можно сразу использовать в любом месте кода. Вот почему в Python знаменитая легкая инструкция print('Здравствуй, мир!') стала мемом Just Works (просто работает).

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

# sales_tax.py
def add_sales_tax(total, tax_rate):    
    return total * tax_rate

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

image

Функции и классы в модуле также имеют локальное пространство имен, обращаться к которому могут только они:

image

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

image

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

  • Это имя находится во встроенном в Python пространстве имен.
  • Имя находится в глобальном пространстве имен текущего модуля.
  • Имя находится в текущей кодовой строке локального пространства имен.

Приоритет для конфликтующих имен работает в обратном порядке: локальное имя будет переопределять глобальное имя, которое будет переопределять встроенное имя. Запомните, что наиболее специфичным для текущего кода является определение, которое используется сейчас (рис. 2.1).

image

Возможно, вы встречали ошибку NameError: name 'my_var' is not defined1. Она означает, что имя my_var не найдено ни в одном из пространств имен, известных коду. То есть, скорее всего, вы ни разу не передавали переменной my_var значение либо передали его где-то еще и должны его импортировать.

Модули представляют собой отличный способ начать дробить код. Если у вас есть один длинный файл script.py с кучей несвязанных функций в нем, попробуйте разбить эти функции на модули.

2.1.2. Лики импортирования


Синтаксис импортирования в Python на первый взгляд кажется прямолинейным, но есть несколько способов выполнения импорта, и каждый из них приводит к тонким различиям в информации, вносимой в пространство имен. Ранее вы импортировали функцию add_sales_tax() из модуля sales_tax в модуль receipt:

# receipt.py
from sales_tax import add_sales_tax

Эта инструкция добавляет функцию add_sales_tax() в глобальное пространство имен модуля receipt. Хорошо, но предположим, что вы добавляете еще десять функций в модуль sales_tax и хотите использовать их все в receipt. Если вы продолжите идти по тому же пути, то в итоге получите что-то вроде этого:

# receipt.py
from sales_tax import add_sales_tax, add_state_tax, add_city_tax,
➥ add_local_millage_tax, ...

Существует альтернативный синтаксис, который немного улучшает ситуацию:

# receipt.py
from sales_tax import (
    add_sales_tax,
    add_state_tax,
    add_city_tax,
    add_local_millage_tax,
...
)

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

# receipt.py
import sales_tax

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

# receipt.py
import sales_tax
def print_receipt():
    total = ...
    locale = ...
    ...
    print(f'AFTER MILLAGE: {sales_tax.add_local_millage_tax(total,
           locale)}')

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

Предупреждение


Python позволяет импортировать все имена из модуля в укороченной форме с помощью инструкции from themodule import *. Заманчиво использовать эту форму вместо добавления префикса к именам с помощью themodule. на протяжении всего кода, но, пожалуйста, не делайте этого! Импорт с подстановочным знаком может приводить к коллизиям имен и затруднять отладку проблем, поскольку вы не увидите конкретные импортируемые имена. Придерживайтесь явных импортов!

2.1.3. Пространства имен предотвращают коллизии


Чтобы получить текущее время в программе Python, можно импортировать функцию time() из модуля time:

from time import time
print(time())

Результат будет примерно таким:

1546021709.3412101

time() возвращает текущее Unix-время. Модуль datetime тоже содержит что-то с именем time, но делает нечто другое:

from datetime import time
print(time())

На этот раз вы должны увидеть вот такой результат:

00:00:00

Это определение time на самом деле является классом, и его вызов возвращает экземпляр класса datetime.time, который по умолчанию равен полуночи. Что происходит, когда вы импортируете их оба?

from time import time
from datetime import time
print(time()) <----- Это какое определение time?

В случаях двусмысленности Python использует самое последнее определение, о котором он знает. Если вы импортируете имя time из одного места, а затем импортируете другое имя time из другого места, то компилятор будет знать только о последнем. Без пространств имен трудно определить, на какое именно time()ссылается код, и по ошибке можно применить неправильное имя. Это веская причина для импортирования модулей целиком и добавления к ним префиксов.

image

Иногда столкновения имен трудно избежать, даже с теми инструментами, которые вы видели до сих пор. Если вы создадите модуль с тем же именем, что и модуль, встроенный в Python или из сторонней библиотеки, и вам нужно будет использовать их оба в одном модуле, то понадобится больше огневой мощи. К счастью, она совсем рядом, на расстоянии всего одного ключевого слова as, которое нужно назначить как псевдоним имени во время импортирования:

import datetime
from mycoollibrary import datetime as cooldatetime

Теперь модуль datetime доступен, как и ожидалось, и сторонний datetime доступен как cooldatetime.

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

Примечание


Большинство интегрированных сред разработки (IDE, integrated development environment) выдадут предупреждение, когда вы переопределите имя встроенного в Python модуля, чтобы вы не зашли слишком далеко.

Теперь вы готовы импортировать все, что нужно, без проблем. Помните, что префиксы имен модулей (подобные time. и datetime.) полезны в долгосрочной перспективе из-за риска коллизий пространств имен. Когда вы столкнетесь с коллизиями, сделайте глубокий вдох и уверенно переработайте свои инструкции импорта или же создайте псевдоним и продолжайте путешествие!

Более подробно с книгой можно ознакомиться на сайте издательства
» Оглавление
» Отрывок

Для Хаброжителей скидка 25% по купону — Python

По факту оплаты бумажной версии книги на e-mail высылается электронная книга.
Теги:
Хабы:
+4
Комментарии3

Публикации

Информация

Сайт
piter.com
Дата регистрации
Дата основания
Численность
201–500 человек
Местоположение
Россия