在Python中,为类实例创建的字典与包含该类相同属性的字典相比很小:
import sys
class Foo(object):
def __init__(self, a, b):
self.a = a
self.b = b
f = Foo(20, 30)
使用Python 3.5.2时,以下调用getsizeof
产生:
>>> sys.getsizeof(vars(f)) # vars gets obj.__dict__
96
>>> sys.getsizeof(dict(vars(f))
288
288 - 96 = 192
节省了字节!
另一方面,使用Python 2.7.12,则返回相同的调用:
>>> sys.getsizeof(vars(f))
280
>>> sys.getsizeof(dict(vars(f)))
280
0
已保存字节。
在这两种情况下,字典显然具有 完全相同的内容 :
>>> vars(f) == dict(vars(f))
True
所以这不是一个因素。此外,这也仅适用于Python 3。
那么,这是怎么回事?为什么__dict__
在Python 3中实例的大小如此之小?
实例__dict__
的实现方式与用dict
或创建的“普通”词典的实现方式不同{}
。实例的字典 共享
键和哈希,并为不同的部分(值)保留一个单独的数组。sys.getsizeof
仅在计算实例字典的大小时才计算这些值。
从Python 3.3开始,CPython中的字典以以下两种形式之一实现:
me_value
成员PyDictKeyEntry
)。据我所知,这种形式被用于与创建字典dict
,{}
和模块命名空间。ma_values
的PyDictObject
)实例字典 始终 以拆分表形式(“密钥共享字典”)实现,该字典表允许给定类的实例为其共享密钥(和散列),__dict__
并且仅在相应值上有所不同。
所有这些都在PEP
412-密钥共享字典中进行了描述
。拆分字典的实现位于Python中,3.3
因此该3
系列的早期版本以及Python2.x
都没有此实现。
__sizeof__
for字典的实现考虑到了这一事实,并且在计算拆分字典的大小时仅考虑与values数组对应的大小。
值得庆幸的是,自我解释:
Py_ssize_t size, res;
size = DK_SIZE(mp->ma_keys);
res = _PyObject_SIZE(Py_TYPE(mp));
if (mp->ma_values) /*Add the values to the result*/
res += size * sizeof(PyObject*);
/* If the dictionary is split, the keys portion is accounted-for
in the type object. */
if (mp->ma_keys->dk_refcnt == 1) /* Add keys/hashes size to res */
res += sizeof(PyDictKeysObject) + (size-1) * sizeof(PyDictKeyEntry);
return res;
据我所知, 仅针对实例的名称空间创建 拆分表字典,使用dict()
或{}
(如PEP中所述) 始终会 导致组合字典没有这些好处。
顺便说一句,因为它很有趣,所以我们总是可以打破这种优化。我目前发现了两种当前的方法,一种愚蠢的方法,或者是一种更为明智的情况:
>>> f = Foo(20, 30)
getsizeof(vars(f))
96
vars(f).update({1:1}) # add a non-string key
getsizeof(vars(f))
288
拆分表仅支持字符串键,添加非字符串键(这实际上是 零 意义)会破坏该规则,CPython将拆分表变成一个合并的表,从而失去了所有内存。
>>> f1, f2 = Foo(20, 30), Foo(30, 40)
for i, j in enumerate([f1, f2]):
… setattr(j, ‘i’+str(i), i)
… print(getsizeof(vars(j)))
96
288
在类的实例中插入不同的键最终将导致拆分表被合并。这并不仅仅适用于已经创建的实例。从该类创建的所有 后续 实例将具有组合字典,而不是拆分字典。
# after running previous snippet
>>> getsizeof(vars(Foo(100, 200)))
288
当然,除了娱乐之外,没有其他理由是故意这样做的。
如果有人想知道,Python
3.6的字典实现不会改变这一事实。前面提到的两种形式的字典仍然可以使用(它们的实现dict.__sizeof__
也作了更改,因此从中返回的值应该有所区别)被进一步压缩getsizeof
。
问题内容: 如果我对Python数据模型的理解是正确的,则类和类实例都具有包含所有属性的关联对象。但是,对于某些类实例(例如的实例)为什么没有属性,我有些困惑。 如果我创建一个自定义类: 然后,我可以说出实例变量: 但是,如果我尝试对内置实例执行相同操作,则会得到: 那么,为什么的实例没有属性? 问题答案: 默认情况下,在C中定义的类型的实例不具有__dict__属性。
问题内容: 我不知道为什么一个类是一个,但是一个实例只是一个普通的 问题答案: 这有助于解释器确保类级属性和方法的键只能是字符串。 在其他地方,Python是一种“成年人同意语言”,这意味着对象的指示由用户公开和可变。但是,对于类的类级属性和方法,如果可以保证键是字符串,则可以在类级简化和加快用于属性和方法查找的常见案例代码。尤其是,通过假定类dict键为字符串,可以简化和加快用于新型类的__mr
代码来源 https://medium.com/geekculture/python-multiprocessing-with-ou... 这里的job.get()是表达什么呢?jobs是个list,每个job也不是queue,list的元素没有get方法,如何理解呢?
问题内容: 因此,我在闲逛时使用了递归,我发现使用递归的循环比常规的while循环要慢得多,我想知道是否有人知道为什么。我已经包括了我下面所做的测试: 但是,在上一次测试中,我注意到如果删除该语句,则表明速度略有提高,因此我想知道if语句是否是造成循环速度差异的原因? 问题答案: 您已将函数编写为尾递归。在许多命令式和函数式语言中,这将触发尾部递归消除,在这种情况下,编译器用简单的JUMP替换了C
问题内容: 这是所有编程语言所共有的吗?在进行多次打印后再执行println似乎更快,但是将所有内容移动到字符串中并仅进行打印似乎最快。为什么? 编辑:例如,Java可以在不到一秒钟的时间内找到所有高达100万的质数- 但要进行打印,然后在自己的println中将它们全部输出可能需要几分钟!最多可打印100亿小时! 例如: 问题答案: 速度并不慢,而是由主机操作系统提供的与控制台连接的基础。 您可
问题内容: 我对此感到困惑 现在让我们来看看numpy: 神圣的CPU周期蝙蝠侠! 使用改进,但恕我直言仍然不够 numpy.version.version =‘1.5.1’ 如果您想知道在第一个示例中是否跳过了列表创建以进行优化,则不是: 问题答案: Numpy已针对大量数据进行了优化。给它一个很小的3长度数组,毫不奇怪,它的性能很差。 考虑单独的测试 输出是 似乎是数组的归零一直花费在nump