Python:即使以不同方式导入模块,也要使类变量保持静态

Chr*_*erC 5 python import static class

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

myApp
|-- myPackage
|    |-- __init__.py 
|    +-- myModule.py
|-- __init__.py
+-- main.py
Run Code Online (Sandbox Code Playgroud)

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

class MyClass( object ):
    _myList = []

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

    def __init__( self ):
        return

    pass
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,没有什么花哨的,我只是将列表定义为静态类变量.现在让我们考虑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
Run Code Online (Sandbox Code Playgroud)

问题是,在我的代码中,我从相同的模块导入两次相同的类,但是以不同的方式:一次作为绝对导入,一次作为相对导入.如代码的最后一部分所示,即使类是相同的,它们也不相同,因为它们的模块被特别注册到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'>
Run Code Online (Sandbox Code Playgroud)

因此,他们的代表性不同:

'MyClassAbs': <class 'myApp.myPackage.myModule.MyClass'>
'MyClassRel': <class 'myPackage.myModule.MyClass'>
Run Code Online (Sandbox Code Playgroud)

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

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

myApp.create( 'myClass' )
Run Code Online (Sandbox Code Playgroud)

这就是为什么我有时最终会有两个指向同一个类的对象但是以不同的方式导入.一个是通过imp.load_module()调用自动导入的,另一个是由用户通过传统的import语句直接导入的.如果用户决定手动导入类,他仍应该访问与在同一但自动注册的类中定义的静态类变量相同的静态类变量.希望有道理吗?

Bre*_*arn 7

不.(至少,不是没有非常丑陋和脆弱的黑客.)当你以这两种不同的方式导入它时,模块实际上是两次导入的.如果再次导入,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
Run Code Online (Sandbox Code Playgroud)

您应该看到True第一个打印(因为前两个导入是通过相同的路径),而是False第二个(因为第三个导入使用不同的路径).这也是你看到有两个条目的模块的原因sys.modules.

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

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