Python模块/类变量泄漏

0 投票
3 回答
907 浏览
提问于 2025-04-15 11:13

好的,我花了一些时间才弄清楚这个问题,但看起来这是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 个回答

0

如果你想了解如何使用getattr以及其他类似的方法,可以参考这个关于描述符的使用指南,而且多多练习是最有效的学习方式!

2

其他回答都很准确,直接切入主题。让我来解释一下我认为你可能存在的一些误解。

我猜测是因为库里有一个“__init__.py”文件,所以整个模块像一个类对象一样被加载,现在它成了一个持久的对象……这是这样吗?

所有的包都有一个__init__.py文件。这个文件是让某个东西变成Python包的必要条件。这个包里面可以有代码,也可以没有。如果有代码,那它一定会被执行。一般来说,这个文件的存在和包里其他模块的执行没有直接关系,虽然你可以在里面放一些很酷的技巧来影响它。

关于模块和类的工作方式,通常把模块想象成一个只被实例化一次的类对象是个不错的主意。加载器会执行文件一次,文件末尾的所有变量、类定义和函数定义都会作为模块的一部分被访问。

类也是如此,主要的区别在于类里面定义的函数会变成方法(还有一个特殊的方法可以让你实例化这个类)。所以testModule有一个testClass,而testClass又有myvars。这三个对象都是独一无二的:它们不会有多个实例。而这种“拥有”的关系其实差不多,无论我们说“模块有一个类对象”还是“类对象有一个类变量”。(区别在于实现细节,你不需要太关注这些。)

7

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)

撰写回答