使用sum()连接元组

Ste*_*uch 20 python tuples sum python-itertools

这篇文章我了解到你可以连接元组:

>>> tuples = (('hello',), ('these', 'are'), ('my', 'tuples!'))
>>> sum(tuples, ())
('hello', 'these', 'are', 'my', 'tuples!')
Run Code Online (Sandbox Code Playgroud)

这看起来很不错.但为什么这有效呢?并且,这是最优的,还是有一些东西itertools比这个结构更可取?

Bou*_*oud 24

加法运算符连接python中的元组:

('a', 'b')+('c', 'd')
Out[34]: ('a', 'b', 'c', 'd')
Run Code Online (Sandbox Code Playgroud)

来自docstring sum:

返回'start'值的总和(默认值:0)加上可迭代的数字

它意味着sum不是从你的iterable的第一个元素开始,而是从通过start=参数传递的初始值开始.

默认情况下sum与数字一起使用,因此默认的起始值为0.因此,总结一个可迭代的元组需要从一个空元组开始.()是一个空元组:

type(())
Out[36]: tuple
Run Code Online (Sandbox Code Playgroud)

因此工作串联.

根据表现,这是一个比较:

%timeit sum(tuples, ())
The slowest run took 9.40 times longer than the fastest. This could mean that an intermediate result is being cached.
1000000 loops, best of 3: 285 ns per loop


%timeit tuple(it.chain.from_iterable(tuples))
The slowest run took 5.00 times longer than the fastest. This could mean that an intermediate result is being cached.
1000000 loops, best of 3: 625 ns per loop
Run Code Online (Sandbox Code Playgroud)

现在使用大小为10000的t2:

%timeit sum(t2, ())
10 loops, best of 3: 188 ms per loop

%timeit tuple(it.chain.from_iterable(t2))
1000 loops, best of 3: 526 µs per loop
Run Code Online (Sandbox Code Playgroud)

所以如果你的元组列表很小,你就不用费心了.如果它是中等大小或更大,你应该使用itertools.


MSe*_*ert 5

它之所以有效,是因为加法被重载(在元组上)以返回连接的元组:

>>> () + ('hello',) + ('these', 'are') + ('my', 'tuples!')
('hello', 'these', 'are', 'my', 'tuples!')
Run Code Online (Sandbox Code Playgroud)

这基本上就是sum所做的事情,您给出一个空元组的初始值,然后将元组添加到其中。

然而,这通常是一个坏主意,因为添加元组会创建一个新元组,因此您创建几个中间元组只是为了将它们复制到连接的元组中:

()
('hello',)
('hello', 'these', 'are')
('hello', 'these', 'are', 'my', 'tuples!')
Run Code Online (Sandbox Code Playgroud)

这是一个具有二次运行时行为的实现。通过避免中间元组可以避免二次运行时行为。

>>> tuples = (('hello',), ('these', 'are'), ('my', 'tuples!'))
Run Code Online (Sandbox Code Playgroud)

使用嵌套生成器表达式:

>>> tuple(tuple_item for tup in tuples for tuple_item in tup)
('hello', 'these', 'are', 'my', 'tuples!')
Run Code Online (Sandbox Code Playgroud)

或者使用生成器函数:

def flatten(it):
    for seq in it:
        for item in seq:
            yield item


>>> tuple(flatten(tuples))
('hello', 'these', 'are', 'my', 'tuples!')
Run Code Online (Sandbox Code Playgroud)

或者使用itertools.chain.from_iterable

>>> import itertools
>>> tuple(itertools.chain.from_iterable(tuples))
('hello', 'these', 'are', 'my', 'tuples!')
Run Code Online (Sandbox Code Playgroud)

如果您对它们的性能感兴趣(使用我的simple_benchmark):

import itertools
import simple_benchmark

def flatten(it):
    for seq in it:
        for item in seq:
            yield item

def sum_approach(tuples):
    return sum(tuples, ())

def generator_expression_approach(tuples):
    return tuple(tuple_item for tup in tuples for tuple_item in tup)

def generator_function_approach(tuples):
    return tuple(flatten(tuples))

def itertools_approach(tuples):
    return tuple(itertools.chain.from_iterable(tuples))

funcs = [sum_approach, generator_expression_approach, generator_function_approach, itertools_approach]
arguments = {(2**i): tuple((1,) for i in range(1, 2**i)) for i in range(1, 13)}
b = simple_benchmark.benchmark(funcs, arguments, argument_name='number of tuples to concatenate')

b.plot()
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

(Python 3.7.2 64 位、Windows 10 64 位)

因此,虽然sum如果您仅连接几个元组,该方法非常快,但如果您尝试连接大量元组,则速度会非常慢。对于许多元组来说,最快的测试方法是itertools.chain.from_iterable