6. Модулі

Якщо ви вийшли з інтерпретатора Python і ввели його знову, зроблені вами визначення (функції та змінні) буде втрачено. Тому, якщо ви хочете написати дещо довшу програму, вам краще використовувати текстовий редактор, щоб підготувати вхідні дані для інтерпретатора, а замість цього запустити її з цим файлом як вхідними. Це відоме як створення сценарію. Коли ваша програма стає довшою, ви можете розділити її на кілька файлів для полегшення обслуговування. Ви також можете використати зручну функцію, яку ви написали в кількох програмах, не копіюючи її визначення в кожну програму.

Для підтримки цього Python має спосіб помістити визначення у файл і використовувати їх у сценарії або в інтерактивному екземплярі інтерпретатора. Такий файл називається модулем; визначення з модуля можна імпортувати в інші модулі або в головний модуль (набір змінних, до яких ви маєте доступ у сценарії, що виконується на верхньому рівні та в режимі калькулятора).

Модуль — це файл, що містить визначення та оператори Python. Ім’я файлу — це ім’я модуля з суфіксом .py. У межах модуля ім’я модуля (у вигляді рядка) доступне як значення глобальної змінної __name__. Наприклад, скористайтеся своїм улюбленим текстовим редактором, щоб створити файл під назвою fibo.py у поточному каталозі з таким вмістом:

# Fibonacci numbers module

def fib(n):    # write Fibonacci series up to n
    a, b = 0, 1
    while a < n:
        print(a, end=' ')
        a, b = b, a+b
    print()

def fib2(n):   # return Fibonacci series up to n
    result = []
    a, b = 0, 1
    while a < n:
        result.append(a)
        a, b = b, a+b
    return result

Тепер увійдіть до інтерпретатора Python та імпортуйте цей модуль за допомогою наступної команди:

>>> import fibo

This does not enter the names of the functions defined in fibo directly in the current symbol table; it only enters the module name fibo there. Using the module name you can access the functions:

>>> fibo.fib(1000)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
>>> fibo.fib2(100)
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
>>> fibo.__name__
'fibo'

Якщо ви маєте намір часто використовувати функцію, ви можете призначити їй локальну назву:

>>> fib = fibo.fib
>>> fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377

6.1. Детальніше про модулі

Модуль може містити виконувані оператори, а також визначення функцій. Ці оператори призначені для ініціалізації модуля. Вони виконуються лише вперше, коли ім’я модуля зустрічається в операторі імпорту. 1 (Вони також запускаються, якщо файл виконується як сценарій.)

Each module has its own private symbol table, which is used as the global symbol table by all functions defined in the module. Thus, the author of a module can use global variables in the module without worrying about accidental clashes with a user’s global variables. On the other hand, if you know what you are doing you can touch a module’s global variables with the same notation used to refer to its functions, modname.itemname.

Modules can import other modules. It is customary but not required to place all import statements at the beginning of a module (or script, for that matter). The imported module names are placed in the importing module’s global symbol table.

There is a variant of the import statement that imports names from a module directly into the importing module’s symbol table. For example:

>>> from fibo import fib, fib2
>>> fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377

This does not introduce the module name from which the imports are taken in the local symbol table (so in the example, fibo is not defined).

Існує навіть варіант імпорту всіх імен, які визначає модуль:

>>> from fibo import *
>>> fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377

Це імпортує всі імена, крім тих, що починаються з підкреслення (_). У більшості випадків програмісти Python не використовують цю можливість, оскільки вона вводить невідомий набір імен в інтерпретатор, можливо, приховуючи деякі речі, які ви вже визначили.

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

Якщо ім’я модуля супроводжується as, тоді ім’я після as прив’язується безпосередньо до імпортованого модуля.

>>> import fibo as fib
>>> fib.fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377

Це фактично імпортує модуль таким же чином, як і import fibo, з тією лише різницею, що він доступний як fib.

Його також можна використовувати при використанні from з подібними ефектами:

