Python Заметки
2.31K subscribers
59 photos
2 videos
2 files
211 links
Интересные заметки и обучающие материалы по Python

Контакт: @paulwinex

⚠️ Рекламу на канале не делаю!⚠️

Хештеги для поиска:
#tricks
#libs
#pep
#basic
#regex
#qt
#django
#2to3
#source
#offtop
Download Telegram
Подразумеваемые неймспейсы или неявные пакеты.

Этот функционал добавлен в Python 3.3
Что он означает?

Ранее, до 3.3 пакетами считались лишь директории, в которых есть файл __init__.py.
Этот файл одновременно являлся свидетельством того, что директория это Python-пакет, и служил "телом" этого пакета. То есть местом, где можно написать код, как это делается внутри модуля. Этот код исполняется в момент импорта пакета, так что его принято называть "код инициализации пакета".

Начиная с версии 3.3 Любая директория считается пакетом и Python будет пытаться использовать любую директорию для импорта.

Конечно, не любую в файловой системе, а только те что находятся в sys.path.

Это значит, что теперь __init__.py нужно делать только если:

🔸 вам требуется создать код инициализации пакета
🔸 нужна совместимость со старыми версиями Python

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

repo_name/
my_library/
__init__.py
main.py
examples/
exam1.py
exam2.py

В этом репозитории пакетом является только my_library, остальные директории это не пакеты, это просто дополнительный код в файлах. Директория examples не добавлена в sys.path, в ней нет рабочих модулей. Но если она лежит рядом с my_library, то Python вполне сможет импортнуть из неё модули, так как посчитает что examples это валидный пакет.

Конечно, пример несколько надуманный. Никто не будет добавлять корень репозитория в sys.path. Но, я думаю, суть ясна. Иногда директория это просто директория а не пакет!

#basic #pep
Первая директория в sys.path

🔸 Когда вы запускаете Python-интерпретатор в интерактивном режиме, в системные пути (sys.path) в самое начало добавляется текущая рабочая директория

>>> for path in sys.path:
... print(f'"{path}"')
""
"/usr/lib/python37.zip"
"/usr/lib/python3.7"
...

Первая строка пустая, что и означает текущую рабочую директорию.

🔸 Если вы запускаете интерпретатор передавая скрипт как аргумент, то история получается иная. На первом месте будет директория в которой располагается скрипт. А текущая рабочая директория игнорируется.

Пишем скрипт с таким содержанием:

# script.py
import sys
for path in sys.path:
print(f'"{path}"')

Запускаем

python3 /home/user/dev/script.py

Получаем

"/home/user/dev"
"/usr/lib/python37.zip"
"/usr/lib/python3.7"
...

🔸 Если вы запускаете скрипт по имени модуля то на первом месте будет домашняя директория текущего юзера

python3 -m script

"/home/user"
"/usr/lib/python37.zip"
"/usr/lib/python3.7"
...

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


На что это влияет?
На видимость модулей для импорта. Если вы ждёте, что, запустив скрипт по пути, сможете импортировать модули из текущей рабочей директории, то вы ошибаетесь. Придётся добавлять путь os.getcwd() в sys.path самостоятельно или заранее объявлять переменную PYTHONPATH.

#basic
Многие из тех кто активно работал с Python2 несколько удивлены, почему в Python3 удобная функция reload() переехала из builtin в imp а потом и в importlib?
Ну было же удобно! А теперь лишний импорт😖

Дело в том, что начиная с Python3.3 функция reload() переписана на Python вместо .
Что это нам даёт?

🔸 Такой код проще поддерживать и развивать

🔸 Python код легче читать, изучать и понимать.
Сравните это ➡️ и это ➡️.

🔸 Как результат пункта 2, проще писать свои расширения импорта. Например, пользовательский импортёр с какой-либо хитрой логикой по аналогии с импортом из zip архивов.

А есть ли у этого решения недостатки? Да, они всегда есть.

🔹 Так как это не builtin функция, её следует импортнуть перед использованием

🔹 Скорость замедлилась примерно на 5%. Очевидно, что это совершенно не критично. К тому же от версии к версии логика импорта будет оптимизироваться и ускоряться.
В самом начале файла importlib/__init__.py мы видим такой импорт:

import _imp  # Just the builtin component, NOT the full Python module

То есть часть функционала по прежнему написана на Си, но достаточно низкоуровневая.

#basic
Вопросы про переменную PYTHONPATH

🔸 Как она определяет пути поиска модулей при импорте?

Пути поиска модулей находятся в списке sys.path. Как формируется этот список?
Исходя из документации мы может выделить 3 основных этапа.

▫️ Путь к запускаемому скрипту или рабочая директория
▫️ Переменная PYTHONPATH
▫️ Стандартные пути к библиотекам

Это значит, что все три этапа выполняются в момент инициализации интерпретатора. Результат заполняет список sys.path. В том числе и пути, указанные в переменной PYTHONPATH.

🔸 Можно ли добавлять новые пути в эту переменную в Python-коде?

Можно, но учитывая, что используется она только во время старта интерпретатора, никакого эффекта это иметь не будет.
Для изменения путей поиска модулей в коде нужно изменять непосредственно список sys.path.

🔸 Можно ли указать много путей для поиска?

Да, с помощью переменной PYTHONPATH можно указать несколько директорий, разделённых символом разделения пути. Для Linux это символ ":", для Windows это ";".
Например:

export PYTHONPATH=/mnt/libs:~/mylibs

#basic #tricks