当前位置: 首页 > 面试题库 >

什么是最快(访问)Python中类似结构的对象?

侯池暝
2023-03-14
问题内容

我正在优化一些代码,这些代码的主要瓶颈正在运行并访问大量类似于结构的对象。目前,我使用namedtuples来提高可读性。但是使用’timeit’进行的一些快速基准测试表明,在性能是一个重要因素的情况下,这确实是错误的方法:

以a,b,c命名的元组:

>>> timeit("z = a.c", "from __main__ import a")
0.38655471766332994

使用__slots__,a,b,c的类:

>>> timeit("z = b.c", "from __main__ import b")
0.14527461047146062

带有键a,b,c的字典:

>>> timeit("z = c['c']", "from __main__ import c")
0.11588272541098377

使用常量键的具有三个值的元组:

>>> timeit("z = d[2]", "from __main__ import d")
0.11106188992948773

使用常数键列出三个值:

>>> timeit("z = e[2]", "from __main__ import e")
0.086038238242508669

使用本地键的具有三个值的元组:

>>> timeit("z = d[key]", "from __main__ import d, key")
0.11187358437882722

使用本地键列出三个值:

>>> timeit("z = e[key]", "from __main__ import e, key")
0.088604143037173344

首先,这些小timeit测试是否会使它们无效?我每次都跑了几次,以确保没有随机的系统事件引发它们,并且结果几乎相同。

似乎字典在性能和可读性之间提供了最佳的平衡,而类次之。这是不幸的,因为出于我的目的,我还需要对象类似于序列。因此,我选择了namedtuple。

列表的速度要快得多,但是常量键是无法维护的。我必须创建一堆索引常量,即KEY_1 = 1,KEY_2 = 2,依此类推,这也不理想。

我会坚持这些选择吗?还是我错过了其他选择?


问题答案:

要记住的一件事是,命名元组已优化为作为元组进行访问。如果将访问器更改为a[2]而不是a.c,则将看到与元组相似的性能。原因是名称访问者正在有效地转换为对self
[idx]的调用,因此要付出索引编制 名称查找的代价。

如果您的使用模式使得按名称访问很常见,而按元组访问则不常见,那么您 可以
编写一个等效于namedtuple的快速等效方法,以相反的方式进行操作:推迟索引查找以按名称访问。但是,您将在索引查找后付出代价。例如,这是一个快速实现:

def makestruct(name, fields):
    fields = fields.split()
    import textwrap
    template = textwrap.dedent("""\
    class {name}(object):
        __slots__ = {fields!r}
        def __init__(self, {args}):
            {self_fields} = {args}
        def __getitem__(self, idx): 
            return getattr(self, fields[idx])
    """).format(
        name=name,
        fields=fields,
        args=','.join(fields), 
        self_fields=','.join('self.' + f for f in fields))
    d = {'fields': fields}
    exec template in d
    return d[name]

但是__getitem__必须调用的时机非常糟糕:

namedtuple.a  :  0.473686933517 
namedtuple[0] :  0.180409193039
struct.a      :  0.180846214294
struct[0]     :  1.32191514969

即,与__slots__属性访问类的性能相同(毫不奇怪-
这就是事实),但是由于基于索引的访问中进行了两次查找,因此付出了巨大的代价。(值得注意的是,__slots__这实际上并没有太大的帮助。它可以节省内存,但是没有它们,访问时间几乎相同。)

第三种选择是复制数据,例如。list的子类,并将值存储在属性和listdata中。但是,您实际上并没有获得等效于列表的性能。子类化对速度有很大的影响(引入纯Python重载检查)。因此,在这种情况下,struct
[0]仍然需要大约0.5s(原始列表为0.18),并且内存使用量会增加一倍,因此这可能不值得。



 类似资料:
  • 问题内容: 我在文件中有一个ASCII表,我想从中读取一组特定的行(例如,4003至4005行)。问题是该文件可能非常长(例如,十万到几百万行),我想尽快这样做。 错误的解决方案 :读取整个文件,然后转到这些行, 更好的解决方案 :遍历每行,以便不将其全部存储在内存中http://codingdict.com/questions/775 最佳解决方案? 但这仍然需要遍历每一行。是否有更好的(从速度

  • 我试图支持类的类似元组的结构化绑定访问。为了简单起见,我将在本文的其余部分使用以下类: (我知道这个类支持开箱即用的结构化绑定,但假设它不支持。) 为了能够像元组一样访问的成员,我们必须专门化和: 我们需要的最后一部分是测试:: 这是可行的。但是,我想返回对成员的引用,就像一样。因此,我实现如下: 然而,在这个版本中,以下代码 产生错误(GCC 7.1): “std::tuple_元素”类型的绑定

  • 问题内容: Jinja2和Mako看上去都相当快。 它们与string.Template(功能不那么强大但可能对我正在做的事情足够好)相比如何? 问题答案: 这是用于渲染10x1000 HTML表的流行模板引擎的结果。 该基准基于Spitfire性能测试中的代码,并添加了一些模板引擎和迭代以提高准确性。最后的列表和生成器concat是经过手工编码的Python,以感受通过编译为Python字节码可

  • 问题内容: 想象一下,你想使用Python开发非平凡的最终用户桌面(非Web)应用程序。构造项目文件夹层次结构的最佳方法是什么? 理想的功能是易于维护,IDE友好,适用于源代码控制分支/合并以及易于生成安装软件包。 尤其是: 你将源放在哪里? 你将应用程序启动脚本放在哪里? 你将IDE项目放在哪里? 你将单元/验收测试放在哪里? 你将非Python数据(例如配置文件)放在哪里? 你在哪里将非Pyt

  • 问题内容: 我有一个Python脚本,该脚本接受一个整数列表作为输入,我需要一次处理四个整数。不幸的是,我无法控制输入,或者将其作为四元素元组的列表传递。目前,我正在以这种方式对其进行迭代: 不过,这看起来很像“C-think”,这让我怀疑有一种更像蟒蛇的方式来处理这种情况。列表在迭代后被丢弃,因此不需要保留它。也许这样更好? 不过,仍然感觉不太正确。 问题答案: 从Python的itertool

  • 问题内容: 什么是%在计算?我似乎无法弄清楚它的作用。 例如,它算出计算的百分比吗:显然等于0。如何? 问题答案: (取模)运算符从第一个参数除以第二个参数得出余数。首先将数字参数转换为通用类型。右零参数引发ZeroDivisionError异常。参数可以是浮点数,例如3.14%0.7等于0.34(因为3.14等于4 * 0.7 + 0.34。)模运算符始终产生与第二个操作数具有相同符号的结果(或