Я играл с примерами, чтобы ответить question posted here on SO и нашел трудным понять механику, через которую python's import *
испортил сферу действия.Понимание импорта python * механика под круговой ссылкой
Сначала немного контекста: этот вопрос не касается практической проблемы; Я хорошо понимаю, что from foo import *
нахмурился (справедливо так), и я понимаю, что это по причинам глубже, чем ясность в коде. Мой интерес здесь в понимания механики, которая вызывает плохое поведение с кругом import *
s. Другими словами, я понимаю , что ожидается наблюдаемое поведение; Я не понимаю почему.
ситуация, я не в состоянии понять те проблемы, которые возникают при наличии, из импортированного модуля (b
), ссылка на импортируемом модуль (a
), используя *
. Мне удалось наблюдать тонкие различия в поведении, когда модуль импорта использует *
или нет, но общее (плохое) поведение остается прежним. Я не мог найти ясного объяснения ни в документации, ни в SO.
Изучение поведения через то, что доступно в области видимости, мне удалось создать небольшой пример, иллюстрирующий различия в его содержании на основе вышеупомянутого вопроса и несколько поисков, которые я сделал здесь, в SO и в других местах. Я стараюсь продемонстрировать как можно более лаконично. Все приведенные ниже коды и эксперименты выполнялись с помощью python 2.7.8.
Рабочие сценарии
Первый тривиальный модуль, содержащий тривиальный модуль, содержащий один класс, a.py
:
class A:
pass
Первый вариант клиентского кода, импортирование модуля а, b_v1.py
:
from pprint import pprint
def dump_frame(offset=0):
import sys
frame = sys._getframe(1+offset)
d = frame.f_globals
d.update(frame.f_locals)
return d
print 'before import v1'
pprint (dump_frame())
import a
print 'after import v1'
pprint (dump_frame())
print a.A()
Второй вариант одного и того же кода, импорт *
из модуля a
, b_v2.py
:
from pprint import pprint
def dump_frame(offset=0):
import sys
frame = sys._getframe(1+offset)
d = frame.f_globals
d.update(frame.f_locals)
return d
print 'before import v2'
pprint (dump_frame())
from a import *
print 'after import v2'
pprint (dump_frame())
print A()
- Запуск как
b_v1
иb_v2
производят тот же результат, перед импортом, и оба способны создать экземпляр, как и ожидалось. Однако после импорта, как и ожидалось, они различаются. Я подчеркнуть разницу:
b_v1.py
, имеет в рамках
'a': <module 'a' from '.../stackoverflow/instance/a.py'>
в то время как b_v2.py
не делает, но имеет
'A': <class a.A at 0x...>
до и после импорта, объем содержит
__builtins__
установлен на<module '__builtin__' (built-in)>
.Оба варианта преуспевают в создании экземпляра
A
.
Не рабочие сценарии
Интригующее поведение при изменении a.py
содержит циклическую ссылку на b
(в обоих b_v1
и b_v2
вариантов).
Скорректированный код a.py
:
from b_v1 import *
class A:
pass
(ради затрудненного, всего один случай a.py
показан, очевидно, в случае b_v2.py
импорта для этого модуля, не b_v1.py
)
В мои наблюдения за содержанием сферы применения в сценарии с круглой ссылкой, я вижу:
В обоих вариантах до импорта в
a
,__builtins__
аналогичен приведенным выше случаям. После импорта, однако, она изменяется и содержитdict
из'ArithmeticError':, 'AssertionError':, 'AttributeError':, ...
, который неоправданно долго здесь.
- Изменен
__builtins__
присутствует в наличии дважды. Это я понимаю как следствие импорта и, вероятно, не произойдет, если код будет внутри функции. В варианте
b_v2
модульa
присутствует в области; он присутствует в вариантеb_v1
.В обоих вариантах, экземпляр
A
не работает. Учитывая, что в вариантеb_v1
модуль присутствует в области (поэтому, я предполагаю, был успешно импортирован), я ожидал, что сможет создать экземплярA
. Это не тот случай. Однако есть различия: в случаеb_v1.py
он не работает сAttributeError: 'module' object has no attribute 'A'
и, как и дляb_v2.py
, сбой -NameError
. В этом более позднем случае это всегда одна и та же ошибка независимо от того, пытаюсь ли я создать экземпляр какA()
(как в рабочем примере)a.A()
.
Суммируя мои вопросы:
через то, что механика циркуляр
import *
путает область?Почему невозможно создать экземпляр
A
в случае b_v1, хотя модуль находится в области?
Возможно, вы можете попросить примеры, которые используют 'из импорта A' вместо' from import * '. 'Import *' не проблема; проблема заключается в попытке получить доступ к чему-либо из 'a', когда' a' еще не загружена полностью. – BrenBarn