Set — eine Sammlung ohne Duplikate
Duplikate entfernen, die vier Zeichen, die zwei Sammlungen vergleichen, warum {} kein leeres Set ist und was es bedeutet, keine Reihenfolge zu haben.
Wie möchtest du lernen?
Das Problem, das wir lösen
Die Verkäufe eines Tages kommen als Liste an, ein Eintrag pro Verkauf:
sold = ["pen", "bag", "pen", "ink", "pen"]Die Frage: wie viele verschiedene Produkte wurden verkauft? len(sold) sagt fünf, aber das ist die Anzahl der Verkäufe. Es waren drei verschiedene Produkte.
Die vorherigen Kapitel kommen damit zurecht — eine leere Liste anlegen und für jedes Element vor dem Hinzufügen prüfen if item not in unique:. Sechs Zeilen, und es funktioniert.
Aber wenn die Frage „welche Produkte wurden sowohl am Montag als auch am Dienstag verkauft?“ lautet, brauchst du zwei verschachtelte Schleifen. „Nur am Montag?“ — eine weitere. Diese Fragen sind so verbreitet, dass sie einen eigenen Zweig der Mathematik haben, und in Python lässt sich diese Mathematik direkt schreiben.
Ein Set ist der Container dafür: Es enthält keine Duplikate und beantwortet „in beiden“, „in dem einen, aber nicht in dem anderen“ mit einem einzigen Zeichen.
Am Ende dieses Kapitels kannst du
- Ein Set bauen und Duplikate aus einer Liste entfernen
- Zwei Sets mit
|,&,-und^vergleichen - Erklären, warum
{}kein leeres Set ist - Wissen, dass ein Set keine Reihenfolge hat und wann das wichtig wird
- Das Muster schreiben, das Duplikate entfernt und die Reihenfolge behält
Voraussetzungen: Dictionary — Paare aus Key und Wert.
Ein Set — eine Sammlung ohne Duplikate
sold = ["pen", "bag", "pen", "ink", "pen"]
unique = set(sold)
print(sorted(unique))
print(len(unique))['bag', 'ink', 'pen']
3set(...) nimmt eine Liste und wirft die Duplikate weg. Drei pen werden zu einem.
Beachte sorted() um das Ausgeben herum — das ist Absicht, und der Grund kommt gleich.
Du kannst ein Set auch direkt schreiben, mit geschweiften Klammern:
letters = {"a", "b", "a"}
print(sorted(letters))
print(len(letters))['a', 'b']
2Das zweite "a" ist stillschweigend verschwunden. Kein Fehler — ein Set kennt den Fall nicht, dass dasselbe Ding zweimal vorkommt.
{} ist kein leeres Set
not_a_set = {}
real_set = set()
print(type(not_a_set))
print(type(real_set))<class 'dict'>
<class 'set'>Geschweifte Klammern gehörten zuerst zu Dictionaries, also blieb ein leeres {} ein Dictionary. Für ein leeres Set musst du set() schreiben.
Der Fehler bleibt anfangs still: Starte mit {}, rufe .add() auf, und du bekommst einen AttributeError, der so aussieht, als wäre add das Problem.
Es gibt keine Reihenfolge
Das ist die wichtigste Einschränkung eines Sets und zugleich seine am meisten missverstandene Eigenschaft.
Die Dinge in einem Set haben keine Reihenfolge. Es gibt weder Indizes noch Slices:
tags = {"new", "sale"}
print(tags[0])TypeError: 'set' object is not subscriptableUnd die Reihenfolge, die du beim Ausgeben siehst, ist nicht zuverlässig — starte dasselbe Programm zweimal, und sie kann sich unterscheiden. Deshalb sortiert jedes Beispiel in diesem Kapitel vor dem Ausgeben: Was dir das direkte Ausgeben eines Sets liefert, ist beim nächsten Mal nicht garantiert dasselbe.
Der kürzeste Weg, Duplikate aus einer Liste zu entfernen, ist list(set(sold)) — und er wirft die ursprüngliche Reihenfolge weg. Wenn die Reihenfolge zählt, nimm ein Set und eine Liste zusammen:
sold = ["pen", "bag", "pen", "ink", "bag"]
seen = set()
unique = []
for item in sold:
if item not in seen:
seen.add(item)
unique.append(item)
print(unique)['pen', 'bag', 'ink']Die Liste behält die Reihenfolge, und das Set merkt sich, was schon vorkam. Dieses Paar ist ein sehr häufiges Muster.
Hinzufügen und Entfernen
tags = {"new"}
tags.add("sale")
tags.add("new")
print(sorted(tags))
tags.discard("gone")
tags.remove("sale")
print(sorted(tags))['new', 'sale']
['new']add legt ein Ding hinein — und wenn es schon da ist, passiert nichts und niemand beschwert sich. Das append einer Liste hätte eine zweite Kopie gemacht; ein Set nicht.
Entfernen gibt es in zwei Formen, und der Unterschied ist nützlich:
tags = {"new"}
tags.remove("gone")KeyError: 'gone'remove stoppt, wenn es das Ding nicht findet; discard tut stillschweigend nichts. Wenn das Ding da sein soll, schreib remove, damit sein Fehlen auffällt.
Zwei Sets vergleichen
Hier werden Sets wirklich nützlich:
monday = {"rafi", "ahmed", "bilal"}
tuesday = {"ahmed", "dia"}
print(sorted(monday | tuesday))
print(sorted(monday & tuesday))
print(sorted(monday - tuesday))
print(sorted(monday ^ tuesday))['ahmed', 'bilal', 'dia', 'rafi']
['ahmed']
['bilal', 'rafi']
['bilal', 'dia', 'rafi']Vier Zeichen, vier Fragen:
|— in einem der beiden (Vereinigung). Alle, die an einem der beiden Tage kamen.&— in beiden (Schnittmenge). Wer an beiden Tagen kam.-— im ersten, aber nicht im zweiten (Differenz). Wer nur am Montag kam.^— in genau einem (symmetrische Differenz). Wer an einem Tag kam, aber nicht an beiden.
Beachte: - achtet auf die Reihenfolge — monday - tuesday und tuesday - monday liefern verschiedene Antworten. Die anderen drei lesen sich in beide Richtungen gleich.
Diese vier mit Schleifen zu schreiben bräuchte für jede mehrere Zeilen — und die Chance, mindestens eine falsch zu haben, wäre groß.
Was hineinkommen darf
Dieselbe Regel wie bei Dictionary-Keys: Alles, was du hineinstellst, muss unveränderlich sein.
s = set()
s.add([1, 2])TypeError: unhashable type: 'list'Wort für Wort dieselbe Meldung, aus demselben Grund — ein Set benutzt darunter ebenfalls Hashing. Strings, Zahlen und Tupel gehen; Listen und Dictionaries nicht.
Ein vollständiges Beispiel
sales.py:
# Which products sold on both days, and which only on one
monday = ["pen", "bag", "pen", "ink"]
tuesday = ["ink", "bottle", "pen", "ink"]
mon = set(monday)
tue = set(tuesday)
print("Monday sold :", sorted(mon), f"({len(monday)} sales, {len(mon)} products)")
print("Tuesday sold :", sorted(tue), f"({len(tuesday)} sales, {len(tue)} products)")
print()
print("Either day :", sorted(mon | tue))
print("Both days :", sorted(mon & tue))
print("Monday only :", sorted(mon - tue))
print("Exactly one day:", sorted(mon ^ tue))
print()
print("Was ink sold on Monday?", "ink" in mon)Monday sold : ['bag', 'ink', 'pen'] (4 sales, 3 products)
Tuesday sold : ['bottle', 'ink', 'pen'] (4 sales, 3 products)
Either day : ['bag', 'bottle', 'ink', 'pen']
Both days : ['ink', 'pen']
Monday only : ['bag']
Exactly one day: ['bag', 'bottle']
Was ink sold on Monday? TrueDrei Dinge, die dir auffallen sollten.
Die ursprünglichen Listen bleiben erhalten. len(monday) meldet vier Verkäufe, len(mon) drei Produkte. Sobald die Listen in ein Set umgewandelt sind, ist die Zahl der Verkäufe weg — das Set ist also kein Ersatz für die Daten, sondern ein Werkzeug, um eine bestimmte Art von Frage daran zu stellen.
sorted() vor jedem Ausgeben. Ohne diese Sortierung würde die Reihenfolge beliebig aussehen und sich zwischen den Läufen ändern können.
Es steht "ink" in mon, nicht "ink" in monday. Beides liefert True, aber ein Set zu durchsuchen ist schneller als eine Liste — bei einer Liste vergleicht Python ein Element nach dem anderen, bei einem Set springt es per Hash direkt an die richtige Stelle. Bei vier Dingen ist das egal; bei vierzigtausend nicht.
Wenn etwas schiefgeht
AttributeError: 'dict' object has no attribute 'add' Jemand hat s = {} geschrieben und ein Set gemeint, aber das ist ein leeres Dictionary. Schreib s = set().
TypeError: 'set' object is not subscriptable Jemand hat s[0] geschrieben. Ein Set hat keine Reihenfolge, also keine Indizes. Wenn du ein bestimmtes Element brauchst, wolltest du wahrscheinlich eine Liste oder ein Dictionary; sonst liefert sorted(s) dir eine Liste.
KeyError: 'gone' remove sollte etwas entfernen, das das Set nicht enthält. Wenn beide Zustände normal sind, benutze discard.
TypeError: unhashable type: 'list' Ein Versuch, eine Liste in ein Set zu legen. Wandle sie mit tuple(...) um.
Die Reihenfolge war nach list(set(...)) durcheinander Das ist normal; Sets haben keine Reihenfolge. Wenn du sie brauchst, benutze das seen-Muster von oben oder sorted().
Ich habe zwei Sets mit == verglichen und bekam True, obwohl die Reihenfolge anders war Mit Recht. Zwei Sets sind gleich, wenn sie dasselbe enthalten — die Reihenfolge spielt keine Rolle. [1, 2] == [2, 1] ist False bei Listen; {1, 2} == {2, 1} ist True bei Sets.
Schritt 4 von 6 — Vorhersagen
Prüfe dein Verständnis
Zwei leere Container werden erzeugt. Was meldet type()?
not_a_set = {}
real_set = set()
print(type(not_a_set))
print(type(real_set))- A<class 'dict'> <class 'set'>
- B<class 'set'> <class 'set'>
- C<class 'set'> <class 'dict'>
- D<class 'dict'> <class 'dict'>
Welche zwei Fragen beantworten & und -?
monday = {"rafi", "ahmed", "bilal"}
tuesday = {"ahmed", "dia"}
print(sorted(monday & tuesday))
print(sorted(monday - tuesday))- A['bilal', 'rafi'] ['ahmed']
- B['ahmed', 'bilal', 'dia', 'rafi'] ['ahmed']
- C['ahmed'] ['bilal', 'rafi']
- D['ahmed'] ['dia']
Nach dem ersten Element eines Sets wird gefragt. Was passiert?
tags = {"new", "sale"}
print(tags[0])- Anew
- Bsale
- CEin `TypeError` — ein Set hat keine Reihenfolge, also keine Indizes
- DEin `KeyError`
Zum Antworten brauchst du ein Konto
Melde dich an, um deine Antworten zu prüfen
Die Fragen stehen oben, und sie im Kopf durchzuarbeiten ist der wichtige Teil. Melde dich an, um die Antworten, die Erklärungen und die dreistufigen Hinweise zu sehen.
Jetzt bist du dran
Schreib eine Datei namens visitors.py mit drei Listen von Besuchern, eine pro Tag, wobei einige Namen an mehr als einem Tag vorkommen.
Gib dann aus:
- Wie viele Besuche an jedem Tag und wie viele verschiedene Personen
- Wie viele verschiedene Personen über alle drei Tage hinweg
- Die Personen, die an allen drei Tagen kamen
- Die Personen, die nur am ersten Tag kamen
- Die Personen, die an genau einem der ersten beiden Tage kamen
Dann zwei Experimente:
- Entferne die Duplikate auf zwei Arten —
list(set(...))und dasseen-Muster von oben — und gib beide Ergebnisse nebeneinander aus. Was ist der Unterschied? - Versuch, ein leeres Set mit
{}zu erzeugen und.add()darauf aufzurufen. Welcher Fehler kommt zurück, und welchen Objekttyp nennt die Meldung?
Das zweite ist klein, und es einmal mit eigenen Augen zu sehen bedeutet, dass dir dieser Fehler nie wieder zehn Minuten kostet.
Schritt 6 von 6
Herausforderung — das Kapitel-Quiz
Zehn Fragen von leicht bis schwer. Die letzten sind absichtlich knifflig.
Melde dich an, um das Quiz zu machen