>>> from fibo import fib as fibonacci
>>> fibonacci(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377

Примітка

З міркувань ефективності кожен модуль імпортується лише один раз за сеанс інтерпретатора. Таким чином, якщо ви змінюєте свої модулі, ви повинні перезапустити інтерпретатор – або, якщо це лише один модуль, який ви хочете протестувати в інтерактивному режимі, використайте importlib.reload(), напр. імпорт importlib; importlib.reload(modulename).

6.1.1. Виконання модулів у вигляді скриптів

Коли ви запускаєте модуль Python з

python fibo.py <arguments>

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

if __name__ == "__main__":
    import sys
    fib(int(sys.argv[1]))

ви можете зробити файл придатним для використання як сценарій, а також як імпортований модуль, оскільки код, який аналізує командний рядок, запускається, лише якщо модуль виконується як «основний» файл:

$ python fibo.py 50
0 1 1 2 3 5 8 13 21 34

Якщо модуль імпортовано, код не виконується:

>>> import fibo
>>>

Це часто використовується або для забезпечення зручного інтерфейсу користувача для модуля, або для цілей тестування (запуск модуля як сценарію виконує набір тестів).

6.1.2. Шлях пошуку модуля

When a module named spam is imported, the interpreter first searches for a built-in module with that name. These module names are listed in sys.builtin_module_names. If not found, it then searches for a file named spam.py in a list of directories given by the variable sys.path. sys.path is initialized from these locations:

  • Каталог, що містить вхідний сценарій (або поточний каталог, якщо файл не вказано).

  • PYTHONPATH (список імен каталогів із тим самим синтаксисом, що й змінна оболонки PATH).

  • Типове значення, що залежить від встановлення (за домовленістю включає каталог site-packages, який обробляється модулем site).

Примітка

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

Після ініціалізації програми Python можуть змінювати sys.path. Каталог, що містить запущений скрипт, розміщується на початку шляху пошуку, перед стандартним шляхом до бібліотеки. Це означає, що скрипти в цьому каталозі будуть завантажені замість однойменних модулів у каталозі бібліотеки. Це помилка, якщо заміна не призначена. Перегляньте розділ Стандартні модулі для отримання додаткової інформації.

6.1.3. «Компільовані» файли Python

Щоб прискорити завантаження модулів, Python кешує скомпільовану версію кожного модуля в каталозі __pycache__ під назвою module.version.pyc, де версія кодує формат скомпільованого файлу; зазвичай містить номер версії Python. Наприклад, у випуску CPython 3.3 скомпільована версія spam.py буде кешована як __pycache__/spam.cpython-33.pyc. Ця угода про іменування дозволяє співіснувати скомпільованим модулям з різних випусків і різних версій Python.

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

Python не перевіряє кеш у двох випадках. По-перше, він завжди перекомпілює і не зберігає результат для модуля, який завантажується безпосередньо з командного рядка. По-друге, він не перевіряє кеш, якщо немає вихідного модуля. Щоб підтримувати дистрибутив без вихідного коду (лише скомпільований), скомпільований модуль має бути у вихідному каталозі, а вихідного модуля не повинно бути.

Деякі поради експертам:

  • Ви можете використовувати перемикачі -O або -OO у команді Python, щоб зменшити розмір скомпільованого модуля. Перемикач -O видаляє оператори assert, перемикач -OO видаляє як оператори assert, так і рядки __doc__. Оскільки деякі програми можуть покладатися на їх наявність, вам слід використовувати цей параметр, лише якщо ви знаєте, що робите. «Оптимізовані» модулі мають тег opt- і зазвичай менші. Майбутні випуски можуть змінити результати оптимізації.

  • Програма не працює швидше, коли вона читається з файлу .pyc, ніж коли вона зчитується з файлу .py; єдине, що є швидшим у файлах .pyc, це швидкість, з якою вони завантажуються.

  • Модуль compileall може створювати файли .pyc для всіх модулів у каталозі.

  • Більш детальну інформацію про цей процес, включаючи блок-схему рішень, можна знайти в PEP 3147.

6.2. Стандартні модулі

Python постачається з бібліотекою стандартних модулів, описаних в окремому документі, Довідник бібліотеки Python (далі «Довідник бібліотеки»). Деякі модулі вбудовані в інтерпретатор; вони надають доступ до операцій, які не є частиною ядра мови, але, тим не менш, вбудовані, або для ефективності, або для забезпечення доступу до примітивів операційної системи, таких як системні виклики. Набір таких модулів є опцією конфігурації, яка також залежить від базової платформи. Наприклад, модуль winreg доступний лише в системах Windows. Один окремий модуль заслуговує на увагу: sys, який вбудовано в кожен інтерпретатор Python. Змінні sys.ps1 і sys.ps2 визначають рядки, які використовуються як первинні та додаткові підказки:

>>> import sys
>>> sys.ps1
'>>> '
>>> sys.ps2
'... '
>>> sys.ps1 = 'C> '
C> print('Yuck!')
Yuck!
C>

Ці дві змінні визначені, лише якщо інтерпретатор перебуває в інтерактивному режимі.

Змінна sys.path - це список рядків, який визначає шлях пошуку модулів інтерпретатором. Він ініціалізується шляхом за замовчуванням, взятим із змінної середовища PYTHONPATH, або з вбудованого за замовчуванням, якщо PYTHONPATH не встановлено. Ви можете змінити його за допомогою стандартних операцій зі списком:

>>> import sys
>>> sys.path.append('/ufs/guido/lib/python')

6.3. Функція dir()

Вбудована функція dir() використовується, щоб дізнатися, які імена визначає модуль. Він повертає відсортований список рядків:

>>> import fibo, sys
>>> dir(fibo)
['__name__', 'fib', 'fib2']
>>> dir(sys)  
['__breakpointhook__', '__displayhook__', '__doc__', '__excepthook__',
 '__interactivehook__', '__loader__', '__name__', '__package__', '__spec__',
 '__stderr__', '__stdin__', '__stdout__', '__unraisablehook__',
 '_clear_type_cache', '_current_frames', '_debugmallocstats', '_framework',
 '_getframe', '_git', '_home', '_xoptions', 'abiflags', 'addaudithook',
 'api_version', 'argv', 'audit', 'base_exec_prefix', 'base_prefix',
 'breakpointhook', 'builtin_module_names', 'byteorder', 'call_tracing',
 'callstats', 'copyright', 'displayhook', 'dont_write_bytecode', 'exc_info',
 'excepthook', 'exec_prefix', 'executable', 'exit', 'flags', 'float_info',
 'float_repr_style', 'get_asyncgen_hooks', 'get_coroutine_origin_tracking_depth',
 'getallocatedblocks', 'getdefaultencoding', 'getdlopenflags',
 'getfilesystemencodeerrors', 'getfilesystemencoding', 'getprofile',
 'getrecursionlimit', 'getrefcount', 'getsizeof', 'getswitchinterval',
 'gettrace', 'hash_info', 'hexversion', 'implementation', 'int_info',
 'intern', 'is_finalizing', 'last_traceback', 'last_type', 'last_value',
 'maxsize', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks',
 'path_importer_cache', 'platform', 'prefix', 'ps1', 'ps2', 'pycache_prefix',
 'set_asyncgen_hooks', 'set_coroutine_origin_tracking_depth', 'setdlopenflags',
 'setprofile', 'setrecursionlimit', 'setswitchinterval', 'settrace', 'stderr',
 'stdin', 'stdout', 'thread_info', 'unraisablehook', 'version', 'version_info',
 'warnoptions']

Без аргументів dir() перераховує імена, які ви визначили на даний момент:

>>> a = [1, 2, 3, 4, 5]
>>> import fibo
>>> fib = fibo.fib
>>> dir()
['__builtins__', '__name__', 'a', 'fib', 'fibo', 'sys']

Зверніть увагу, що в ньому перераховано всі типи імен: змінні, модулі, функції тощо.

dir() не містить списку вбудованих функцій і змінних. Якщо вам потрібен їх список, вони визначені в стандартному модулі builtins:

>>> import builtins
>>> dir(builtins)  
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException',
 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning',
 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError',
 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning',
 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False',
 'FileExistsError', 'FileNotFoundError', 'FloatingPointError',
 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError',
 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError',
 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError',
 'MemoryError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented',
 'NotImplementedError', 'OSError', 'OverflowError',
 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError',
 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning',
 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError',
 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError',
 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError',
 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning',
 'ValueError', 'Warning', 'ZeroDivisionError', '_', '__build_class__',
 '__debug__', '__doc__', '__import__', '__name__', '__package__', 'abs',
 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable',
 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits',
 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit',
 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr',
 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass',
 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview',
 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property',
 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice',
 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars',
 'zip']

