Это пример того, как надо начинать писать статью
Содержание
- Модули в Python: обзор
- Правильное место для модуля
- Оператор import
- Функция dir()
- Выполнение модуля как скрипта
- Перезагрузка модуля
- Пакеты Python
- Инициализация пакета
- Импорт * из пакета
- Подпакеты
- Заключение
Здесь вы познакомитесь с модулями (modules) и пакетами (packages) Python, с двумя механизмами модульного программирования (modular programming).
Модульное программирование есть процесс разбиения большой и громоздкой задачи на отдельные, более маленькие, управляемые подзадачи и модули. Все это по научному называется декомпозицией. Далее отдельные модули могут быть скомпонованы вместе, как строительные блоки, для создания более крупного приложения, решающего вашу задачу.
У такого, модульного подхода при проектировании кода больших приложений есть сразу несколько преимуществ:
- Простота: Вместо того, чтобы думать о всей проблеме в целом, обычно, в модуле фокусируются на решении одной, относительно небольшой, части программы. Работая над одним модулем, сужается область размышлений, что делает разработку проще и менее подверженной ошибкам.
- Ремонтопригодность: Обычно, модули имеют логические границы между различными задачами проблемы в целом. Если в модулях свести к минимуму взаимозависимости, то снижается вероятность того, что модификации одного модуля окажут влияние на другие части программы. Возможно, вы даже сможете вносить изменения в модуль, не зная ничего о приложении, для которого он написан. Таким образом, над одним приложением может работать большая группа программистов, что есть совместная разработка.
- Повторное использование кода: Функциональность, определенная в одном модуле, может быть легко использована повторно (через соответствующий интерфейс) другими приложениями, что избавляет от необходимости дублирования.
- Область действия: Обычно, в модуле определяется отдельное пространство имен, что помогает избежать коллизий между идентификаторами в разных областях программы. (Один из тезисов Дзен Python гласит Пространства имён — отличная штука! Будем делать их больше!)
Функции, модули и пакеты есть все конструкции в Python, которые способствуют модульному программированию на Python.
Модули в Python: обзор
На самом деле в Python есть три способа определения модуля:
- Модуль может быть написан на самом Python.
- Модуль может быть написан на C и динамически подгружен во время исполнения, как модуль
re
(regular expression). - Модуль, встроенный в интерпретатор, как инструмент
itertools
.
Во всех трёх случаях доступ к модулю предоставляется одинаково — с помощью оператора import
.
Здесь основное внимание будет уделено модулям, написанным на Python. Крутая штука в модулях, написанных на Python, заключается в том, что их чрезвычайно просто сделать. Все, что нужно, так это просто создать файл, который содержит допустимый код Python и дать ему имя с расширением .py
. Это всё! Никакого специального синтаксиса или танцев с бубном не требуется.
Например, у вас есть файл mod.py
со следующим кодом:
s = "Если товарищ Наполеон говорит, то это должно быть правильно." a = [100, 200, 300] def foo(arg): print(f'arg = {arg}') class Foo: pass
В mod.py
определены следующие объекты:
s
(строка)a
(список)foo()
(функция)Foo
(класс)
Предполагая, что файл mod.py
расположен в правильном месте, о котором расскажем позже, доступ к этим объектам модуля можно получить импортируя модуль следующим образом:
>>> import mod >>> print(mod.s) Если товарищ Наполеон говорит, то это должно быть правильно. >>> mod.a [100, 200, 300] >>> mod.foo(['quux', 'corge', 'grault']) arg = ['quux', 'corge', 'grault'] >>> x = mod.Foo() >>> x <mod.Foo object at 0x03C181F0>
Правильное место для модуля
Продолжая разговор о вышеприведённом примере, посмотрим, что делает Python, выполняя оператор:
import mod
Когда интерпретатор выполняет оператор import
, то он ищет файл mod.py
в следующих каталогах в порядке приоритетности:
- Текущий каталог, т.е. тот каталог, из которого был запущен наш скрипт с оператором
import
, если он запущен в интерактивном режиме. - В списке каталогов, определенном в установленной переменной окружения
PYTHONPATH
. (ФорматPYTHONPATH
зависит от операционной системы, но всегда похож на переменную окружения ОСPATH
.) - В списоке каталогов, определённых и настроенных во время установки Python.
В результате, в переменной окружения sys.path
модуля sys
содержится список каталогов для поиска импортируемого модуля:
>>> import sys >>> sys.path ['', 'C:\\Users\\john\\Documents\\Python\\doc', 'C:\\Python36\\Lib\\idlelib', 'C:\\Python36\\python36.zip', 'C:\\Python36\\DLLs', 'C:\\Python36\\lib', 'C:\\Python36', 'C:\\Python36\\lib\\site-packages']
Примечание: Значение в
sys.path
зависит от установки и на вашем компьютере, наверняка, будет отличаться от того, что написано выше.
Таким образом, чтобы убедиться, что ваш модуль найден, вам необходимо выполнить одно из следующих действий:
- Разместить файл
mod.py
в текущем каталоге, если вы работаете в интерактивном режиме. - Изменить переменную окружения
PYTHONPATH
таким образом, что бы она содержала название каталога, где расположен файлmod.py
- или: Разместить файл
mod.py
в одном из каталогов уже описанном в переменнойPYTHONPATH
.
- или: Разместить файл
- Разместить файл модуля
mod.py
в одном из каталогов, которые определены при установке интерпретатора в вашей операционной системе.
На самом деле есть еще одна дополнительная возможность — можно поместить файл модуля в любой каталог по своему выбору, а затем изменить sys.path
во время выполнения, чтобы он содержал этот каталог. Например, можно поместить mod.py
в каталог C:\Users\john
и затем выполнить следующие операторы:
>>> sys.path.append(r'C:\Users\john') >>> sys.path ['', 'C:\\Users\\john\\Documents\\Python\\doc', 'C:\\Python36\\Lib\\idlelib', 'C:\\Python36\\python36.zip', 'C:\\Python36\\DLLs', 'C:\\Python36\\lib', 'C:\\Python36', 'C:\\Python36\\lib\\site-packages', 'C:\\Users\\john'] >>> import mod
После того, как модуль импортирован, можно определить его местоположение с помощью атрибута __file__
модуля:
>>> import mod >>> mod.__file__ 'C:\\Users\\john\\mod.py' >>> import re >>> re.__file__ 'C:\\Python36\\lib\\re.py'
Часть каталога __file__
должна быть одной из каталогов в sys.path
.
Оператор import
Содержимое модуля становится доступным вызывающему объекту после выполнения оператора import
, который может быть записан в нескольких форматах. Их мы сейчас и обсудим.
import <module_name>
Простейшая форма уже показана выше:
import <module_name>
Заметим, что это не делает напрямую содержимое модуля доступным для вызывающей стороны. Каждый модуль имеет свою собственную таблицу частных определений, которая служит глобальной таблицей определений всех объектов в модуле. Таким образом, модуль создает отдельное пространство имен, как уже отмечалось.
Оператор import <module_name>
только размещает <module_name>
в таблице определений вызвавшего объекта. Объекты остаются в собственной таблице определений модуля.
Получить оступ к объектам модуля можно только, используя префикс <module_name>
, так называемая точечная нотация, так, как показано ниже.
После выполнения оператора import
mod
размещается в локальной таблице определений. Таким образом, mod
становится видимым в локальном контексте вызвавшего его кода:
>>> import mod >>> mod <module 'mod' from 'C:\\Users\\john\\Documents\\Python\\doc\\mod.py'>
Но s
и foo
остаются в собственной таблице определений модуля и не доступны в локальном контексте вызвавшего скрипта:
>>> s NameError: name 's' is not defined >>> foo('quux') NameError: name 'foo' is not defined
Для доступа в локальном контексте имена объектов, определенных в модуле, должны иметь префикс mod
:
>>> mod.s 'Когда говорит товарищ Наполеон, то это должно быть правильно.' >>> mod.foo('quux') arg = quux
В одном операторе import
можно указать несколько модулей, разделенных запятыми:
import <module_name>[, <module_name> ...]
from <module_name> import <name(s)>
Альтернативный формат записи оператора import
позволяет импортировать отдельные объекты из модуля непосредственно в таблицу определений вызывающего скрипта:
from <module_name> import <name(s)>
После выполнения приведенного выше оператора в среде вызывающего скрипта на <name(s)>
можно ссылаться без префикса <module_name>
:
>>> from mod import s, foo >>> s 'Когда говорит товарищ Наполеон, то это должно быть правильно.' >>> foo('quux') arg = quux >>> from mod import Foo >>> x = Foo() >>> x <mod.Foo object at 0x02E3AD50>
Поскольку эта форма импорта помещает имена объектов непосредственно в таблицу определений вызывающего скрипта, то все объекты с таким же именем будут перезаписаны:
>>> a = ['foo', 'bar', 'baz'] >>> a ['foo', 'bar', 'baz'] >>> from mod import a >>> a [100, 200, 300]
Можно даже сделать import
всего из модуля одним махом, без разбора:
from <module_name> import *
Это поместит имена всех объектов из<module_name>
в локальную таблицу определений, за сключением тех, которые начинаются с символа подчеркивания (_
).
Например:
>>> from mod import * >>> s 'Когда говорит товарищ Наполеон, то это должно быть правильно.' >>> a [100, 200, 300] >>> foo <function foo at 0x03B449C0> >>> Foo <class 'mod.Foo'>
В крупномасштабном производственном коде это не рекомендуется, птому как немного опасно, ожним махом вводить имена в локальную таблицу определений. Если вы не знаете их всех хорошо и не уверены, что конфликта не будет, то есть хороший шанс непреднамеренно перезаписать существующие имена. Однако, такой синтаксис очень удобен, когда вы просто возитесь с интерактивным интерпретатором при тестировании или отладке, потому что он быстро дает вам доступ ко всему, что может предложить модуль, без большого набора текста.
from <module_name> import <name> as <alt_name>
Кроме того, можно сделать import
отдельных объекты, вводя их в локальную таблицу определений с альтернативными именами:
from <module_name> import <name> as <alt_name>[, <name> as <alt_name> …]
Это позволяет размещать имена непосредственно в локальной таблице определений, избегая конфликтов с ранее существующими именами:
>>> s = 'foo' >>> a = ['foo', 'bar', 'baz'] >>> from mod import s as string, a as alist >>> s 'foo' >>> string 'Когда говорит товарищ Наполеон, то это должно быть правильно.' >>> a ['foo', 'bar', 'baz'] >>> alist [100, 200, 300]
import <module_name> as <alt_name>
Вы также можете импортировать весь модуль под другим именем:
import <module_name> as <alt_name>
>>> import mod as my_module >>> my_module.a [100, 200, 300] >>> my_module.foo('qux') arg = qux
Содержимое модуля может быть импортировано из функции. В этом случае import
не происходит до тех пор, пока функция не будет вызвана:
>>> def bar(): ... from mod import foo ... foo('corge') ... >>> bar() arg = corge
Однако, синтаксис Python 3 не допускает оператора import *
внутри описания функции:
>>> def bar(): ... from mod import * ... SyntaxError: import * only allowed at module level
Наконец, оператор try
с выражением except ImportError
позволяет избежать рекорректного import
:
>>> try: ... # Non-existent module ... import baz ... except ImportError: ... print('Module not found') ... Module not found
>>> try: ... # Existing module, but non-existent object ... from mod import baz ... except ImportError: ... print('Object not found in module') ... Object not found in module
Функция dir()
Встроенная функция dir()
возвращает список всех имен, определенных в пространстве. Если при вызове отсутствуют аргументы, то она создает алфавитно упорядоченный список имен в текущей локальной таблице определений:
>>> dir() ['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__'] >>> qux = [1, 2, 3, 4, 5] >>> dir() ['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'qux'] >>> class Bar(): ... pass ... >>> x = Bar() >>> dir() ['Bar', '__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'qux', 'x']
Обратите внимание, что при первом вызове dir()
перечисляются несколько имен, которые определяются автоматически и уже находятся в пространстве имен при запуске интерпретатора. По мере определения новых имен (quux
, Bar
, x
) они появляются при последующих вызовах dir()
.
Это бывает полезно для понимания того, что именно было добавлено в пространство имен с помощью инструкции import:
>>> dir() ['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__'] >>> import mod >>> dir() ['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'mod'] >>> mod.s 'Когда говорит товарищ Наполеон, то это должно быть правильно.' >>> mod.foo([1, 2, 3]) arg = [1, 2, 3] >>> from mod import a, Foo >>> dir() ['Foo', '__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'a', 'mod'] >>> a [100, 200, 300] >>> x = Foo() >>> x <mod.Foo object at 0x002EAD50> >>> from mod import s as string >>> dir() ['Foo', '__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'a', 'mod', 'string', 'x'] >>> string 'Когда говорит товарищ Наполеон, то это должно быть правильно.'
При задании в качестве аргумента имени модуля, dir()
перечислит имена, определенные в этом модуле:
>>> import mod >>> dir(mod) ['Foo', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'a', 'foo', 's']
>>> dir() ['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__'] >>> from mod import * >>> dir() ['Foo', '__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'a', 'foo', 's']
Выполнение модуля как скрипта
Любой файл с расширением .py
, содержащий модуль, по существу является скриптом Python и нет никаких причин, по которым он не может быть выполнен самостоятельно.
Здесь снова есть mod.py
как это было определено выше:
mod.py
s = "Когда говорит товарищ Наполеон, то это должно быть правильно." a = [100, 200, 300] def foo(arg): print(f'arg = {arg}') class Foo: pass
Этот скрипт можно запустить:
C:\Users\john\Documents>python mod.py C:\Users\john\Documents>
Там нет ошибок и, видимо, это сработало, но, конечно, не очень интересно. Так, как здесь написано, только определяет объекты, но ничего не делает с ними и не генерирует никакого вывода.
Давайте изменим вышеупомянутый модуль Python, чтобы он генерировал некоторый вывод при запуске в виде скрипта:
mod.py
s = "Когда говорит товарищ Наполеон, то это должно быть правильно." a = [100, 200, 300] def foo(arg): print(f'arg = {arg}') class Foo: pass print(s) print(a) foo('quux') x = Foo() print(x)
Теперь должно быть немного интереснее:
C:\Users\john\Documents>python mod.py Когда говорит товарищ Наполеон, то это должно быть правильно. [100, 200, 300] arg = quux <__main__.Foo object at 0x02F101D0>
К сожалению, теперь он также генерирует вывод при импорте в виде модуля:
>>> import mod Когда говорит товарищ Наполеон, то это должно быть правильно. [100, 200, 300] arg = quux <mod.Foo object at 0x0169AD50>
Это, вероятно, не то, что вы хотите. Обычно модуль генерирует выходные данные при импорте.
Было бы неплохо, если бы можно было различать, когда файл загружается как модуль и когда он запускается как отдельный скрипт?
Просите, и дано вам будет.
Когда файл .py
импортируется как модуль, Python устанавливает специальную переменную dunder в значение имени модуля __name__
. Однако, если файл запускается как отдельный скрипт, то __name__
(творчески) устанавливается в строку '__main__'
. Используя этот факт, можно определить, что происходит во время выполнения, и соответственно изменить поведение:
mod.py
s = "Когда говорит товарищ Наполеон, то это должно быть правильно." a = [100, 200, 300] def foo(arg): print(f'arg = {arg}') class Foo: pass if (__name__ == '__main__'): print('Executing as standalone script') print(s) print(a) foo('quux') x = Foo() print(x)
Теперь, если вы запускаете его, как скрипт, то получите результат:
C:\Users\john\Documents>python mod.py Executing as standalone script Когда говорит товарищ Наполеон, то это должно быть правильно. [100, 200, 300] arg = quux <__main__.Foo object at 0x03450690>
Но если вы импортируете его, как модуль, то:
>>> import mod >>> mod.foo('grault') arg = grault
Модули часто разрабатываются с возможностью запуска в качестве отдельного сценария для тестирования функциональности, содержащейся в модуле. Это называется модульное тестирование. Например, вы создали модуль fact.py
, где вычисляете factorial следующим образом:
fact.py
def fact(n): return 1 if n == 1 else n * fact(n-1) if (__name__ == '__main__'): import sys if len(sys.argv) > 1: print(fact(int(sys.argv[1])))
Файл может рассматриваться как модуль, а функция fact()
импортируется:
>>> from fact import fact >>> fact(6) 720
Но он также может быть запущен как отдельный, передавая целочисленный аргумент в командной строке для тестирования:
C:\Users\john\Documents>python fact.py 6 720
Перезагрузка модуля
Из соображений эффективности модуль загружается только один раз за сеанс интерпретатора. Это хорошо для определений функций и классов, которые обычно составляют основную часть содержимого модуля. Но модуль также может содержать исполняемые операторы, обычно для инициализации. Помните, что эти операторы будут выполняться только при первом импорте модуля.
Рассмотрим следующий файл mod.py
:
mod.py
a = [100, 200, 300] print('a =', a)
>>> import mod a = [100, 200, 300] >>> import mod >>> import mod >>> mod.a [100, 200, 300]
Оператор print()
не выполняется при последующем импорте. (В этом отношении ни один из них не является оператором присваивания, но, как показывает окончательное отображение значения mod.a
, это не имеет значения. Как только присвоение выполнено, оно придерживается.)
Если вы вносите изменения в модуль и вам необходимо его перезагрузить, вам нужно либо перезапустить интерпретатор, либо использовать функцию с именем reload()
из модуля importlib
:
>>> import mod a = [100, 200, 300] >>> import mod >>> import importlib >>> importlib.reload(mod) a = [100, 200, 300] <module 'mod' from 'C:\\Users\\john\\Documents\\Python\\doc\\mod.py'>
Пакеты Python
Предположим, вы разработали очень большое приложение, которое включает в себя множество модулей. По мере роста количества модулей становится сложно отслеживать их все, если они выбрасываются в одно место. Это особенно верно, если они имеют похожие имена или функциональность. Вы можете пожелать средства группировки и организации их.
Пакеты позволяют иерархически структурировать пространство имен модуля с использованием точечной нотации. Точно так же, как модули помогают избежать коллизий между именами глобальных переменных, пакеты помогают избежать коллизий между именами модулей.
Создать пакет довольно просто, поскольку он использует внутреннюю иерархическую структуру файлов операционной системы. Рассмотрим следующую договоренность:
Здесь есть каталог с именем pkg
, который содержит два модуля: mod1.py
и mod2.py
. Содержимое модулей:
mod1.py
def foo(): print('[mod1] foo()') class Foo: pass
mod2.py
def bar(): print('[mod2] bar()') class Bar: pass
Учитывая эту структуру, если каталог pkg
находится в месте, где его можно найти (в одном из каталогов, содержащихся в sys.path
), вы можете обратиться к двум модули с точечной нотацией (pkg.mod1
, pkg.mod2
) и импортируйте их с уже используемым pyfrjvsv синтаксисом:
import <module_name>[, <module_name> ...]
>>> import pkg.mod1, pkg.mod2 >>> pkg.mod1.foo() [mod1] foo() >>> x = pkg.mod2.Bar() >>> x <pkg.mod2.Bar object at 0x033F7290>
from <module_name> import <name(s)>
>>> from pkg.mod1 import foo >>> foo() [mod1] foo()
from <module_name> import <name> as <alt_name>
>>> from pkg.mod2 import Bar as Qux >>> x = Qux() >>> x <pkg.mod2.Bar object at 0x036DFFD0>
You can import modules with these statements as well:
from <package_name> import <modules_name>[, <module_name> ...] from <package_name> import <module_name> as <alt_name>
>>> from pkg import mod1 >>> mod1.foo() [mod1] foo() >>> from pkg import mod2 as quux >>> quux.bar() [mod2] bar()
You can technically import the package as well:
>>> import pkg >>> pkg <module 'pkg' (namespace)>
But this is of little avail. Though this is, strictly speaking, a syntactically correct Python statement, it doesn’t do much of anything useful. In particular, it does not place any of the modules in pkg
into the local namespace:
>>> pkg.mod1 Traceback (most recent call last): File "<pyshell#34>", line 1, in <module> pkg.mod1 AttributeError: module 'pkg' has no attribute 'mod1' >>> pkg.mod1.foo() Traceback (most recent call last): File "<pyshell#35>", line 1, in <module> pkg.mod1.foo() AttributeError: module 'pkg' has no attribute 'mod1' >>> pkg.mod2.Bar() Traceback (most recent call last): File "<pyshell#36>", line 1, in <module> pkg.mod2.Bar() AttributeError: module 'pkg' has no attribute 'mod2'
To actually import the modules or their contents, you need to use one of the forms shown above.
Инициализация пакета
If a file named __init__.py
is present in a package directory, it is invoked when the package or a module in the package is imported. This can be used for execution of Инициализация пакета code, such as initialization of package-level data.
For example, consider the following __init__.py
file:
__init__.py
print(f'Invoking __init__.py for {__name__}') A = ['quux', 'corge', 'grault']
Let’s add this file to the pkg
directory from the above example:
Now when the package is imported, the global list A
is initialized:
>>> import pkg Invoking __init__.py for pkg >>> pkg.A ['quux', 'corge', 'grault']
A module in the package can access the global variable by importing it in turn:
mod1.py
def foo(): from pkg import A print('[mod1] foo() / A = ', A) class Foo: pass
>>> from pkg import mod1 Invoking __init__.py for pkg >>> mod1.foo() [mod1] foo() / A = ['quux', 'corge', 'grault']
__init__.py
can also be used to effect automatic importing of modules from a package. For example, earlier you saw that the statement import pkg
only places the name pkg
in the caller’s local symbol table and doesn’t import any modules. But if __init__.py
in the pkg
directory contains the following:
__init__.py
print(f'Invoking __init__.py for {__name__}') import pkg.mod1, pkg.mod2
then when you execute import pkg
, modules mod1
and mod2
are imported automatically:
>>> import pkg Invoking __init__.py for pkg >>> pkg.mod1.foo() [mod1] foo() >>> pkg.mod2.bar() [mod2] bar()
Примечание:
Большая часть документации Python гласит, что файл
__init__.py
должен присутствовать в каталоге пакета при его создании. Когда-то это было правдой. Раньше было так, что само присутствие__init__.py
означало, что для Python пакет определен. Файл может содержать код инициализации или даже быть пустым, но он должен быть.Начиная с Python 3.3, были представлены неявные пакеты пространства имен. Они позволяют создавать пакеты без какого-либо файла
__init__.py
. Конечно, он может быть, если требуется инициализация пакета. Но теперь это не обязательно.
Импорт *
из пакета
Для дальнейшего обсуждения расширим ранее определенный пакет и теперь он содержать некоторые дополнительные модули:
В каталоге pkg
теперь расположены 4 файла:
mod1.py
def foo(): print('[mod1] foo()') class Foo: pass
mod2.py
def bar(): print('[mod2] bar()') class Bar: pass
mod3.py
def baz(): print('[mod3] baz()') class Baz: pass
mod4.py
def qux(): print('[mod4] qux()') class Qux: pass
(Необычно, не так ли?)
Вы уже видели, что когда для модуля используется import *
, то, как всегда, из модуля в локальную таблицу определений импортируются все объекты, кроме тех объектов, чьи имена начинаются с подчеркивания:
>>> dir() ['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__'] >>> from pkg.mod3 import * >>> dir() ['Baz', '__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'baz'] >>> baz() [mod3] baz() >>> Baz <class 'pkg.mod3.Baz'>
Аналогичное утверждение для пакета таково:
from <package_name> import *
Что это значит?
>>> dir() ['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__'] >>> from pkg import * >>> dir() ['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__']
Hmph. Not much. You might have expected (assuming you had any expectations at all) that Python would dive down into the package directory, find all the modules it could, and import them all. But as you can see, by default that is not what happens.
Instead, Python follows this convention: if the __init__.py
file in the package directory contains a list named __all__
, it is taken to be a list of modules that should be imported when the statement from <package_name> import *
is encountered.
For the present example, suppose you create an __init__.py
in the pkg
directory like this:
pkg/__init__.py
__all__ = [ 'mod1', 'mod2', 'mod3', 'mod4' ]
Now from pkg import *
imports all four modules:
>>> dir() ['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__'] >>> from pkg import * >>> dir() ['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'mod1', 'mod2', 'mod3', 'mod4'] >>> mod2.bar() [mod2] bar() >>> mod4.Qux <class 'pkg.mod4.Qux'>
Using import *
still isn’t considered terrific form, any more for packages than for modules. But this facility at least gives the creator of the package some control over what happens when import *
is specified. (In fact, it provides the capability to disallow it entirely, simply by declining to define __all__
at all. As you have seen, the default behavior for packages is to import nothing.)
By the way, __all__
can be defined in a module as well and serves the same purpose: to control what is imported with import *
. For example, modify mod1.py
as follows:
pkg/mod1.py
__all__ = ['foo'] def foo(): print('[mod1] foo()') class Foo: pass
Now an import *
statement from pkg.mod1
will only import what is contained in __all__
:
>>> dir() ['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__'] >>> from pkg.mod1 import * >>> dir() ['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'foo'] >>> foo() [mod1] foo() >>> Foo Traceback (most recent call last): File "<pyshell#37>", line 1, in <module> Foo NameError: name 'Foo' is not defined
foo()
(the function) is now defined in the local namespace, but Foo
(the class) is not, because the latter is not in __all__
.
In summary, __all__
is used by both packages and modules to control what is imported when import *
is specified. But the default behavior differs:
- For a package, when
__all__
is not defined,import *
does not import anything. - For a module, when
__all__
is not defined,import *
imports everything (except—you guessed it—names starting with an underscore).
Subpackages
Packages can contain nested subpackages to arbitrary depth. For example, let’s make one more modification to the example package directory as follows:
The four modules (mod1.py
, mod2.py
, mod3.py
and mod4.py
) are defined as previously. But now, instead of being lumped together into the pkg
directory, they are split out into two subpackage directories, sub_pkg1
and sub_pkg2
.
Importing still works the same as shown previously. Syntax is similar, but additional dot notation is used to separate package name from subpackage name:
>>> import pkg.sub_pkg1.mod1 >>> pkg.sub_pkg1.mod1.foo() [mod1] foo() >>> from pkg.sub_pkg1 import mod2 >>> mod2.bar() [mod2] bar() >>> from pkg.sub_pkg2.mod3 import baz >>> baz() [mod3] baz() >>> from pkg.sub_pkg2.mod4 import qux as grault >>> grault() [mod4] qux()
In addition, a module in one subpackage can reference objects in a sibling subpackage (in the event that the sibling contains some functionality that you need). For example, suppose you want to import and execute function foo()
(defined in module mod1
) from within module mod3
. You can either use an absolute import:
pkg/sub__pkg2/mod3.py
def baz(): print('[mod3] baz()') class Baz: pass from pkg.sub_pkg1.mod1 import foo foo()
>>> from pkg.sub_pkg2 import mod3 [mod1] foo() >>> mod3.foo() [mod1] foo()
Or you can use a relative import, where ..
refers to the package one level up. From within mod3.py
, which is in subpackage sub_pkg2
,
..
evaluates to the parent package (pkg
), and..sub_pkg1
evaluates to subpackagesub_pkg1
of the parent package.
pkg/sub__pkg2/mod3.py
def baz(): print('[mod3] baz()') class Baz: pass from .. import sub_pkg1 print(sub_pkg1) from ..sub_pkg1.mod1 import foo foo()
>>> from pkg.sub_pkg2 import mod3 <module 'pkg.sub_pkg1' (namespace)> [mod1] foo()
Заключение
В этом уроке вы узнали:
- Как создать модуль Python.
- Места, где интерпретатор Python ищет модуль.
- Как получить доступ к объектам, определенном в модуле, используя оператор
import
. - Как создать модуль, который будет выполняться как отдельный скрипт
- Как организовать модули в пакеты (packages) и подпакеты (subpackages).
- Как управлять инициализацией пакета.
Надеемся, что теперь вы лучше понимаете, как получить доступ к функциям, доступным во многих сторонних и встроенных модулях для Python.
Кроме того, если вы разрабатываете свое собственное приложение, создание собственных модулей и пакетов поможет организовать и модульно оформить код, что упрощает кодирование, обслуживание и отладку.
Если вы хотите узнать больше, обратитесь к следующей документации на Python.org:
Счастливого пайтонинга!
С использованием материалов Python Modules and Packages – An Introduction