Python导入时出现全局名称未定义的错误

4
我有一个应用程序,运行在Postgres和Mysql上。每个程序都会检查确定数据库类型,然后导入postgres_db作为db_util或者mysql_dt作为db_util。当主要代码引用db_util时,一切正常,但是如果导入类,对db_util的引用就没有定义。
我创建了以下两个类和主函数来测试这个问题,并发现了另一个有趣的副作用。B和C类在不同的导入情况下引用ClassA。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. 为什么导入和导入 as 的行为会有差异?我不清楚为什么主要的全局变量对于导入的类不可用。
  2. 动态确定适当的db_util模块进行导入并使其可访问其他已导入的类的良好方法是什么?

更新: 在阅读另一篇关于命名空间的帖子后:“Visibility of global variables from imported modules”,我明白了上面的例子中ClassC可以访问ClassA的原因是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

xa=ca()的类调用更改为xa=self.ca(),这样就可以将其应用到scope_test脚本中。我认为,虽然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对我的帮助,他们的回复有助于澄清这个问题。我会很感激任何与设计相关的反馈。

2个回答

10
在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,它们将得到一个单独的(重复的)副本!


我在我的test_scope.py脚本中的导入语句后立即添加了cb.ca = ca,以为这样会将ca添加到cb命名空间中。但我仍然收到“全局未定义”的错误,并且当我从ClassC打印全局变量时,它打印出全局变量:['self'] -- 没有提到ca,错误仍然存在。我错过了什么? - cswaim
我错过了ca,现在必须将其引用为self.ca,因为ca已经在类中定义。 - cswaim

2

您的问题解决方式存在错误。每个模块都是一个起始为空的命名空间,并且随着它运行的每个语句都填充有一个名称(通常是唯一的):

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”。因此,执行来自ClassX.py的“xa = ca()”代码失败。
通常,每个模块都会导入其所需的内容。您还可以从外部将名称“插入”到模块中,但这通常被认为是非常糟糕的风格(仅保留给非常特殊的情况)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接