python-3.x – 与tuple / dictionary / class相比,namedtuple变慢

我很好奇为什么namedtuple比
python中的常规类慢.考虑以下:

In [1]: from collections import namedtuple

In [2]: Stock = namedtuple('Stock', 'name price shares')  

In [3]: s = Stock('AAPL', 750.34, 90)

In [4]: %%timeit 
   ...: value = s.price * s.shares
   ...:          
175 ns ± 1.17 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

In [5]: class Stock2:
   ...:     __slots__ = ('name', 'price', 'shares')
   ...:     def __init__(self, name, price, shares):
   ...:         self.name = name       
   ...:         self.price = price
   ...:         self.shares = shares

In [6]: s2 = Stock2('AAPL', 750.34, 90)

In [8]: %%timeit
   ...: value = s2.price * s2.shares
   ...:                                
106 ns ± 0.832 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

In [9]: class Stock3:                  
   ...:     def __init__(self, name, price, shares):
   ...:         self.name = name 
   ...:         self.price = price     
   ...:         self.shares = shares

In [10]: s3 = Stock3('AAPL', 750.34, 90)

In [11]: %%timeit                      
    ...: value = s3.price * s3.shares
    ...:         
118 ns ± 3.54 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

In [12]: t = ('AAPL', 750.34, 90)

In [13]: %%timeit         
    ...: values = t[1] * t[2]          
    ...:
93.8 ns ± 1.13 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

In [14]: d = dict(name='AAPL', price=750.34, shares=90)                                

In [15]: %%timeit                          
...: value = d['price'] * d['shares']
...:                                   
92.5 ns ± 0.37 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

我期望namedtuple来到没有插槽的课程之前.这是在python3.6上.同样令人惊讶的是,字典的性能可与元组相媲美.

最佳答案 对于python类的一个实例,通过点表示法设置和获取属性主要是通过__dict __ [attribute_name](__ dict__本身是一个属性,它是一个字典)的实例,取决于__dict的值__ [attribute_name]调用它v ,有不同的行为.

情况一:v不是descriptor,所以点符号只返回v.
情况二:v是一个描述符,结果将从描述符的__get__方法中获取.

对于描述中的简单类实例:很容易就是第一种情况

对于namedtuple情况:看看namedtuple source code,函数namedtuple正在使用这个template创建一个类,其中在dict中命名字段存储作为属性.
其中property是描述符,itemgetter实例将在描述符的__get__方法中使用!
这是python代码中的属性类,位于Cpython中c源代码的注释中:

class property(object):

    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
        if doc is None and fget is not None and hasattr(fget, "__doc__"):
            doc = fget.__doc__
        self.__get = fget
        self.__set = fset
        self.__del = fdel
        self.__doc__ = doc

    def __get__(self, inst, type=None):
        if inst is None:
            return self
        if self.__get is None:
            raise AttributeError, "unreadable attribute"
        return self.__get(inst)

    def __set__(self, inst, value):
        if self.__set is None:
            raise AttributeError, "can't set attribute"
        return self.__set(inst, value)

    def __delete__(self, inst):
        if self.__del is None:
            raise AttributeError, "can't delete attribute"
        return self.__del(inst)

总结一下,我们应该理解为什么namedtupple访问速度较慢,有一些额外的步骤可以从nametuple创建的类的实例中获取值而不是简单的类.

如果你想深入挖掘,看看如何存储和获取值,你可以通过上面的链接阅读python3.6的源代码.

提示:
namedtuple创建类是一个子类,将字段值存储为元组,并通过属性存储相关索引及其名称.

点赞