为什么属性引用与 Python 继承的行为如此相似?[重复]
- 2025-03-21 09:07:00
- admin 原创
- 43
问题描述:
以下内容看起来很奇怪..基本上,somedata 属性似乎在从继承的所有类之间共享the_base_class
。
class the_base_class:
somedata = {}
somedata['was_false_in_base'] = False
class subclassthing(the_base_class):
def __init__(self):
print self.somedata
first = subclassthing()
{'was_false_in_base': False}
first.somedata['was_false_in_base'] = True
second = subclassthing()
{'was_false_in_base': True}
>>> del first
>>> del second
>>> third = subclassthing()
{'was_false_in_base': True}
self.somedata
在函数中定义__init__
显然是解决这个问题的正确方法(所以每个类都有自己的somedata
字典) - 但是什么时候这种行为是可取的?
解决方案 1:
你是对的,somedata
它是在类及其子类的所有实例之间共享的,因为它是在类定义时创建的。
somedata = {}
somedata['was_false_in_base'] = False
在定义类时执行,即解释器遇到class
语句时执行 -而不是在创建实例时执行(请考虑 Java 中的静态初始化块)。如果类实例中不存在某个属性,则检查类对象是否存在该属性。
在类定义时,您可以运行任意代码,如下所示:
import sys
class Test(object):
if sys.platform == "linux2":
def hello(self):
print "Hello Linux"
else:
def hello(self):
print "Hello ~Linux"
在 Linux 系统上,Test().hello()
将打印Hello Linux
,在所有其他系统上,将打印其他字符串。
相比之下, 中的对象__init__
是在实例时创建的,并且仅属于该实例(当它们被分配给时self
):
class Test(object):
def __init__(self):
self.inst_var = [1, 2, 3]
在类对象上定义的对象(而不是实例)在许多情况下都很有用。例如,您可能希望缓存类的实例,以便可以共享具有相同成员值的实例(假设它们应该是不可变的):
class SomeClass(object):
__instances__ = {}
def __new__(cls, v1, v2, v3):
try:
return cls.__insts__[(v1, v2, v3)]
except KeyError:
return cls.__insts__.setdefault(
(v1, v2, v3),
object.__new__(cls, v1, v2, v3))
大多数情况下,我将类主体中的数据与元类或通用工厂方法结合使用。
解决方案 2:
请注意,您所看到的部分行为是由于somedata
,dict
而不是诸如 之类的简单数据类型bool
。
例如,看看这个行为不同(尽管非常相似)的不同例子:
class the_base_class:
somedata = False
class subclassthing(the_base_class):
def __init__(self):
print self.somedata
>>> first = subclassthing()
False
>>> first.somedata = True
>>> print first.somedata
True
>>> second = subclassthing()
False
>>> print first.somedata
True
>>> del first
>>> del second
>>> third = subclassthing()
False
这个例子的行为与问题中给出的例子不同,原因是这里first.somedata
被赋予了一个新值(对象True
),而在第一个例子中,引用的 dict 对象first.somedata
(以及其他子类实例)被修改了。
请参阅 Torsten Marek 对此答案的评论以获得进一步的说明。
解决方案 3:
我认为理解这一点的最简单方法(以便您可以预测行为)是认识到somedata
如果您以这种方式定义它,您是该类的一个属性而不是该类的实例。
实际上始终只有一个,somedata
因为在您的示例中,您没有分配给该名称,而是使用它来查找字典,然后为其分配一个项目(键、值)。这是一个陷阱,是 Python 解释器工作方式的结果,一开始可能会令人困惑。