Python模块/类变量泄漏
好的,我花了一些时间才弄清楚这个问题,但看起来这是Python故意这样做的。有人能解释一下为什么会这样吗?我该怎么解决这个问题呢?
文件:library/testModule.py
class testClass:
myvars = dict()
def __getattr__(self, k):
if self.myvars.has_key(k):
return self.myvars[k]
def __setattr__(self, k, v):
self.myvars[k] = v
def __str__(self):
l = []
for k, v in self.myvars.iteritems():
l.append(str(k) + ":" + str(v))
return " - ".join(l)
test.py
from library import testModule
#I get the same result if I instantiate both classes one after another
c1 = testClass()
c1.foo = "hello"
c2 = testClass()
print("c1: " + str(c1) + "\n")
print("c2: " + str(c2) + "\n")
输出:
c1: foo:hello
c2: foo:hello
我猜测是因为library
里有一个"__init__.py"
文件,所以整个模块就像一个类对象一样被加载了,现在它变成了一个持久的对象……这是这样吗?
3 个回答
如果你想了解如何使用getattr以及其他类似的方法,可以参考这个关于描述符的使用指南,而且多多练习是最有效的学习方式!
其他回答都很准确,直接切入主题。让我来解释一下我认为你可能存在的一些误解。
我猜测是因为库里有一个“
__init__.py
”文件,所以整个模块像一个类对象一样被加载,现在它成了一个持久的对象……这是这样吗?
所有的包都有一个__init__.py
文件。这个文件是让某个东西变成Python包的必要条件。这个包里面可以有代码,也可以没有。如果有代码,那它一定会被执行。一般来说,这个文件的存在和包里其他模块的执行没有直接关系,虽然你可以在里面放一些很酷的技巧来影响它。
关于模块和类的工作方式,通常把模块想象成一个只被实例化一次的类对象是个不错的主意。加载器会执行文件一次,文件末尾的所有变量、类定义和函数定义都会作为模块的一部分被访问。
类也是如此,主要的区别在于类里面定义的函数会变成方法(还有一个特殊的方法可以让你实例化这个类)。所以testModule
有一个testClass
,而testClass
又有myvars
。这三个对象都是独一无二的:它们不会有多个实例。而这种“拥有”的关系其实差不多,无论我们说“模块有一个类对象”还是“类对象有一个类变量”。(区别在于实现细节,你不需要太关注这些。)
myvars
是一个属于 类 的属性,而不是属于 实例 的。这意味着,当你从实例 c1
中往 myvars
里添加一个属性时,这个属性是和类 testClass
关联的,而不是专门和实例 c1
关联的。因为 c2
也是同一个类的实例,所以它也会有这个相同的属性。
如果你想要实现你想要的效果,可以这样写:
class testClass:
def __init__(self):
self.myvars = dict()
def __getattr__(self, k):
if self.myvars.has_key(k):
return self.myvars[k]
def __setattr__(self, k, v):
self.myvars[k] = v
def __str__(self):
l = []
for k, v in self.myvars.iteritems():
l.append(str(k) + ":" + str(v))
return " - ".join(l)