6.4. пакети

Packages are a way of structuring Python’s module namespace by using «dotted module names». For example, the module name A.B designates a submodule named B in a package named A. Just like the use of modules saves the authors of different modules from having to worry about each other’s global variable names, the use of dotted module names saves the authors of multi-module packages like NumPy or Pillow from having to worry about each other’s module names.

Припустімо, ви хочете розробити набір модулів («пакет») для однакової обробки звукових файлів і звукових даних. Існує багато різних форматів звукових файлів (зазвичай розпізнаються за їх розширенням, наприклад: .wav, .aiff, .au), тому вам може знадобитися створювати та підтримувати зростаюча колекція модулів для перетворення між різними форматами файлів. Існує також багато різних операцій, які ви можете виконати зі звуковими даними (наприклад, мікшування, додавання відлуння, застосування функції еквалайзера, створення штучного стереоефекту), тож на додаток ви будете писати нескінченний потік модулів для виконання ці операції. Ось можлива структура для вашого пакета (виражена в термінах ієрархічної файлової системи):

sound/                          Top-level package
      __init__.py               Initialize the sound package
      formats/                  Subpackage for file format conversions
              __init__.py
              wavread.py
              wavwrite.py
              aiffread.py
              aiffwrite.py
              auread.py
              auwrite.py
              ...
      effects/                  Subpackage for sound effects
              __init__.py
              echo.py
              surround.py
              reverse.py
              ...
      filters/                  Subpackage for filters
              __init__.py
              equalizer.py
              vocoder.py
              karaoke.py
              ...

