Python导入为全局名称未定义

时间:2013-05-02 19:23:04

标签: python import global python-import

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

我在类和main中创建了以下内容来测试问题,并发现了另一个有趣的副作用。课程B& C引用ClassA在不同的导入情况下。 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可用(导入时没有)。

  1. 为什么导入和导入之间的区别是行为?我不清楚为什么主要全局变量不能用于主要进口类。
  2. 动态确定要导入的相应db_util模块并让其他导入类可以访问的有什么好方法?
  3. 更新 在阅读了关于命名空间的另一篇文章后:“ Visibility of global variables from imported modules ”,我理解在上面的例子中,classA可用于ClassC的原因是A&amp; 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模块选择适当的db

    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 个答案:

答案 0 :(得分:9)

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

答案 1 :(得分: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()失败。

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