我问这个是因为我觉得这很令人惊讶——我认为 anamedtuple
会有更多的开销。
(背景是我在内存中缓存了一个大的 Django 查询,发现 Django 对象的大小是 的 100 倍.values()
。然后我想知道namedtuple
这些对象的开销版本是什么,允许我仍然使用.
对项目的访问作为属性。较小的是不是我所期望的。)
#!/usr/bin/env python
from pympler.asizeof import asizeof
from collections import namedtuple
import random
import string
QTY = 100000
class Foz(object):
pass
dicts = [{'foo': random.randint(0, 10000),
'bar': ''.join([random.choice(string.ascii_letters + string.digits) for n in xrange(32)]),
'baz': random.randrange(10000),
'faz': random.choice([True, False]),
'foz': Foz()} for _ in range(QTY)]
print "%d dicts: %d" % (len(dicts), asizeof(dicts))
# /sf/ask/3074486831/
MyTuple = namedtuple('MyTuple', sorted(dicts[0]))
tuples = [MyTuple(**d) for d in dicts]
print "%d namedtuples: %d" % (len(tuples), asizeof(tuples))
print "Ratio: %.01f" % (float(asizeof(tuples)) / float(asizeof(dicts)))
Run Code Online (Sandbox Code Playgroud)
跑步,
$ ./foo.py
100000 dicts: 75107672
100000 namedtuples: 56707472
Ratio: 0.8
Run Code Online (Sandbox Code Playgroud)
单个元组甚至更少,可能是由于以下开销list
:
$ ./foo.py
1 dicts: 1072
1 namedtuples: 688
Ratio: 0.6
Run Code Online (Sandbox Code Playgroud)
是哈希表数组的开销吗?但是不是namedtuple
也需要属性的哈希表吗?是pympler
不是准确?
基本答案是“是”:普通对象有一个内部字典来存储实例的属性:
class Foo:
pass
f = Foo()
print(f.__dict__)
# {}
Run Code Online (Sandbox Code Playgroud)
它必须是一个字典,因为在 Python 中,您可以为类未定义的实例分配新属性:
f.a = 1
print(f.__dict__)
# {'a': 1}
Run Code Online (Sandbox Code Playgroud)
使用 dict 允许快速属性查找,但由于数据结构本身存在内存开销。此外,由于 的不同实例Foo
可能定义了不同的属性,因此每个实例可能需要自己的字典:
g = Foo()
print(g.__dict__)
# {}
print(f.__dict_ == g.__dict__)
# False
Run Code Online (Sandbox Code Playgroud)
Anamedtuple
不允许在运行时添加属性。namedtuple
因此, 的特定实例可以将其所有属性存储在由所有实例共享的单个实例中。
给定一个namedtuple
和一个实例:
Foo = collections.namedtuple("Foo", 'a,b')
f = Foo(1,2)
Run Code Online (Sandbox Code Playgroud)
所述namedtuple
-constructor产生一个描述符的每个字段,并把它存储在类; 这里是存储命名属性和元组索引之间的转换的地方。当您访问a
instance 上f
的属性时,属性访问将通过此描述符路由:
type(Foo.a)
#<class 'property'>
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
647 次 |
最近记录: |