Naiver Ansatz
def transpose_finite_iterable(iterable):
return zip(*iterable) # `itertools.izip` for Python 2 users
funktioniert gut für endliche iterierbare (z. B. Sequenzen wie list
/ tuple
/ str
) von (potentiell unendlichen) Iterablen, die wie folgt dargestellt werden können
| |a_00| |a_10| ... |a_n0| |
| |a_01| |a_11| ... |a_n1| |
| |... | |... | ... |... | |
| |a_0i| |a_1i| ... |a_ni| |
| |... | |... | ... |... | |
where
n in
,
a_ij
entspricht j
-te Element von i
-th iterable,
und nach der Anwendung transpose_finite_iterable
erhalten wir
| |a_00| |a_01| ... |a_0i| ... |
| |a_10| |a_11| ... |a_1i| ... |
| |... | |... | ... |... | ... |
| |a_n0| |a_n1| ... |a_ni| ... |
Ein Beispiel für einen solchen Fall ist die Python. a_ij == j
, n == 2
>>> from itertools import count
>>> iterable = [count(), count()]
>>> result = transpose_finite_iterable(iterable)
>>> next(result)
(0, 0)
>>> next(result)
(1, 1)
Aber wir können nicht mit transpose_finite_iterable
um zur Struktur des Originals zurückzukehren iterable
denn result
ist eine unendliche Iterabilität von endlichen Iterabilien ( tuple
s in unserem Fall):
>>> transpose_finite_iterable(result)
... hangs ...
Traceback (most recent call last):
File "...", line 1, in ...
File "...", line 2, in transpose_finite_iterable
MemoryError
Wie können wir also mit diesem Fall umgehen?
... und hier kommt die deque
Nachdem wir einen Blick auf die Dokumente von itertools.tee
Funktion gibt es ein Python-Rezept, das mit einigen Änderungen in unserem Fall helfen kann
def transpose_finite_iterables(iterable):
iterator = iter(iterable)
try:
first_elements = next(iterator)
except StopIteration:
return ()
queues = [deque([element])
for element in first_elements]
def coordinate(queue):
while True:
if not queue:
try:
elements = next(iterator)
except StopIteration:
return
for sub_queue, element in zip(queues, elements):
sub_queue.append(element)
yield queue.popleft()
return tuple(map(coordinate, queues))
Prüfen wir
>>> from itertools import count
>>> iterable = [count(), count()]
>>> result = transpose_finite_iterables(transpose_finite_iterable(iterable))
>>> result
(<generator object transpose_finite_iterables.<locals>.coordinate at ...>, <generator object transpose_finite_iterables.<locals>.coordinate at ...>)
>>> next(result[0])
0
>>> next(result[0])
1
Synthese
Jetzt können wir eine allgemeine Funktion für die Arbeit mit Iterablen von Iterablen definieren, von denen eine endlich und eine andere potentiell unendlich ist, indem wir functools.singledispatch
Tapezierer wie
from collections import (abc,
deque)
from functools import singledispatch
@singledispatch
def transpose(object_):
"""
Transposes given object.
"""
raise TypeError('Unsupported object type: {type}.'
.format(type=type))
@transpose.register(abc.Iterable)
def transpose_finite_iterables(object_):
"""
Transposes given iterable of finite iterables.
"""
iterator = iter(object_)
try:
first_elements = next(iterator)
except StopIteration:
return ()
queues = [deque([element])
for element in first_elements]
def coordinate(queue):
while True:
if not queue:
try:
elements = next(iterator)
except StopIteration:
return
for sub_queue, element in zip(queues, elements):
sub_queue.append(element)
yield queue.popleft()
return tuple(map(coordinate, queues))
def transpose_finite_iterable(object_):
"""
Transposes given finite iterable of iterables.
"""
yield from zip(*object_)
try:
transpose.register(abc.Collection, transpose_finite_iterable)
except AttributeError:
# Python3.5-
transpose.register(abc.Mapping, transpose_finite_iterable)
transpose.register(abc.Sequence, transpose_finite_iterable)
transpose.register(abc.Set, transpose_finite_iterable)
die als ihre eigene Umkehrung betrachtet werden kann (Mathematiker nennen diese Art von Funktionen "Verwicklungen" ) in der Klasse der binären Operatoren über endlichen nicht-leeren Iterablen.
Als Bonus von singledispatch
die wir bewältigen können numpy
Arrays wie
import numpy as np
...
transpose.register(np.ndarray, np.transpose)
und verwenden Sie es dann wie
>>> array = np.arange(4).reshape((2,2))
>>> array
array([[0, 1],
[2, 3]])
>>> transpose(array)
array([[0, 2],
[1, 3]])
Hinweis
Seit transpose
gibt Iteratoren zurück und wenn jemand eine tuple
de list
s wie in OP -- dies kann zusätzlich gemacht werden mit map
eingebaute Funktion wie
>>> original = [('a', 1), ('b', 2), ('c', 3), ('d', 4)]
>>> tuple(map(list, transpose(original)))
(['a', 'b', 'c', 'd'], [1, 2, 3, 4])
Anzeige
Ich habe eine verallgemeinerte Lösung zu lz
Paket de 0.5.0
Version, die wie folgt verwendet werden kann
>>> from lz.transposition import transpose
>>> list(map(tuple, transpose(zip(range(10), range(10, 20)))))
[(0, 1, 2, 3, 4, 5, 6, 7, 8, 9), (10, 11, 12, 13, 14, 15, 16, 17, 18, 19)]
P.S.
Es gibt keine (zumindest offensichtliche) Lösung für den Umgang mit potenziell unendlichen Iterablen von potenziell unendlichen Iterablen, aber dieser Fall ist weniger häufig.
8 Stimmen
Tolle Antworten unten, aber sehen Sie sich auch an Numpys Transponieren
5 Stimmen
Siehe diese nette Antwort, um dasselbe mit Generatoren anstelle von Listen zu tun: wie-entpacke-einen-iterator
0 Stimmen
Warum wird zip als Transponierung bezeichnet?