Сводка по импорту модуля

Python

Как программист 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модуль, его стоит посмотреть и изучить. Конечно, вы также можете посмотреть на код, написанный другими, и продолжить поиск более полезных трюков.