Импорт Python как глобальное имя не определен

У меня есть приложение, которое работает на Postgres и Mysql. Каждая программа проверяет базу данных, а затем импортирует либо postgres_db как db_util, либо mysql_dt как db_util. Все работает хорошо, когда код в main ссылается на db_util, но если класс импортируется, ссылка на db_util не определяется.

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

ClassX.py

class ClassA(object):
    def print_a(self):
        print "this is class a"

class ClassC(object):
    def ref_a(self):
        print 'from C ref a  ==>',
        xa=ClassA()
        xa.print_a()
    def ref_ca(self):
        print 'from C ref ca ==>',
        xa=ca()
        xa.print_a()

test_scope.py

from classes.ClassX import ClassA
from classes.ClassX import ClassA as ca
from classes.ClassX import ClassC as cb


class ClassB(object):
    def ref_a(self):
        print 'from B ref a  ==>',
        xa=ClassA()
        xa.print_a()
    def ref_ca(self):
        print 'from B ref ca ==>',
        xa=ca()
        xa.print_a()

print 'globals:',dir()
print 'modules','ca:',ca,'cb:',cb,'CA:',ClassA
print ''
print 'from main'
xb=ClassB()
xb.ref_a()
xb.ref_ca()

print ''
print 'from imports'
xbs=cb()
xbs.ref_a()
xbs.ref_ca()

И результаты:

globals: ['ClassA', 'ClassB', '__builtins__', '__doc__', '__file__', '__name__', '__package__', 'ca', 'cb']
modules ca: <class 'classes.ClassX.ClassA'> cb: <class 'classes.ClassX.ClassC'> CA: <class 'classes.ClassX.ClassA'>

from main
from B ref a  ==> this is class a
from B ref ca ==> this is class a

from imports
from C ref a  ==> this is class a
from C ref ca ==>
Traceback (most recent call last):
  File "test_scope.py", line 32, in <module>
    xbs.ref_ca()
  File "R:\python\test_scripts\scope\classes\ClassX.py", line 13, in ref_ca
    xa=ca()
NameError: global name 'ca' is not defined
Press any key to continue . . .

Из моего теста я вижу, что объект ca (импортированный как) недоступен для ClassC, однако доступен модуль ClassA (импортированный без as).

  1. Почему разница между импортом и импортом как поведением? Я не понимаю, почему основные глобальные переменные недоступны для основного импорта классов.
  2. Каков хороший подход к динамическому определению соответствующего модуля db_util для импорта и доступности его для других импортированных классов?

Обновление: после прочтения еще одного сообщения о пространствах имен: "Видимость глобальных переменных из импортированных модулей", я понимаю, что в моем примере выше причина, по которой класс A доступен для ClassC, заключается в том, что A и C находятся в одном и том же импортированном файл, таким образом, одно и то же пространство имен.

Итак, оставшийся вопрос - это вопрос дизайна:

если у меня есть такой код:

if db == 'MySQL':
    from mysql_db import db_util
elif db == 'Postgres'
    from postgres_db import db_util

Каков хороший способ сделать db_util доступным для всех импортируемых модулей?

ОБНОВЛЕНИЕ:

из ответа Blckknght я добавил код

cb.ca =ca

к сценарию scope_test. Это требует, чтобы вызов класса xa=ca() был изменен на xa=self.ca(). Я также думаю, что добавление объектов в класс извне, хотя Python это и позволяет, не является хорошей методологией проектирования и превратит отладку в кошмар.

Однако, поскольку я считаю, что модули и классы должны быть автономными или специально объявлять свои зависимости, я собираюсь реализовать класс таким образом, используя приведенный выше пример кода.

разбейте ClassA и ClassC на отдельные модули и в верхней части ClassC, перед определением класса, сделайте импорт

from ClassA import ClassA
from ClassA import ClassA as ca

class ClassB(object):

и в моей реальной ситуации, когда мне нужно импортировать модуль db_util в несколько модулей

ci.py #новый модуль для выбора класса для соответствующей БД

if db == 'MySQL':
    from mysql_db import db_util
elif db == 'Postgres'
    from postgres_db import db_util

в каждом модуле, нуждающемся в классе db_util

import ci
db_util=ci.db_util         #add db_util to module globals

class Module(object):

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

Я закрою этот вопрос и хочу поблагодарить Blckknght и Armin Rigo за их ответы, которые помогли мне прояснить этот вопрос. Буду признателен за любые отзывы, связанные с дизайном.


person cswaim    schedule 02.05.2013    source источник


Ответы (2)


В Python каждый модуль имеет собственное глобальное пространство имен. Когда вы выполняете импорт, вы добавляете импортированные модули только в пространство имен текущего модуля, а не в пространство имен любого другого модуля. Если вы хотите поместить его в другое пространство имен, вам нужно указать это Python явно.

main.py:

if db == "mysql": # or whatever your real logic is
    import mysql_db as db_util
elif db == "postgres":
    import postgres_db as db_util

import some_helper_module

some_helper_module.db_util = db_util # explicitly add to another namespace

#...

Другие модули:

import some_helper_module

db = some_helper_module.db_util.connect() # or whatever the real API is

#...

Обратите внимание, что вы обычно не можете использовать свой основной модуль (который выполняется как сценарий) в качестве общего пространства имен. Это связано с тем, что Python использует атрибут __name__ модуля, чтобы определить, как кэшировать модуль (чтобы вы всегда получали один и тот же объект из нескольких импортов), но сценарию всегда присваивается __name__ из "__main__", а не его настоящее имя. Если другой модуль импортирует main, он получит отдельную (дубликат) копию!

person Blckknght    schedule 02.05.2013
comment
Я добавил cb.ca =ca в свой сценарий test_scope.py выше сразу после импорта, думая, что это добавит ca в пространство имен cb. Я все еще получаю глобальные не определенные, и когда я печатаю глобальные переменные из ClassC, они печатают глобальные переменные: ['self'] - нет ссылки на ca, и ошибка все еще существует. Что я пропустил? - person cswaim; 03.05.2013
comment
Я пропустил, что на ca нужно ссылаться как на self.ca, так как ca теперь определено внутри класса. - person cswaim; 03.05.2013

Вы подходите к проблеме с неправильной точки зрения. Каждый модуль представляет собой пространство имен, которое начинается пустым и заполняется именем (обычно) каждого выполняемого им оператора:

import foo                   => defines foo
from foo import bar          => defines bar
from foo import bar as baz   => defines baz
class kls:
    pass                     => defines kls
def fun():
    pass                     => defines fun
var = 6 * 7                  => defines var

Глядя на ClassX.py, мы видим, что имя ca не определено в этом модуле, но ClassA и ClassC определены. Таким образом, выполнение строки xa=ca() из ClassX.py не выполняется.

В общем, идея в том, что каждый отдельный модуль импортирует то, что ему нужно. Вы также можете вставить имя "в" модуль извне, но это обычно считается очень плохим стилем (зарезервировано для очень особых случаев).

person Armin Rigo    schedule 02.05.2013