为什么通过字段名访问 namedtuple 比访问 class 的成员变量慢?

Why is accessing a namedtuple by field name slower than accessing a class's member variable?

我们正在做一些实验来比较 classes 和命名元组中的访问时间并观察到一些奇怪的东西。

import time
from collections import namedtuple as namedtuple

class myclass(object):

  def __init__(self, _name, _dob, _value):
    self.name = _name
    self.dob = _dob
    self.value = _value

randomperson1 = myclass( 'randomperson', 10102000, 10.45 )

person = namedtuple( 'person', 'name dob value' )
randomperson2 = person( 'randomperson', 10102000, 10.45) 

在使用 ipython 的 timeit 时,观察到以下情况:

%timeit randomperson1.name,randomperson1.value,randomperson1.dob
10000000 loops, best of 3: 125 ns per loop

%timeit randomperson2.name,randomperson2.value,randomperson2.dob
1000000 loops, best of 3: 320 ns per loop

%timeit randomperson2[0],randomperson2[1],randomperson2[2]
10000000 loops, best of 3: 127 ns per loop

为什么通过字段名访问 namedtuple 比访问 class 的成员变量慢得多?有什么办法可以加快速度吗?

那是因为在 namedtuple 中属性 name, value, dob 不是实例上的简单属性。它们实际上变成了更复杂的东西

collections.py

_field_template = '''\
    {name} = _property(_itemgetter({index:d}), doc='Alias for field number {index:d}')
'''

例如

dob = property(itemgetter(2), doc='Alias for field number 2')

所以你可以看到它上面有额外的层。创建 namedtuple 的人决定以牺牲 CPU 效率为代价来保持内存效率的一致性。这就是原因。

当您创建自己的自定义 class 模拟时,可以很容易地观察到这一点:

from operator import itemgetter

class CustomTuple(tuple):
    my_attr = property(itemgetter(0))

test_tuple = CustomTuple([1])

现在测量对 test_tuple.my_attr 的访问。你应该得到几乎相同的结果。