vacia una tuplas tupla llenar listas lista diccionario datos crear conjuntos python sum tuples itertools

python - una - tupla sql



Concatenar las tuplas usando suma() (4)

Eso es inteligente y tuve que reírme porque la ayuda prohíbe expresamente las cadenas, pero funciona.

sum(...) sum(iterable[, start]) -> value Return the sum of an iterable of numbers (NOT strings) plus the value of parameter ''start'' (which defaults to 0). When the iterable is empty, return start.

Puedes agregar tuplas para obtener una tupla nueva y más grande. Y como le dio una tupla como valor de inicio, la adición funciona.

De este post aprendí que puedes concatenar tuplas con:

>>> tuples = ((''hello'',), (''these'', ''are''), (''my'', ''tuples!'')) >>> sum(tuples, ()) (''hello'', ''these'', ''are'', ''my'', ''tuples!'')

Que se ve muy bien. Pero ¿por qué funciona esto? Y, ¿es esto óptimo, o hay algo de itertools que sería preferible a esta construcción?


Funciona porque la adición está sobrecargada (en tuplas) para devolver la tupla concatenada:

>>> () + (''hello'',) + (''these'', ''are'') + (''my'', ''tuples!'') (''hello'', ''these'', ''are'', ''my'', ''tuples!'')

Eso es básicamente lo que está haciendo la sum , le das un valor inicial de una tupla vacía y luego le agregas las tuplas.

Sin embargo, esto generalmente es una mala idea porque la adición de tuplas crea una tupla nueva, por lo que crea varias tuplas intermedias solo para copiarlas en la tupla concatenada:

() (''hello'',) (''hello'', ''these'', ''are'') (''hello'', ''these'', ''are'', ''my'', ''tuples!'')

Esa es una implementación que tiene un comportamiento de tiempo de ejecución cuadrático. Ese comportamiento de tiempo de ejecución cuadrático puede evitarse evitando las tuplas intermedias.

>>> tuples = ((''hello'',), (''these'', ''are''), (''my'', ''tuples!''))

Usando expresiones de generador anidadas:

>>> tuple(tuple_item for tup in tuples for tuple_item in tup) (''hello'', ''these'', ''are'', ''my'', ''tuples!'')

O usando una función de generador:

def flatten(it): for seq in it: for item in seq: yield item >>> tuple(flatten(tuples)) (''hello'', ''these'', ''are'', ''my'', ''tuples!'')

O usando itertools.chain.from_iterable :

>>> import itertools >>> tuple(itertools.chain.from_iterable(tuples)) (''hello'', ''these'', ''are'', ''my'', ''tuples!'')

Y si está interesado en cómo funcionan (usando mi paquete 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()

(Python 3.7.2 64 bits, Windows 10 64 bits)

Entonces, mientras que el enfoque de la sum es muy rápido si concatena solo unas pocas tuplas, será muy lento si intenta concatenar muchas tuplas. El más rápido de los enfoques probados para muchas tuplas es itertools.chain.from_iterable


Solo para complementar la respuesta aceptada con algunos puntos de referencia más:

import functools, operator, itertools import numpy as np N = 10000 M = 2 ll = tuple(tuple(x) for x in np.random.random((N, M)).tolist()) %timeit functools.reduce(operator.add, ll) # 407 ms ± 5.63 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) %timeit functools.reduce(lambda x, y: x + y, ll) # 425 ms ± 7.16 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) %timeit sum(ll, ()) # 426 ms ± 14.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) %timeit tuple(itertools.chain(*ll)) # 601 µs ± 5.43 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) %timeit tuple(itertools.chain.from_iterable(ll)) # 546 µs ± 25.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

EDITAR : el código se actualiza para utilizar tuplas. Y, según los comentarios, las dos últimas opciones ahora están dentro de un constructor de tuple() , y todas las veces se han actualizado (por coherencia). Las opciones de itertools.chain* siguen siendo las más rápidas, pero ahora el margen se reduce.


el operador de adición concatena las tuplas en python:

(''a'', ''b'')+(''c'', ''d'') Out[34]: (''a'', ''b'', ''c'', ''d'')

De la cadena de documentos de la sum :

Devuelva la suma de un valor de ''inicio'' (predeterminado: 0) más un número de números

Significa que la sum no comienza con el primer elemento de su iterable, sino con un valor inicial que se pasa a través del argumento start= .

Por defecto, la sum se usa con números, por lo tanto, el valor de inicio predeterminado es 0 . Así que sumar una iterable de tuplas requiere comenzar con una tupla vacía. () es una tupla vacía:

type(()) Out[36]: tuple

Por eso la concatenación de trabajo.

Según el rendimiento, aquí hay una comparación:

%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

Ahora con t2 de un tamaño 10000:

%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

Así que si tu lista de tuplas es pequeña, no te preocupes. Si es de tamaño mediano o más grande, debes usar itertools .