Нужно ли повторно импортировать внешние модули, если они уже были импортированы в другой импортированный модуль?

#python #import #module

Вопрос:

Допустим, у вас есть два модуля mod1.py и mod2.py .

В mod1.py вы импортируете внешний (в пакет, который вы пишете) модуль, скажем numpy , и в mod2.py вы импортируете mod1 :

Внутри mod1.py :

 import numpy as np
 

Внутри mod2.py :

 import mod1
 

Теперь, если строка кода внутри mod2 вызывает np это вызывает ошибку:

Ошибка имени: имя » np » не определено

Как я могу избежать импорта внешних модулей в каждый модуль (= файл) моего пакета?

Ответ №1:

import может делать две вещи:

  1. Создайте модуль
  2. Определите новую переменную в текущей области видимости, привязанную к модулю

Модуль foo будет создаваться только один раз для каждого процесса, независимо от того, сколько раз import foo он выполняется. Однако import foo всегда будет привязывать модуль к имени foo , даже если foo он уже привязан (к этому модулю или какому-либо другому значению) в текущей области.

import numpy as np привязывает имя np к numpy модулю в глобальной области mod1 .

import mod1 связывает только имя mod1 ; оно не переносит глобальные переменные mod1 в глобальную область действия mod2 . Для этого вам понадобится что-то вроде

 from mod1 import np
 

или просто используйте mod1.np внутри mod2 .

То, что вы, похоже, ищете,-это способ поместить np в пространство имен для всего процесса, доступное из любого модуля. В Python есть только одно такое пространство имен, встроенное пространство имен, но вы не можете добавлять к нему имена. У вас есть только отдельные модули-глобальные пространства имен.

Выше я упоминал, что модуль создается только один раз. Это связано с тем, что import сначала будет проверено, существует ли уже запрошенный модуль sys.modules . Вы можете получить доступ np из любого места после того, как он был импортирован один раз с помощью

 sys.modules['numpy']  # The "real" name, not the import-defined alias
 

Однако нет никакой гарантии, что numpy это еще не определено, и вам все равно придется импортировать sys , чтобы получить доступ sys.modules , так что это ничего не даст вам по сравнению с простым использованием

 import numpy as np
 

куда захочешь np .

Комментарии:

1. То, что вы, похоже, ищете,-это способ поместить np в пространство имен для всего процесса, доступное из любого модуля. Это именно то, что я искал. Спасибо вам за ваш ответ.

Ответ №2:

Здесь нужно подумать о двух вещах: об импорте модулей и об именовании этих импорта.

С вашей точки зрения, эти проблемы одинаковы: если вам нужно что-то в пространстве имен, вы импортируете это:

 # mod1.py
import numpy as np
 

Другого способа получить что-либо в пространство имен вашего модуля нет. (Хорошо, нет другого способа, который не включал бы в себя возню с sys.modules тем, чтобы делать то же самое).

Но что здесь сделал питон? Во-первых, он заглянул numpy внутрь sys.modules . Если его там нет, он загружает numpy и вставляет в него загруженный модуль sys.modules["numpy"] . Затем он привязывает локальное имя np , на которое указывает sys.modules["numpy"] . Таким образом, если вы импортируете mod1 , вы можете получить доступ к его импорту:

 #mod2.py
import mod1
mod1.np
 

(В стороне: это строгое соблюдение пространств имен является большим преимуществом и делает python гораздо менее восприимчивым к ошибкам случайного столкновения имен. Это упрощает вашу работу в качестве программиста, так как вам не нужно беспокоиться о том, что имена в импортированном модуле будут перезаписаны другими материалами, поэтому вам не нужно прибегать к таким вещам, как функции именования $get или что-то еще, «очень маловероятное», которое будет использоваться).

Производительность

Означает ли это, что python загрузит один и тот же модуль несколько раз, если вы импортируете его несколько раз? Нет. Python импортирует модуль только один раз: после этого он просто привязывает локальное имя, указывающее на модуль sys.modules . Таким образом, единственное наказание за выполнение:

 #mod2
import numpy as np
import mod1
np
 

Это привязка имени, что тривиально (если вас это так волнует, вы не хотите использовать python). Для этого нет никаких причин:

 # mod2.py
from mod1 import np
np
 

если только не проще не думать о том, откуда берутся ваши функции (вы часто будете видеть, что для удобства вы можете импортировать x из библиотеки y , например, импортировать компоненты Starlette напрямую из FastApi ).

Будучи Декларативным

Управление пространствами имен подобным образом имеет одно огромное преимущество: когда я читаю ваш код, я точно знаю, откуда function_xyz() он берется. Либо вы определили его в области, либо импортировали в эту область. Сравните это, например, с JS или R, когда я должен спросить—откуда именно берется xyz? а потом иди и посмотри его. По той же причине большинство линтеров рассердятся на вас, если вы напишете

 from numpy import *
 

И настаивайте на том, чтобы вы явно импортировали то, что вам нужно.

Комментарии:

1. такое строгое соблюдение пространств имен является большим благом и делает python гораздо менее восприимчивым к ошибкам столкновения случайных имен. Это упрощает вашу работу в качестве программиста,[…] иначе «очень маловероятно» будет использоваться) Если я правильно понял: никто не использует этот синтаксис (практика noob), но вы, кажется, рекомендуете его ?

2. я имел в виду следующее: в отличие от других языков, где вы ожидаете , что ваши модули будут помещены в чужое пространство имен, или где существует только глобальное пространство имен , вам не нужно беспокоиться об этом, если люди этого не делают from yourmodule import * , что в любом случае является плохой практикой. В языках, где это неверное предположение, если вы ожидаете, что ваш код будет использоваться везде, вам придется очень серьезно подумать о том, как вы называете вещи. В python вы можете положиться на то, что люди делают import yourmodule; yourmodule.thing , и не беспокоиться о том, есть ли у них a thing в их пространстве имен.

3. Я открыт для предложений по более четкой формулировке ответа. Я начал этот ответ до того, как был опубликован другой ответ, но мы рассматриваем по существу одну и ту же тему. Я рассказал немного подробнее, чтобы оправдать сохранение этого.

4. Ваш ответ в порядке, спасибо :).