【问题标题】:Python import as global name not definedPython导入为未定义的全局名称
【发布时间】:2013-04-27 02:25:54
【问题描述】:

我有一个在 Postgres 和 Mysql 上运行的应用程序。每个程序都会检查以确定数据库,然后将 postgres_db 作为 db_util 或 mysql_dt 作为 db_util 导入。当 main 中的代码引用 db_util 时一切正常,但如果导入了一个类,则未定义对 db_util 的引用。

我在 classes 和 main 中创建了以下内容来测试问题,并发现了另一个有趣的副作用。 B类和C类在不同的导入情况下引用A类。 B & C 是相同的,除了 B 在 main 中并且 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. 为什么 import 和 import as 行为有区别?我不清楚为什么 mains 全局变量不适用于类 main 导入。
  2. 动态确定要导入的适当 db_util 模块并使其可供其他导入的类访问的好方法是什么?

更新: 在阅读了关于命名空间的另一篇文章:“Visibility of global variables from imported modules”之后,我了解到在上面的示例中,classA 可用于 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 #new 模块为适当的数据库选择类

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 的回答,他们的回答帮助我澄清了这个问题。如有任何与设计相关的反馈,我将不胜感激。

【问题讨论】:

    标签: python import global python-import


    【解决方案1】:

    在 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__ 属性来确定如何缓存模块(这样您总是从多个导入中获取相同的对象),但是脚本总是被赋予 "__main__"__name__ 而不是它的真实名称.如果另一个模块导入 main,他们将获得一个单独的(重复的)副本!

    【讨论】:

    • 我在导入后立即将 cb.ca =ca 添加到上面的 test_scope.py 脚本中,认为这会将 ca 添加到 cb 命名空间。我仍然没有定义全局,当我从 ClassC 打印全局时,它会打印 globals: ['self'] -- 没有引用 ca 并且错误仍然存​​在。我错过了什么?
    • 我错过了 ca 必须被引用为 self.ca 因为 ca 现在在类中定义。
    【解决方案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 没有在这个模块中定义,但 ClassAClassC 是。所以 ClassX.py 中xa=ca() 行的执行失败了。

    一般来说,这个想法是每个模块都导入它需要的东西。您也可以从外部将名称“修补”到模块中,但这通常被认为是非常糟糕的样式(保留用于非常特殊的情况)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-02-03
      • 2013-08-23
      • 2013-11-12
      • 2014-12-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-26
      相关资源
      最近更新 更多