Как структурировать Python-код


Неймспейсы

В процессе работы мы создаем имена и связываем с ними какие-то объекты.

Пространство имен - это сопоставление имен переменных, функций, классов и реальных объектов в оперативной памяти.


Множество ссылок от все имен до объектов называется пространством имен.

В любой момент времени можно спросить у пространства имен, а связан ли с конкретным именем какой-нибудь объект.

Каждый вызов функции сопровождается созданием нового пространства имен.

Первое пространство имен создается, когда мы запускаем интерпретатор. Это пространство имен builtins содержит в себе встроенные функции и классы: 
  • int, str, float, bool;
  • max, abs, id, ...

Второе пространство имен, которое создается это пространство main. Оно является глобальным и содержит в себе те имена, которые объявлены на самом верхнем уровне кода.

x не попадет в глобальный namespace. Тело функции не исполняется, когда мы его определяем. Тело функции исполняется, когда мы вызываем эту функцию. Однако даже, когда мы вызовем функцию, x не будет лежать в глобальном неймспейсе. При вызове функции создается локальный неймспейс.

Локальные пространства имен не создаются, когда мы используем условный оператор или циклы:
x = 13
if x % 2 == 1:
 x += 1
print(x) #4
Внутри цикла for создается имя, которое попадает в текущий неймспейс:
for i in range(5):
 x = i * i

print(x) # 16
Как делать не нужно:
for i in []:
 y = i * i
print(y) # NameError


Система модулей и импорт

Имя модуля это имя файла без расширения .py. Когда мы пишем import и модуль, мы берем файл с модулем и целиком его исполняем. Те имена, которые останутся в пространстве имен связанном с исполнением этого модуля, мы затем сможем импортировать.
import exceptions7

print(exceptions7.greet("Student"))

Можно использовать import и в режиме работы интерпретатора.
>>> import exceptions7
Import is execution
>>> exceptions7.greet
<function greet at 0x00802DF8>
>>> exceptions7.BadName
<class 'exceptions7.BadName'>

Можно разграничивать код, который мы будем исполнять при импорте от кода, который нам бы не стоило выполнять при импорте. Для этого внутри модуля всегда доступно глобальное имя __name__. Таким образом для того чтобы понять, когда на импортируют, а когда запускают в интерпретаторе необходимо просто сравнить текущее имя с именем __main__.
def fib(k):
    if k == 0 or k == 1:
        return 1
    else:
        return fib(k - 1) + fib(k - 2)

if __name__ == "__main__":
    print(__name__)
    print(fib(31))

При импорте модуля происходит исполнение кода, однако это делается лишь единожды. Все глобальные имена, которые остались внутри данного неймспейса мы закрепим за нашим объектом модуля. При повторном импорте будет переиспользован старый объект для нашего модуля. Когда мы пишем import exceptions, интерпретатор идет и спрашивает у словаря sys.modules, а есть ли у тебя ключ 'exceptions'. Если ключа нет, тогда происходит исполнение модуля exceptions.



Таким образом любой модуль будет исполнен лишь единожды.

Если в sys.modules не было найдено имя модуля, то мы попробуем найти его в файле с расширением .py в текущей директории. Если и в текущей директории ничего не будет найдено, тогда мы пойдем искать его во внешние библиотеки. Для того чтобы понять в каком порядке это будет происходить можно вывести все пути в списке sys.path.
import sys

for path in sys.path:
    print(path)

Также в языке Python мы можем импортировать не весь модуль целиком, а лишь какие-то имена из него.
from exceptions7 import BadName, greet as exc_greet
import exceptions7 as exc


def greet():
    print("Greetings!")

print(BadName)
print(greet)
greet()
print(exc_greet("Student"))
print(exc)

Чтобы импортировать все имена из модуля надо использовать звездочку.
from exceptions7 import *
print(BadName)
print(greet("Student"))
Однако через звездочку импортируются далеко не все имена. В конструкции __all__ определяются те имена, которые мы можем импортировать с помощью звездочки. Даже если не определить эту конструкцию, то тогда не будут импортироваться имена начинающиеся с нижнего подчеркивания.
GREETING = "Hello, "


class BadName(Exception):
    pass


def greet(n):
    if n[0].isupper():
        return GREETING + n
    else:
        raise BadName(n + " is inappropriate name")

__all__ = ["BadName", "greet"]
В вышеприведенном коде константа GREETING не будет импортироваться. Но если убрать __all__ и написать _GREETING, то тогда она тоже не будет импортироваться.

Пакеты

Пакет - это удобный способ представления некоторого числа файлов в качестве одного модуля. Пакеты по сути это такие папки, которые могут вести себя как модули, которые мы тоже можем импортировать. Интерпретатор определяет является ли папка пакетом по наличию файла __init__.py внутри. Если внутри находится файл __init__.py, то он и исполняется при импорте.

PyPI (Python Package Index) содержит в себе десятки тысяч пакетов предназначенных для решения различных задач. Пакеты устанавливаются с помощью утилиты pip. Для того, чтобы установить какую-нибудь библиотеку из Python Package Index (например пакет simple-crypt из следующей задачи), необходимо запустить команду pip install, например:
pip install simple-crypt


Комментариев нет:

Отправить комментарий