4

我有一个在 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 是进口的。

类X.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 模块并使其可供其他导入的类访问的好方法是什么?

更新: 在阅读了关于命名空间的另一篇文章后:“导入模块中全局变量的可见性”,我明白在上面的示例中,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 的回答,他们的回答帮助我澄清了这个问题。我将不胜感激任何与设计相关的反馈。

4

2 回答 2

10

在 Python 中,每个模块都有自己的全局命名空间。当您进行导入时,您只是将导入的模块添加到当前模块的命名空间,而不是添加到任何其他模块的命名空间。如果你想把它放在另一个命名空间中,你需要明确地告诉 Python。

主要.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__of"__main__"而不是它的真实名称。如果另一个模块导入main,他们将获得一个单独的(重复的)副本!

于 2013-05-02T21:51:40.583 回答
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而是定义了。因此,ClassX.py 中的行的执行失败。ClassAClassCxa=ca()

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

于 2013-05-02T21:56:54.927 回答