5

让我们考虑如下的包结构:

myApp
|-- myPackage
|    |-- __init__.py 
|    +-- myModule.py
|-- __init__.py
+-- main.py

myModule.py 包含一个类,例如:

class MyClass( object ):
    _myList = []

    @classmethod
    def test( cls ):
        cls._myList.append( len( cls._myList ) )
        return cls._myList

    def __init__( self ):
        return

    pass

如您所见,没有什么花哨的,我只是将列表定义为静态类变量。现在让我们考虑 main.py 中的代码:

from myApp.myPackage.myModule import MyClass as MyClassAbs
from myPackage.myModule import MyClass as MyClassRel

if __name__ == '__main__':

    print '\n  myList'
    print 'MyClassAbs().test():', MyClassAbs().test() #< Prints [0].
    print 'MyClassRel().test():', MyClassRel().test() #< Prints [0] but should be [0, 1].
    print 'MyClassAbs.test():', MyClassAbs.test() #< Prints [0, 1] but should be [0, 1, 2].
    print 'MyClassRel.test():', MyClassRel.test() #< Prints [0, 1] but should be [0, 1, 2, 3].

    print '\n  myList ids'
    print id( MyClassAbs().test() )
    print id( MyClassRel().test() )
    print id( MyClassAbs.test() )
    print id( MyClassRel.test() )

    print ''
    print 'MyClassAbs == MyClassRel:', MyClassAbs == MyClassRel #< Prints False
    print 'isinstance( MyClassAbs(), MyClassRel ):', isinstance( MyClassAbs(), MyClassRel ) #< Prints False
    print 'isinstance( MyClassRel(), MyClassAbs ):', isinstance( MyClassRel(), MyClassAbs ) #< Prints False

    pass

问题是,在我的代码中,我从同一个模块导入了两次相同的类,但方式不同:一次是绝对导入,一次是相对导入。如代码的最后一部分所示,即使类相同,它们也不相等,因为它们的模块被独特地注册到 sys.modules 中:

'myApp.myPackage.myModule': <module 'myApp.myPackage.myModule' from '/full/path/myApp/myPackage/myModule.py'>
'myPackage.myModule': <module 'myPackage.myModule' from '/full/path/myApp/myPackage/myModule.py'>

因此,它们的表示不同:

'MyClassAbs': <class 'myApp.myPackage.myModule.MyClass'>
'MyClassRel': <class 'myPackage.myModule.MyClass'>

那么......有没有办法将此变量设置为静态的好?

编辑:
上面的代码显然只是减少了我真正的问题。实际上,我基本上有一段代码,它检查递归嵌套在文件夹中的所有模块,并注册其中包含的类。以这种方式注册的所有类都可以使用通用语法进行实例化,例如

myApp.create( 'myClass' )

这就是为什么我有时最终有 2 个对象指向同一个类,但它们以不同的方式导入。一个已通过 imp.load_module() 调用自动导入,另一个将由用户通过常规导入语句直接导入。如果用户决定手动导入一个类,他应该仍然可以访问相同的静态类变量,而不是在同一个但自动注册的类中定义的变量。希望它有意义吗?

4

2 回答 2

8

不。(至少,不是没有非常丑陋和脆弱的黑客。)当您以这两种不同的方式导入它时,该模块实际上被导入了两次。如果再次导入,Python 通常会重新使用已经导入的模块,但这不是基于导入的实际文件,而是基于相对于sys.path. 因此,如果您myModule.py绝对导入一次,相对导入一次,则整个文件实际上会执行两次。如果您执行以下操作,您可以看到这一点:

from myApp.myPackage import myModule
import myApp.myPackage.myModule as sameModule
import myPackage.myModule
print myModule is sameModule
print myModule is myPackage.myModule

您应该看到True第一个打印(因为前两个导入是通过相同的路径),但False第二个(因为第三个导入使用不同的路径)。这也是为什么您会看到该模块在sys.modules.

因为整个模块被导入了两次,所以实际上有两个不同的类称为 MyClass,而不是一个。没有合法的方式让这两个类共享它们的状态。就好像您从两个不同的模块导入了两个不同的类。即使它们碰巧在同一个源文件中定义,当您像这样双重导入它们时,它们也不会以任何方式链接。(链接它们有一种邪恶的方法,即您可以在模块中包含代码以手动检查它是否以不同的名称导入,然后手动sys.modules将当前导入的模块设置为已导入的模块. 但这是个坏主意,因为很难判断导入的内容是否真的是同一个模块,而且还因为踩到sys.modules如果原始导入或新导入因任何原因失败,可能会导致奇怪的错误。)

真正的问题是,为什么要两次导入模块?第二个问题是,你为什么要使用相对进口?解决方案是使用绝对导入只导入一次模块,然后你就没有问题了。

于 2012-11-15T05:41:47.837 回答
-1

当涉及到 python 时,其他语言中的“静态”变量有很多需要回答的问题。当谈到这种语言时,我也发现这种思维方式让我很头疼。

很难说出你的最终目标是什么,但值得注意的是,你可以通过在模块中声明它来创建一个“静态”变量(|cough| 一个“模块”变量?)。所以 myModule 的结构可能类似于:

modList = []

class MyClass(object):
    def __init__(self):
        self._myList = [len(modList),]

    def appendLenToModList(self):
        modList.append(len(self._myList))

The modList is initialized on the import and should serve you as a singleton/static var. If you really want to mess with it directly I'm pretty sure you can even use it from myApp using the namespace myPackage.myModule.modList; but some accessors to formalize its use and purpose might be advisable.

于 2012-11-15T06:28:57.457 回答