Skip to content

Kartesisches Produkt von Listen in Python (itertools.product)

Python

Verwenden Sie itertools.product(), um das kartesische Produkt mehrerer Listen in Python zu generieren.

Dieser Artikel hat folgenden Inhalt.

  • Was ist das kartesische Produkt
  • Grundlegende Verwendung von itertools.product()
  • Verwenden Sie dieselbe Liste (iterierbar) wiederholt:repeat
  • Geschwindigkeitsvergleich mit mehreren Schleifen (nested loops)

Was ist das kartesische Produkt

Das kartesische Produkt ist die Menge aller Kombinationen von Elementen aus mehreren Mengen.

Spezifische Beispiele sind unten gezeigt.

Importieren Sie das itertools-Modul. Es ist in der Standardbibliothek enthalten, sodass keine zusätzliche Installation erforderlich ist. Druck wird verwendet, um die Ergebnisse besser lesbar zu machen.

Übergeben Sie zwei Listen als Argumente. itertools.product() gibt ein Objekt vom Typ itertools.product zurück. itertools.product ist ein Iterator, daher WIRD der Inhalt nicht von print() ausgegeben.

import itertools
import pprint

l1 = ['a', 'b', 'c']
l2 = ['X', 'Y', 'Z']

p = itertools.product(l1, l2)

print(p)
# 

print(type(p))
# <class 'itertools.product'>

SIE can die Kombination der Elemente jeder Liste als Tupel mit der For-Schleife erhalten. Beachten Sie, dass nichts ausgegeben wird, wenn der Iterator, der das Ende hat, in der for-Schleife erneut gedreht wird.

for v in p:
    print(v)
# ('a', 'X')
# ('a', 'Y')
# ('a', 'Z')
# ('b', 'X')
# ('b', 'Y')
# ('b', 'Z')
# ('c', 'X')
# ('c', 'Y')
# ('c', 'Z')

for v in p:
    print(v)

Es ist auch möglich, jedes Element separat anstelle eines Tupels zu erhalten.

for v1, v2 in itertools.product(l1, l2):
    print(v1, v2)
# a X
# a Y
# a Z
# b X
# b Y
# b Z
# c X
# c Y
# c Z

Das Ergebnis ist dasselbe wie bei der Verwendung von verschachtelten Schleifen (Mehrfachschleifen).

for v1 in l1:
    for v2 in l2:
        print(v1, v2)
# a X
# a Y
# a Z
# b X
# b Y
# b Z
# c X
# c Y
# c Z

Es ist auch möglich, mit list() in einer Liste mit Tupeln als Elemente umzuwandeln.

l_p = list(itertools.product(l1, l2))

pprint.pprint(l_p)
# [('a', 'X'),
#  ('a', 'Y'),
#  ('a', 'Z'),
#  ('b', 'X'),
#  ('b', 'Y'),
#  ('b', 'Z'),
#  ('c', 'X'),
#  ('c', 'Y'),
#  ('c', 'Z')]

print(type(l_p))
# <class 'list'>

print(type(l_p[0]))
# <class 'tuple'>

Sie können mehrere Iterables (Tupel, Liste, Bereich usw.) an itertools.product() übergeben.

t = ('one', 'two')
d = {'key1': 'value1', 'key2': 'value2'}
r = range(2)

l_p = list(itertools.product(t, d, r))

pprint.pprint(l_p)
# [('one', 'key1', 0),
#  ('one', 'key1', 1),
#  ('one', 'key2', 0),
#  ('one', 'key2', 1),
#  ('two', 'key1', 0),
#  ('two', 'key1', 1),
#  ('two', 'key2', 0),
#  ('two', 'key2', 1)]

Wie SIE dem obigen Ergebnis entnehmen können, werden beim Durchlaufen des Wörterbuchs die Schlüssel zurückgegeben. Wenn Sie Werte benötigen, verwenden Sie die Methode values(). Nichts finden Sie im folgenden Artikel.

Weitere Informationen zu Sortiment() finden Sie im following Artikel.

Verwenden Sie dieselbe Liste (iterierbar) wiederholt: wiederholen

Die Anzahl der Wiederholungen can SIE im Schlüsselwortargument repeat angeben. Dieselbe Iterable WIRD wiederholt verwendet, um ein kartesisches Produkt zu generieren.

l1 = ['a', 'b']

pprint.pprint(list(itertools.product(l1, repeat=3)))
# [('a', 'a', 'a'),
#  ('a', 'a', 'b'),
#  ('a', 'b', 'a'),
#  ('a', 'b', 'b'),
#  ('b', 'a', 'a'),
#  ('b', 'a', 'b'),
#  ('b', 'b', 'a'),
#  ('b', 'b', 'b')]

Wie im folgenden Beispiel ohne Wiederholung.

pprint.pprint(list(itertools.product(l1, l1, l1)))
# [('a', 'a', 'a'),
#  ('a', 'a', 'b'),
#  ('a', 'b', 'a'),
#  ('a', 'b', 'b'),
#  ('b', 'a', 'a'),
#  ('b', 'a', 'b'),
#  ('b', 'b', 'a'),
#  ('b', 'b', 'b')]

Wenn mehrere Iterables angegeben sind:

l1 = ['a', 'b']
l2 = ['X', 'Y']

