Гарантирован ли порядок импорта расширений в именах файлов модулей в Python? - PullRequest
2 голосов
/ 06 мая 2019

Экспериментально, я убедился, что когда скомпилированные extension.pyd (или .so) и обычный extension.py оба существуют в одном каталоге, файл .pyd импортируется первым; .py импортируется, только если файл .pyd не найден:

In [1]: import extension

In [2]: extension.__file__
Out[2]: 'extension.pyd'

In [3]: import glob; glob.glob("extension.py*")
Out[3]: ['extension.py', 'extension.pyd']

Это гарантированно будет одинаковым для всех версий Python, и могу ли я положиться на это, чтобы добавить логику в файл .py, который выполняется только тогда, когда файл .pyd не найден?

1 Ответ

2 голосов
/ 14 мая 2019

FWIW, мне не удалось найти ссылку, утверждающую, что расширения должны быть загружены перед py-файлами, поэтому, вероятно, безопаснее рассматривать это как деталь реализации (если кто-то не предоставит ссылку). Даже если эта информация стабильна для всех версий, по крайней мере, до 2.7.

Когда модуль импортируется, он сначала просматривается в кеше (т. Е. sys.modules), и, если его еще нет, используются средства поиска из sys.meta_path. Обычно sys.meta_path состоит из BuiltinImporter, FrozenImporter и PathFinder, где PathFinder отвечает за поиск модулей на пути к диску / python.

PathFinder предоставляет некоторые функции кэширования для ускорения поиска, но в основном делегирует поиск на перехватчиков с sys.path_hooks - обзор можно найти, например, в PEP 302 .

Обычно sys.path_hooks состоят из zipimporter, которые делают возможным импорт сжатых файлов, и обернутыми FileFinder, что является рабочей лошадкой всего импорт-машина.

FileFinder опробует различные варианты (т. Е. .so, .py, .pyc) в заданном порядке, который устанавливается _get_supported_file_loaders() -методом :

def _get_supported_file_loaders():
    """Returns a list of file-based module loaders.
    Each item is a tuple (loader, suffixes).
    """
    extensions = ExtensionFileLoader, _imp.extension_suffixes()
    source = SourceFileLoader, SOURCE_SUFFIXES
    bytecode = SourcelessFileLoader, BYTECODE_SUFFIXES
    return [extensions, source, bytecode]

Как видно:

  • расширения предшествуют исходным файлам (т.е. py-файлам)
  • исходные файлы предшествуют pyc-файлам

Очевидно, что sys.meta_path, как и sys.path_hooks, можно манипулировать таким образом, чтобы установить произвольный порядок предпочтений загрузки.

Как личное примечание: я бы постарался избежать ситуации, когда py- и так / pyd-файлы находятся рядом друг с другом.

...