我有一个在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可用(导入时没有)。
更新 在阅读了关于命名空间的另一篇文章后:“ 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的回复,这些回复有助于我澄清这个问题。我很感激任何与设计相关的反馈。
答案 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
,但ClassA
和ClassC
是。因此,从ClassX.py执行行xa=ca()
失败。
一般来说,我们的想法是每个模块都会导入所需的内容。您也可以从外部将一个名称“修补”到一个模块中,但这通常被认为是非常糟糕的样式(保留用于特殊情况)。