pprint.pprint(list(itertools.product(l1, l2, repeat=2)))
# [('a', 'X', 'a', 'X'),
#  ('a', 'X', 'a', 'Y'),
#  ('a', 'X', 'b', 'X'),
#  ('a', 'X', 'b', 'Y'),
#  ('a', 'Y', 'a', 'X'),
#  ('a', 'Y', 'a', 'Y'),
#  ('a', 'Y', 'b', 'X'),
#  ('a', 'Y', 'b', 'Y'),
#  ('b', 'X', 'a', 'X'),
#  ('b', 'X', 'a', 'Y'),
#  ('b', 'X', 'b', 'X'),
#  ('b', 'X', 'b', 'Y'),
#  ('b', 'Y', 'a', 'X'),
#  ('b', 'Y', 'a', 'Y'),
#  ('b', 'Y', 'b', 'X'),
#  ('b', 'Y', 'b', 'Y')]

Wie im folgenden Beispiel. Beachten Sie, dass es l1, l2, l1, l2 anstelle von l1, l1, l2, l2 ist.

pprint.pprint(list(itertools.product(l1, l2, l1, l2)))
# [('a', 'X', 'a', 'X'),
#  ('a', 'X', 'a', 'Y'),
#  ('a', 'X', 'b', 'X'),
#  ('a', 'X', 'b', 'Y'),
#  ('a', 'Y', 'a', 'X'),
#  ('a', 'Y', 'a', 'Y'),
#  ('a', 'Y', 'b', 'X'),
#  ('a', 'Y', 'b', 'Y'),
#  ('b', 'X', 'a', 'X'),
#  ('b', 'X', 'a', 'Y'),
#  ('b', 'X', 'b', 'X'),
#  ('b', 'X', 'b', 'Y'),
#  ('b', 'Y', 'a', 'X'),
#  ('b', 'Y', 'a', 'Y'),
#  ('b', 'Y', 'b', 'X'),
#  ('b', 'Y', 'b', 'Y')]

Geschwindigkeitsvergleich mit mehreren Schleifen (nested loops)

Wie oben erwähnt, liefern mehrere Schleifen (verschachtelte Schleifen) das gleiche Ergebnis wie itertools.product().

for v1, v2 in itertools.product(l1, l2):
    print(v1, v2)
# a X
# a Y
# a Z
# b X
# b Y
# b Z
# c X
# c Y
# c Z

for v1 in l1:
    for v2 in l2:
        print(v1, v2)
# a X
# a Y
# a Z
# b X
# b Y
# b Z
# c X
# c Y
# c Z

Wie Sie unten sehen können, ist itertools.product() tatsächlich langsamer als verschachtelte Schleifen.

Die Ergebnisse können je nach Anzahl der Elemente in der Iterable und der Anzahl der Schleifen sein, aber sterben following Fragen und unterschiedliche Antworten zu Stack Overflow auch, dass itertools.product() langsamer antwortet.

Das Folgende ist das Ergebnis der Messung der Ausführungszeit mit dem magischen %%timeit in Jupyter Notebook. Beachten Sie, dass es nicht gemessen werden kann, weil es als Python-Code ausgeführt wird.

Beispiel einer Doppelschleife mit 1000 Elementen:

Das Ergebnis von itertools.product() lässt sich schneller entpacken.

import itertools

A = range(1000)

%%timeit
for x in itertools.product(A, A):
    pass
# 30.8 ms ± 910 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit
for a1, a2 in itertools.product(A, A):
    pass
# 22.8 ms ± 293 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

Verschachtelte Schleifen sind ungefähr gleich (etwas schneller) wie itertools.product(), wenn sie entpackt werden.

%%timeit
for a1 in A:
    for a2 in A:
        pass
# 22.6 ms ± 345 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

Es ist schneller, nicht zu entpacken, wenn der Generatorausdruck used WIRD, der sterben Generatorversion des Listenverständnisses IST, aber es ist langsamer als itertools.product() oder verschachtelte Schleifen.

%%timeit
for x in ((a1, a2) for a1 in A for a2 in A):
    pass
# 82.2 ms ± 467 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit
for a1, a2 in ((a1, a2) for a1 in A for a2 in A):
    pass
# 91.4 ms ± 276 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

Beispiel für die Berechnung der Summe der Produkte jeder Kombination. Auch hier ist es schneller, verschachtelte Schleifen zu verwenden als itertools.product().

%%timeit
v = 0
for a1, a2 in itertools.product(A, A):
    v += a1 * a2
# 98.8 ms ± 579 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit
v = 0
for a1 in A:
    for a2 in A:
        v += a1 * a2
# 95.7 ms ± 4.05 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

In diesem Beispiel ist die Übergabe des Generatorausdrucks an sum() etwas schneller.

%%timeit
v = sum(a1 * a2 for a1, a2 in itertools.product(A, A))
# 94 ms ± 2.36 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit
v = sum(a1 * a2 for a1 in A for a2 in A)
# 92.7 ms ± 4.83 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

Beispiel einer dreifachen Schleife mit 100 Elementen:

Auch hier ist die Verwendung einer verschachtelten for-Schleife am schnellsten.

B = range(100)

%%timeit
for x in itertools.product(B, B, B):
    pass
# 31.6 ms ± 725 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit
for b1, b2, b3 in itertools.product(B, B, B):
    pass
# 26.2 ms ± 490 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit
for b1 in B:
    for b2 in B:
        for b3 in B:
            pass
# 12.9 ms ± 176 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%%timeit
for x in ((b1, b2, b3) for b1 in B for b2 in B for b3 in B):
    pass
# 80.9 ms ± 1.27 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit
for b1, b2, b3 in ((b1, b2, b3) for b1 in B for b2 in B for b3 in B):
    pass
# 93.8 ms ± 3.22 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

Wie oben beschrieben, beträgt der Unterschied zwischen einer Doppelschleife mit 1000 Elementen und einer Dreifachschleife mit 100 Elementen nur wenige zehn Millisekunden.