Під час імпортування пакета Python шукає підкаталог пакета в каталогах sys.path.

The __init__.py files are required to make Python treat directories containing the file as packages. This prevents directories with a common name, such as string, unintentionally hiding valid modules that occur later on the module search path. In the simplest case, __init__.py can just be an empty file, but it can also execute initialization code for the package or set the __all__ variable, described later.

Користувачі пакету можуть імпортувати окремі модулі з пакета, наприклад:

import sound.effects.echo

This loads the submodule sound.effects.echo. It must be referenced with its full name.

sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)

Альтернативний спосіб імпорту субмодуля:

from sound.effects import echo

This also loads the submodule echo, and makes it available without its package prefix, so it can be used as follows:

echo.echofilter(input, output, delay=0.7, atten=4)

Ще одним варіантом є імпорт потрібної функції або змінної безпосередньо:

from sound.effects.echo import echofilter

Again, this loads the submodule echo, but this makes its function echofilter() directly available:

echofilter(input, output, delay=0.7, atten=4)

Зауважте, що при використанні з елемента імпорту пакета, елемент може бути або підмодулем (або підпакетом) пакета, або іншим ім’ям, визначеним у пакеті, як-от функція, клас або змінна. Оператор import спочатку перевіряє, чи визначено елемент у пакеті; якщо ні, він припускає, що це модуль, і намагається його завантажити. Якщо не вдається знайти його, виникає виняток ImportError.

Навпаки, при використанні такого синтаксису, як import item.subitem.subsubitem, кожен елемент, крім останнього, має бути пакетом; останній елемент може бути модулем або пакетом, але не може бути класом, функцією чи змінною, визначеною в попередньому елементі.

6.4.1. Імпортування * з пакету

Тепер що відбувається, коли користувач пише from sound.effects import *? В ідеалі можна було б сподіватися, що це якимось чином переходить до файлової системи, знаходить, які підмодулі присутні в пакунку, і імпортує їх усі. Це може зайняти багато часу, а імпортування субмодулів може мати небажані побічні ефекти, які мають статися лише тоді, коли субмодуль імпортовано явно.

Єдине рішення полягає в тому, щоб автор пакета надав явний індекс пакета. Інструкція import використовує таку умову: якщо код __init__.py пакета визначає список під назвою __all__, він вважається списком імен модулів, які слід імпортувати, коли Зустрічається from package import *. Автор пакета повинен підтримувати цей список актуальним, коли виходить нова версія пакета. Автори пакетів також можуть вирішити не підтримувати його, якщо вони не бачать користі для імпортування * зі свого пакета. Наприклад, файл sound/effects/__init__.py може містити такий код:

__all__ = ["echo", "surround", "reverse"]

This would mean that from sound.effects import * would import the three named submodules of the sound.effects package.

If __all__ is not defined, the statement from sound.effects import * does not import all submodules from the package sound.effects into the current namespace; it only ensures that the package sound.effects has been imported (possibly running any initialization code in __init__.py) and then imports whatever names are defined in the package. This includes any names defined (and submodules explicitly loaded) by __init__.py. It also includes any submodules of the package that were explicitly loaded by previous import statements. Consider this code:

import sound.effects.echo
import sound.effects.surround
from sound.effects import *

In this example, the echo and surround modules are imported in the current namespace because they are defined in the sound.effects package when the from...import statement is executed. (This also works when __all__ is defined.)

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

Пам’ятайте, що немає нічого поганого у використанні from package import specific_submodule! Фактично, це рекомендована нотація, якщо тільки імпортуючому модулю не потрібно використовувати підмодулі з однаковими назвами з різних пакунків.

6.4.2. Внутрішньопакетні посилання

When packages are structured into subpackages (as with the sound package in the example), you can use absolute imports to refer to submodules of siblings packages. For example, if the module sound.filters.vocoder needs to use the echo module in the sound.effects package, it can use from sound.effects import echo.

You can also write relative imports, with the from module import name form of import statement. These imports use leading dots to indicate the current and parent packages involved in the relative import. From the surround module for example, you might use:

from . import echo
from .. import formats
from ..filters import equalizer

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

6.4.3. Пакунки в кількох каталогах

Packages support one more special attribute, __path__. This is initialized to be a list containing the name of the directory holding the package’s __init__.py before the code in that file is executed. This variable can be modified; doing so affects future searches for modules and subpackages contained in the package.

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

Виноски

1

In fact function definitions are also „statements“ that are „executed“; the execution of a module-level function definition enters the function name in the module’s global symbol table.