Автор оригинала: Doug Hellmann.
Цель:
Импортируйте модули Python, сохраненные как члены ZIP-архивов.
Модуль zipimport
реализует класс zipimporter
, который можно использовать для поиска и загрузки модулей Python внутри ZIP-архивов. zipimporter
поддерживает API перехватчиков импорта, указанный в PEP 302; вот как работают Python Eggs.
Обычно нет необходимости использовать модуль zipimport
напрямую, так как можно импортировать непосредственно из ZIP-архива, если этот архив находится в sys.path
. Однако поучительно изучить, как можно использовать API импортера, изучить доступные функции и понять, как работает импорт модулей. Знание того, как работает импортер ZIP, также поможет отладить проблемы, которые могут возникнуть при распространении приложений в виде ZIP-архивов, созданных с помощью zipfile.PyZipFile
.
Пример
В этих примерах повторно используется часть кода из обсуждения zipfile для создания примера ZIP-архива, содержащего несколько модулей Python.
zipimport_make_example.py
import sys import zipfile if __name__ '__main__': zf zipfile.PyZipFile('zipimport_example.zip', mode'w') try: zf.writepy('.') zf.write('zipimport_get_source.py') zf.write('example_package/README.txt') finally: zf.close() for name in zf.namelist(): print(name)
Запустите zipimport_make_example.py
перед любым из остальных примеров, чтобы создать ZIP-архив, содержащий все модули в каталоге примеров, а также некоторые тестовые данные, необходимые для примеров в этом разделе.
$ python3 zipimport_make_example.py __init__.pyc example_package/__init__.pyc zipimport_find_module.pyc zipimport_get_code.pyc zipimport_get_data.pyc zipimport_get_data_nozip.pyc zipimport_get_data_zip.pyc zipimport_get_source.pyc zipimport_is_package.pyc zipimport_load_module.pyc zipimport_make_example.pyc zipimport_get_source.py example_package/README.txt
Поиск модуля
Учитывая полное имя модуля, find_module ()
попытается найти этот модуль внутри ZIP-архива.
zipimport_find_module.py
import zipimport importer zipimport.zipimporter('zipimport_example.zip') for module_name in ['zipimport_find_module', 'not_there']: print(module_name, ':', importer.find_module(module_name))
Если модуль найден, возвращается экземпляр zipimporter
. В противном случае возвращается None
.
$ python3 zipimport_find_module.py zipimport_find_module :not_there : None
Код доступа
Метод get_code ()
загружает объект кода для модуля из архива.
zipimport_get_code.py
import zipimport importer zipimport.zipimporter('zipimport_example.zip') code importer.get_code('zipimport_get_code') print(code)
Объект кода не такой же, как объект module
, но используется для его создания.
$ python3 zipimport_get_code.py
at 0x1012b4ae0, file
"./zipimport_get_code.py", line 6>
Чтобы загрузить код как используемый модуль, используйте вместо него load_module ()
.
zipimport_load_module.py
import zipimport importer zipimport.zipimporter('zipimport_example.zip') module importer.load_module('zipimport_get_code') print('Name :', module.__name__) print('Loader :', module.__loader__) print('Code :', module.code)
Результатом является объект модуля, настроенный так, как если бы код был загружен из обычного импорта.
$ python3 zipimport_load_module.py
at 0x1007b4c00, file
"./zipimport_get_code.py", line 6>
Name : zipimport_get_code
Loader :
Code : at 0x1007b4c00, file
"./zipimport_get_code.py", line 6>
Источник
Как и в случае с модулем inspect, можно получить исходный код модуля из архива ZIP, если архив включает исходный код. В случае примера только zipimport_get_source.py
добавляется в zipimport_example.zip
(остальные модули просто добавляются как .pyc
файлы).
zipimport_get_source.py
import zipimport modules [ 'zipimport_get_code', 'zipimport_get_source', ] importer zipimport.zipimporter('zipimport_example.zip') for module_name in modules: source importer.get_source(module_name) print( * 80) print(module_name) print( * 80) print(source) print()
Если источник для модуля недоступен, get_source ()
возвращает None
.
$ python3 zipimport_get_source.py zipimport_get_code None zipimport_get_source #!/usr/bin/env python3 # # Copyright 2007 Doug Hellmann. # """Retrieving the source code for a module within a zip archive. """ #end_pymotw_header import zipimport modules = [ 'zipimport_get_code', 'zipimport_get_source', ] importer = zipimport.zipimporter('zipimport_example.zip') for module_name in modules: source = importer.get_source(module_name) print(module_name) print(source) print()
Пакеты
Чтобы определить, относится ли имя к пакету, а не к обычному модулю, используйте is_package ()
.
zipimport_is_package.py
import zipimport importer zipimport.zipimporter('zipimport_example.zip') for name in ['zipimport_is_package', 'example_package']: print(name, importer.is_package(name))
В этом случае zipimport_is_package
был получен из модуля, а example_package
– это пакет.
$ python3 zipimport_is_package.py zipimport_is_package False example_package True
Данные
Бывают случаи, когда исходные модули или пакеты необходимо распространять с данными, не содержащими кода. Образы, файлы конфигурации, данные по умолчанию и тестовые инструменты – лишь несколько примеров этого. Часто атрибуты модуля __path__
или __file__
используются для поиска этих файлов данных относительно места установки кода.
Например, для «обычного» модуля путь к файловой системе может быть построен из атрибута __file__
импортированного пакета следующим образом:
zipimport_get_data_nozip.py
import os import example_package # Find the directory containing the imported # package and build the data filename from it. pkg_dir os.path.dirname(example_package.__file__) data_filename os.path.join(pkg_dir, 'README.txt') # Read the file and show its contents. print(data_filename, ':') print(open(data_filename, 'r').read())
Результат будет зависеть от того, где находится образец кода в файловой системе.
$ python3 zipimport_get_data_nozip.py .../example_package/README.txt : This file represents sample data which could be embedded in the ZIP archive. You could include a configuration file, images, or any other sort of noncode data.
Если example_package
импортируется из ZIP-архива, а не из файловой системы, использование __file__
не работает.
zipimport_get_data_zip.py
import sys sys.path.insert(0, 'zipimport_example.zip') import os import example_package print(example_package.__file__) data_filename os.path.join( os.path.dirname(example_package.__file__), 'README.txt', ) print(data_filename, ':') print(open(data_filename, 'rt').read())
__file__
пакета относится к ZIP-архиву, а не к каталогу, поэтому создание пути к файлу README.txt
дает неверное значение.
$ python3 zipimport_get_data_zip.py zipimport_example.zip/example_package/__init__.pyc zipimport_example.zip/example_package/README.txt : Traceback (most recent call last): File "zipimport_get_data_zip.py", line 20, inprint(open(data_filename, 'rt').read()) NotADirectoryError: [Errno 20] Not a directory: 'zipimport_example.zip/example_package/README.txt'
Более надежный способ получить файл – использовать метод get_data ()
. К экземпляру zipimporter
, который загрузил модуль, можно получить доступ через атрибут __loader__
импортированного модуля:
zipimport_get_data.py
import sys sys.path.insert(0, 'zipimport_example.zip') import os import example_package print(example_package.__file__) data example_package.__loader__.get_data( 'example_package/README.txt') print(data.decode('utf-8'))
pkgutil.get_data ()
использует этот интерфейс для доступа к данным из пакета. Возвращаемое значение представляет собой байтовую строку, которую необходимо декодировать в строку Unicode перед печатью.
$ python3 zipimport_get_data.py zipimport_example.zip/example_package/__init__.pyc This file represents sample data which could be embedded in the ZIP archive. You could include a configuration file, images, or any other sort of noncode data.
__loader__
не установлен для модулей, не импортированных через zipimport
.
Смотрите также
- стандартная библиотека документации для zipimport
- Заметки о переносе Python 2 на 3 для zipimport
imp
– другие функции, связанные с импортом.- pkgutil – предоставляет более общий интерфейс для
get_data ()
. - zipfile – чтение и запись файлов ZIP-архива.
- PEP 302 – новые хуки импорта