Python导入为全局名称未定义

csw*_*aim 3 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是主要的,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()
Run Code Online (Sandbox Code Playgroud)

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()
Run Code Online (Sandbox Code Playgroud)

结果如下:

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 . . .
Run Code Online (Sandbox Code Playgroud)

从我的测试中,我看到对象ca(导入为)不可用于ClassC,但是,模块ClassA可用(导入时没有).

  1. 为什么导入和导入之间的区别是行为?我不清楚为什么主要全局变量不适用于主要进口类.
  2. 动态确定要导入的相应db_util模块并让其他导入类可以访问的有什么好方法?

更新: 在阅读了关于命名空间的另一篇文章:" 导入模块中全局变量的可见性 "之后,我理解在上面的示例中,ClassA可用于ClassC的原因是A&C在同一导入文件中,因此具有相同的命名空间.

所以剩下的问题是一个设计问题:

如果我有这样的代码:

if db == 'MySQL':
    from mysql_db import db_util
elif db == 'Postgres'
    from postgres_db import db_util
Run Code Online (Sandbox Code Playgroud)

使db_util可用于所有导入模块的好方法是什么?

更新:

从Blckknght的回应中,我添加了代码

cb.ca =ca
Run Code Online (Sandbox Code Playgroud)

到scope_test脚本.这需要将对xa = ca()的类调用更改为xa = self.ca().我还认为,尽管Python允许,但是从类外部向类中添加对象并不是一种好的设计方法,并且会使调试成为一场噩梦.

但是,因为我认为模块和类应该是独立的或者专门声明它们的依赖关系,所以我将使用上面的代码示例来实现这样的类.

将ClassA和ClassC分解为单独的模块,并在ClassC的顶部,在类定义之前进行导入

from ClassA import ClassA
from ClassA import ClassA as ca

class ClassB(object):
Run Code Online (Sandbox Code Playgroud)

在我的实际情况中,我需要将db_util模块导入几个模块

ci.py #new模块为适当的db选择类

if db == 'MySQL':
    from mysql_db import db_util
elif db == 'Postgres'
    from postgres_db import db_util
Run Code Online (Sandbox Code Playgroud)

在需要db_util类的每个模块中

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

class Module(object):
Run Code Online (Sandbox Code Playgroud)

这样做的一个问题是它需要每个模块使用db_util来导入它,但它确实使依赖性已知.

我将结束这个问题,并感谢Blckknght和Armin Rigo的回复,这些回复有助于我澄清这个问题.我很感激任何与设计相关的反馈.

Blc*_*ght 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

#...
Run Code Online (Sandbox Code Playgroud)

其他模块:

import some_helper_module

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

#...
Run Code Online (Sandbox Code Playgroud)

请注意,您通常不能将主模块(作为脚本执行)用作共享命名空间.这是因为Python使用模块的__name__属性来确定如何缓存模块(让你始终得到多次导入同一个对象),但脚本总是给出__name__"__main__",而不是它的真实姓名.如果另一个模块导入main,他们将获得一个单独的(重复)副本!