Как программист Python, одна из первых вещей, которую вам нужно изучить, — это импортировать модуль или пакет. Но я заметил, что те, кто время от времени использовал Python в течение многих лет, не все знают, что механизм импорта Python на самом деле очень гибкий. В этой статье мы рассмотрим следующие темы:
- регулярный импорт
- Импорт с помощью оператора from
- относительный импорт
- необязательный импорт
- местный импорт
- Рекомендации по импорту
регулярный импорт
Обычный импорт должен быть наиболее часто используемым методом импорта, который, вероятно, выглядит следующим образом:
import sys
вам просто нужно использоватьimport
слово, а затем укажите модуль или пакет, который вы хотите импортировать. Преимущество импорта таким образом заключается в том, что вы можете импортировать сразу несколько пакетов или модулей:
import os, sys, time
Хотя это экономит место, это противоречит руководству по стилю Python.Руководство по стилю Python рекомендует помещать каждый оператор импорта в отдельную строку..
Иногда при импорте модуля вы хотите переименовать модуль. Эту функцию легко реализовать:
import sys as system
print(system.platform)
Приведенный выше код импортирует нашsys
Модуль переименован вsystem
. Мы можем вызывать методы модуля так же, как и раньше, но с новым именем модуля. Есть также некоторые подмодули, которые необходимо импортировать с использованием записи через точку.
import urllib.error
Это необычная ситуация, но знать об этом всегда не помешает.
Импорт с помощью оператора from
Часто вам нужно импортировать только определенную часть модуля или библиотеки. Давайте посмотрим, как это сделать в Python:
from functools import lru_cache
Приведенная выше строка кода позволяет вам напрямую вызыватьlru_cache
. Если вы импортируете обычным способомfunctools
, то вы должны позвонить такlru_cache
:
functools.lru_cache(*args)
В зависимости от вашего фактического сценария использования описанный выше подход может быть лучше. В сложных кодовых базах может быть полезно видеть, откуда была импортирована функция. Однако, если ваш код хорошо поддерживается и является модульным, очень удобно и лаконично импортировать только часть содержимого из модуля.
Конечно, вы также можете импортировать все содержимое модуля, используя метод from, например:
from os import *
Это удобно в некоторых случаях, но также загромождает ваше пространство имен. Проблема в том, что вы могли определить переменную или функцию с тем же именем, что и в импортируемом модуле, тогда, если вы попытаетесь использоватьos
Переменная или функция с тем же именем в модуле фактически будет использовать содержимое, определенное вами. Таким образом, вы можете столкнуться с довольно запутанной логической ошибкой.Единственный модуль в стандартной библиотеке, который я рекомендую полностью импортировать, — это Tkinter..
Если вы пишете свой собственный модуль или пакет, кто-то может предложить вам__init__.py
Импортируйте все содержимое файла, чтобы упростить использование модуля или пакета. Лично я предпочитаю явный импорт неявному.
Вы также можете пойти на компромисс и импортировать несколько элементов из пакета:
from os import path, walk, unlink
from os import uname, remove
В приведенном выше коде мы начинаемos
В модуль импортировано 5 функций. Вы могли заметить, что мы делаем это, импортируя из одного и того же модуля несколько раз. Конечно, вы можете использовать круглые скобки для одновременного импорта нескольких элементов, если хотите:
from os import (path, walk, unlink, uname,
remove, rename)
Это полезный трюк, но вы также можете сделать это по-другому:
from os import path, walk, unlink, uname, \
remove, rename
Обратная косая черта выше — это символ продолжения строки в Python, сообщающий интерпретатору, что эта строка кода продолжается до следующей строки.
относительный импорт
PEP 328Объясняет, почему был введен относительный импорт и какой синтаксис был выбран. В частности, использование точек для определения того, как относительно импортировать другие пакеты или модули. Это делается для того, чтобы избежать случайного импорта конфликтующих модулей из стандартной библиотеки. Здесь мы берем структуру папок, приведенную в PEP 328, в качестве примера, чтобы увидеть, как работает относительный импорт:
my_package/
__init__.py
subpackage1/
__init__.py
module_x.py
module_y.py
subpackage2/
__init__.py
module_z.py
module_a.py
Найдите место на локальном диске для создания вышеуказанных файлов и папок. на вершине__init__.py
файла введите следующий код:
from . import subpackage1
from . import subpackage2
Введите следующийsubpackage1
папку, отредактируйте__init__.py
файла введите следующий код:
from . import module_x
from . import module_y
Изменить сейчасmodule_x.py
файла введите следующий код:
from .module_y import spam as ham
def main():
ham()
последнее редактированиеmodule_y.py
файла введите следующий код:
def spam():
print('spam ' * 3)
Откройте терминал,cd
кmy_package
папка, в которой лежит пакет, но не заходить в нееmy_package
. Запустите интерпретатор Python в этой папке. Я использую IPython, потому что его автозаполнение очень удобно:
In [1]: import my_package
In [2]: my_package.subpackage1.module_x
Out[2]: <module 'my_package.subpackage1.module_x' from 'my_package/subpackage1/module_x.py'>
In [3]: my_package.subpackage1.module_x.main()
spam spam spam
Относительный импорт предназначен для кода, который вы в конечном итоге помещаете в пакет. Это способ импорта, если вы пишете много связанного кода.Вы обнаружите, что многие популярные пакеты в PyPI также используют относительный импорт.. Также обратите внимание, что если вы хотите импортировать несколько уровней файлов, просто используйте несколько точек. но,PEP 328 рекомендует не более двух уровней относительного импорта..
Также обратите внимание, что если вы идете вmodule_x.py
файл добавленif __name__ == ‘__main__’
, а затем попробуйте запустить файл, вы получите непонятную ошибку. Отредактируйте файл и попробуйте!
from . module_y import spam as ham
def main():
ham()
if __name__ == '__main__':
# This won't work!
main()
Теперь войдите из терминалаsubpackage1
папку, выполните следующую команду:
python module_x.py
Если вы используете Python 2, вы должны увидеть следующее сообщение об ошибке:
Traceback (most recent call last):
File "module_x.py", line 1, in <module>
from . module_y import spam as ham
ValueError: Attempted relative import in non-package
Если вы используете Python 3, сообщение об ошибке будет выглядеть так:
Traceback (most recent call last):
File "module_x.py", line 1, in <module>
from . module_y import spam as ham
SystemError: Parent module '' not loaded, cannot perform relative import
Это означает,module_x.py
является модулем в пакете, и вы пытаетесь выполнить его в режиме сценария, ноЭтот режим не поддерживает относительный импорт.
Если вы хотите использовать этот модуль в своем собственном коде, вы должны добавить его в путь поиска импорта Python. Самый простой способ сделать это заключается в следующем:
import sys
sys.path.append('/path/to/folder/containing/my_package')
import my_package
Обратите внимание, что вам нужно добавитьmy_package
путь к папке вышеmy_package
сам. Причина в том, чтоmy_package
— это пакет, который мы хотим использовать, поэтому, если вы добавите к нему путь, пакет будет недоступен.
Далее поговорим о дополнительном импорте.
Необязательный импорт
Если вы хотите сначала использовать определенный модуль или пакет, но также хотите иметь альтернативу при отсутствии этого модуля или пакета, вы можете использовать необязательный метод импорта. Это может импортировать поддержку нескольких версий программного обеспечения или повысить производительность. отпакет github2Пример кода в:
try:
# For Python 3
from http.client import responses
except ImportError: # For Python 2.5-2.7
try:
from httplib import responses # NOQA
except ImportError: # For Python 2.4
from BaseHTTPServer import BaseHTTPRequestHandler as _BHRH
responses = dict([(k, v[0]) for k, v in _BHRH.responses.items()])
lxml
Пакеты также используют необязательный импорт:
try:
from urlparse import urljoin
from urllib2 import urlopen
except ImportError:
# Python 3
from urllib.parse import urljoin
from urllib.request import urlopen
Как показывает приведенный выше пример,Использование необязательных импортов является обычным явлением, и это навык, который стоит освоить..
местный импорт
Когда вы импортируете модуль в локальную область, вы выполняете локальный импорт. Если вы импортируете модуль в начало файла скрипта Python, вы импортируете модуль в глобальную область, а это означает, что любые последующие функции или методы могут получить доступ к модулю. Например:
import sys # global scope
def square_root(a):
# This import is into the square_root functions local scope
import math
return math.sqrt(a)
def my_pow(base_num, power):
return math.pow(base_num, power)
if __name__ == '__main__':
print(square_root(49))
print(my_pow(2, 3))
Здесь мы будемsys
Модуль импортируется в глобальную область видимости, но мы не используем этот модуль. Затем вsquare_root
функция, мы будемmath
модуль импортируется в локальную область действия функции, что означаетmath
модули могут быть толькоsquare_root
Используется внутри функции. если мы попытаемсяmy_pow
используется в функцииmath
, вызоветNameError
. Попробуйте запустить этот скрипт и посмотрите, что произойдет.
Одним из преимуществ использования локальной области видимости является то, что импорт используемых вами модулей может занять много времени, и в этом случае может быть более целесообразно поместить их в какую-нибудь редко вызываемую функцию, а не напрямую в глобальную область импорта. Честно говоря, я почти никогда не использую частичный импорт, в основном потому, что если внутри модуля есть операторы импорта, может быть трудно сказать, почему и что делать.По соглашению все операторы импорта должны быть в верхней части модуля..
Рекомендации по импорту
Есть несколько распространенных ошибок, которые допускают программисты, когда дело доходит до импорта модулей. Здесь мы вводим два.
- круговой импорт
- Переопределить импорт (Затененный импорт, временно переведенный на переопределение импорта)
Давайте сначала рассмотрим круговой импорт.
круговой импорт
Если вы создаете два модуля, которые импортируют друг друга, происходит циклический импорт. Например:
# a.py
import b
def a_test():
print("in a_test")
b.b_test()
a_test()
Затем создайте еще один модуль в той же папке, назовите егоb.py
.
import a
def b_test():
print('In test_b"')
a.a_test()
b_test()
Если вы запустите любой модуль, он выдастAttributeError
. Это потому, что оба модуля пытаются импортировать друг друга. Проще говоря, модульa
хотите импортировать модульb
, а поскольку модульb
Также пытаюсь импортировать модульa
(он выполняется в это время), модульa
не завершает модульb
Импортировать. Я видел несколько хаков для решения этой проблемы, ноВ общем, вам следует реорганизовать свой код, чтобы этого не произошло..
Переопределить импорт
Когда вы создаете модуль с тем же именем, что и у модуля в стандартной библиотеке, происходит переопределение импорта, если вы импортируете этот модуль. Например, создайтеmath.py
файл и пишем в нем следующий код:
import math
def square_root(number):
return math.sqrt(number)
square_root(72)
Теперь откройте терминал и попробуйте запустить файл, вы получите следующую трассировку:
Traceback (most recent call last):
File "math.py", line 1, in <module>
import math
File "/Users/michael/Desktop/math.py", line 6, in <module>
square_root(72)
File "/Users/michael/Desktop/math.py", line 4, in square_root
return math.sqrt(number)
AttributeError: module 'math' has no attribute 'sqrt'
Что именно происходит? На самом деле, когда вы запускаете этот файл, интерпретатор Python сначала ищет файл с именем в папке, где находится запущенный в данный момент скрипт.math
модуль. В этом примере интерпретатор нашел исполняемый нами модуль и попытался его импортировать. Но наш модуль не вызываетsqrt
функция или свойство, поэтому она выдаетAttributeError
.
Суммировать
В этой статье мы многое рассказали об импорте, но кое-что еще не рассмотрели.PEP 302Введены в хуки импорта (import hooks), поддержка реализации некоторых очень крутых фич, таких как импорт напрямую из github. Он также есть в стандартной библиотеке Python.importlibмодуль, его стоит посмотреть и изучить. Конечно, вы также можете посмотреть на код, написанный другими, и продолжить поиск более полезных трюков.