出于清晰和组织的原因,我尝试始终在 __init__
中声明类属性。最近,由于PEP 412,我了解到严格遵循这种做法也有额外的非美学好处。为 Python 3.3 添加。具体来说,如果所有属性都在 __init__
中定义,那么对象可以通过共享它们的键和哈希来减少空间。
我的问题是,当在 __init__
调用的函数中声明属性时,是否会发生对象 key 共享?
这是一个例子:
class Dog:
def __init__(self):
self.height = 5
self.weight = 25
class Cat:
def __init__(self):
self.set_shape()
def set_shape(self):
self.height = 2
self.weight = 10
在这种情况下,Dog
的所有实例将共享键 height
和 weight
。 Cat
的实例是否也共享键 height
和 weight
(彼此之间,当然不是 Dog
).
顺便说一句,您将如何测试它?
请注意,Brandon Rhodes 在他的 Dictionary Even Mightier talk 中谈到了 key 共享:
If a single key is added that is not in the prototypical set of keys, you loose the key sharing
最佳答案
does object key-sharing happen when attributes are declared in a function that is called by
__init__
?
是的,无论您从何处设置属性,假设在初始化后两者都具有相同的键集,实例字典使用共享键字典实现。两种情况都减少了内存占用。
您可以使用 sys.getsizeof
获取实例字典的大小,然后将其与从中创建的类似字典进行比较,从而对此进行测试。 dict.__sizeof__
的实现基于此进行区分以返回不同的大小:
# on 64bit version of Python 3.6.1
print(sys.getsizeof(vars(c)))
112
print(getsizeof(dict(vars(c))))
240
所以,要找出答案,您需要做的就是比较这些。
至于您的编辑:
"If a single key is added that is not in the prototypical set of keys, you loose the key sharing"
正确,这是我(目前)发现破坏共享 key 用法的两件事之一:
- 在实例字典中使用非字符串键。这只能以愚蠢的方式完成。 (你可以使用
vars(inst).update
来完成) 同一类的两个实例的字典内容有偏差,可以通过修改实例字典来解决。 (添加到其中的单个键不在原型(prototype)键集中)
我不确定在添加单个 键时是否会发生这种情况,这是一个可能会更改的实现细节。 (附录:见 Martijn 的评论)
有关这方面的相关讨论,请参阅我在此处进行的问答:Why is the __dict__ of instances so small in Python 3?
这两件事都会导致 CPython 使用“普通”字典。当然,这是不应依赖的实现细节。您可能会或可能不会在 Python 的其他实现和/或 CPython 的 future 版本中找到它。
关于python - 在从 __init__ 调用的函数中声明变量是否仍然使用 key 共享字典?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44705535/