Mengen
Einführung
Auch wenn die Mengenlehre über lange Zeit heftig kritisiert wurde und immer noch kritisiert wird, ist sie ein wesentliches Gebiet der Mathematik. Die heutige Mathematik ist in der Terminologie der Mengenlehre formuliert und baut auf deren Axiomen auf. Die Mengenlehre ist noch ein recht junges Gebiet der Mathematik. Der deutsche Mathematiker Georg Cantor (1845 - 1918) begründete die Mengenlehre mit seinem 1874 erschienenen Artikel "Über eine Eigenschaft des Inbegriffes aller reellen algebraischen Zahlen". Anfangs also bis ins Jahr 1877 bezeichnete er übrigens noch die Mengenlehre als "Mannigfaltigkeitslehre".
1895 gab er folgende Definition einer Menge: "Unter einer 'Menge' verstehen wir jede Zusammenfassung M von bestimmten wohlunterschiedenen Objekten m unserer Anschauung oder unseres Denkens (welche die 'Elemente' von M genannt werden) zu einem Ganzen."
Eine Menge kann beliebige Elemente enthalten: zum Beispiel Zahlen, Zeichen, Buchstaben, Wörter, Namen oder sogar andere Mengen. Eine Menge wird in der Mathematik üblicherweise mit einem Großbuchstaben bezeichnet.
Mengen in Python
Der Datentyp "set", der ein sogenannter "collection"-Typ ist, ist in Python seit Version 2.4. enthalten. Ein set enthält eine ungeordnete Sammlung von einmaligen und unveränderlichen Elementen. In anderen Worten: Ein Element kann in einem set-Objekt nicht mehrmals vorkommen, was bei Listen und Tupel jedoch möglich ist. Beim Datentyp set handelt es sich um die Python-Implementierung von Mengen, wie sie aus der Mathematik bekannt sind.
Sets erzeugen
Will man eine Menge erzeugen, so ist dies in Python3 sehr einfach. Man bedient sich der gewohnten mathematischen Schreibweise.
staedte = {'Hamburg', 'München', 'Frankfurt', 'Berlin'}
print(staedte)
'Berlin' in staedte
'Köln' in staedte
Wenn man sich das obige Beispiel einer Menge genau anschaut, dann wundert man sich sicherlich, dass die Reihenfolge der Städte durch print anders ausgegeben wird, als man die Städte bei der Definition der Menge angegeben hat. Das liegt daran, dass Mengen allgemein und auch bei der Programmiersprache Python eine ungeordnete Sammlung von verschiedenen Objekten enthalten. Es gibt also keine Reihenfolge bei Mengen, und deshalb kann die Ausgabe durch print beliebig sein. Mehr dazu finden Sie im letzten Abschnitt dieses Kapitels. Die Elemente dürfen in Python aber nur unveränderliche Typen sein, d.h. sie dürfen beispielsweise keine Listen sein. Wir demonstrieren dies im folgenden Beispiel, in dem wir versuchen eine Menge anzulegen, die Zweierlisten mit Städten und deren Einwohnerzahlen (Stand 2013) enthalten soll:
staedte = { ['Berlin', 3.42], ['Hamburg', 1.75], ['München', 1.41], ['Köln', 1.03], ['Frankfurt', 0.70], ['Stuttgart', 0.60]}
Nimmt man Tupel (unveränderlich) statt Listen (veränderlich) als Elemente, dann funktioniert es:
staedte = {('Frankfurt', 0.7), ('Stuttgart', 0.6), ('Hamburg', 1.75), ('Berlin', 3.42), ('Köln', 1.03), ('München', 1.41)}
Noch eine Anmerkung: Diejenigen, die Dictionaries schon kennen oder bereits unser Kapitel über Dictionaries durchgearbeitet haben, mögen sich vielleicht wundern, dass wir die geschweiften Klammern auch für Mengen verwenden.
Neben dem Datentyp "set" gibt es auch eine Funktion "set()". Mit der Funktion set() kann man sequentielle Datentypen, also zum Beispiel Listen, in Mengen wandeln. Im folgenden kleinen Beispiel definieren wir eine Liste von Wörtern, die wir mittels der set()-Funktion in eine Menge wandeln:
liste_von_woertern = ["gut", "hilfreich", "besser", "optimal"]
menge_von_woertern = set(liste_von_woertern)
menge_von_woertern
Häufig wird die set-Funktion angewendet, um aus Listen oder anderen sequentiellen Datentypen mehrfache Vorkommen zu entfernen. Wir wollen dies in einem Beispiel mit einer Liste von Städten aus der Schweiz, Österreich und Deutschland zeigen. In dieser Liste erscheint die Stadt Zürich zweimal. In der erzeugten Menge erscheint sie wie erwartet nur noch einmal, da es in einer Menge keine mehrfachen Vorkommen geben kann:
liste_von_staedten = ["Frankfurt", "Zürich", "Bern", "Stuttgart", "Freiburg", "Ulm", "Hamburg", "München", "Nürnberg", "Zürich", "Bregenz", "Salzburg", "Wien"]
liste_von_staedten
menge_von_staedten = set(liste_von_staedten)
menge_von_staedten
Anschließend kann man dann wieder die Menge in eine Liste wandeln, falls man eine Liste als Datentyp braucht:
liste_von_staedten = list(menge_von_staedten)
liste_von_staedten
Wie wir sehen können, hat diese Vorgehensweise einen kleinen Schönheitsfehler. Mehrfache Vorkommen sind zwar nun nicht mehr in der Ergebnisliste enthalten, aber die ursprüngliche Reihenfolge ist in der Regel nicht mehr vorhanden.
Wir kommen nun zu einem weiteren Anwendungsfall der set-Funktion. Im folgenden Beispiel benutzen wir sie, um einen String in seine Zeichen zu vereinzeln:
x = set("Ein gutes Python-Tutorial")
x
type(x)
Städte = set((("Python","Perl"), ("Paris", "Berlin", "London")))
Städte = set((["Python","Perl"], ["Paris", "Berlin", "London"]))
Städte = {"Frankfurt", "Basel","Freiburg"}
Städte.add("Straßburg")
Städte
Frozensets sind wie sets, aber sie können nicht verändert werden. Sie sind also unveränderlich (immutable):
Städte = frozenset(["Frankfurt", "Basel","Freiburg"])
Städte.add("Straßburg")
Farben = {"rot","grün"}
Farben.add("gelb")
Farben
Farben.add(["schwarz","weiß"])
Selbstverständlich wird ein Objekt nur dann als neues Element eingefügt, wenn es noch nicht enthalten ist. Ist es bereits enthalten, hat der Aufruf der Methode keine Auswirkungen.
clear-Methode
Alle Elemente einer Menge werden entfernt. Die Menge ist also anschließend leer, wie wir im folgenden Beispiel sehen:
cities = {"Stuttgart", "Konstanz", "Freiburg"}
cities.clear()
cities
more_cities = {"Winterthur","Schaffhausen","St. Gallen"}
cities_backup = more_cities.copy()
more_cities.clear()
cities_backup
Nur für diejenigen, die glauben, dass eine einfache Zuweisung auch genügen könnte:
more_cities = {"Winterthur", "Schaffhausen", "St. Gallen"}
cities_backup = more_cities
more_cities.clear()
cities_backup
x = {"a","b","c","d","e"}
y = {"b","c"}
z = {"c","d"}
x.difference(y)
x.difference(y).difference(z)
Statt die Methode "difference" zu benutzen, hätten wir auch den Operator "-" benutzen können:
x - y
x - y - z
x = {"a","b","c","d","e"}
y = {"b","c"}
x.difference_update(y)
x
x = {"a","b","c","d","e"}
y = {"b","c"}
x = x - y
x
x = {"a","b","c","d","e"}
x.discard("a")
x
x.discard("z")
x
x = {"a","b","c","d","e"}
x.remove("a")
x
x.remove("z")
x
x = {"a","b","c","d","e"}
y = {"c","d","e","f","g"}
x.union(y)
Um die Vereinigen zu bilden kann man auch das Pipe-Zeichen "|" als Operator verwenden:
x = {"a","b","c","d","e"}
y = {"c","d","e","f","g"}
x | y
x = {"a","b","c","d","e"}
y = {"c","d","e","f","g"}
x.intersection(y)
Dies kann auch mit dem "&"-Zeichen formuliert werden:
x = {"a","b","c","d","e"}
y = {"c","d","e","f","g"}
x & y
x = {"a","b","c"}
y = {"c","d","e"}
x.isdisjoint(y)
x = {"a","b","c"}
y = {"d","e","f"}
x.isdisjoint(y)
x = {"a","b","c","d","e"}
y = {"c","d"}
x.issubset(y)
y.issubset(x)
x < y
y < x # y ist eine echte Untermenge von x
x < x # eine Menge kann nie eine echte Untermenge ihrer selbst sein.
x <= x
x = {"a","b","c","d","e"}
y = {"c","d"}
x.issuperset(y)
x > y
x >= y
x >= x
x > x
x.issuperset(x)
x = {"a","b","c","d","e"}
x.pop()
x.pop()
x.pop()
x.pop()
x.pop()
Auch wenn "pop" die Elemente in der Reihenfolge der internen Repräsentierung der Menge zurückliefert, so stimmt dennoch die Formulierung "pop() liefert ein beliebiges Element der Menge zurück." Die Menge die wir im "Kopf" haben, lautet ja im obigen Beispiel {"a","b","c","d","e"} und die Reihenfolge in der pop() die Elemente zurückliefert ist 'd', 'e', 'a', 'b', 'c'. Dass diese Reihenfolge für diese Menge nicht immer gleich ist, kann man in der folgenden neuen interaktiven Python-Sitzung sehen:
x = {"a", "b", "c", "d", "e", "f"}
x.pop()
x.pop()
x.pop()
x.pop()
x.pop()
Beliebige oder zufällige Reihenfolge
Wir haben gelernt, dass es bei Mengen keine festgelegte Reihenfolge für die Elemente gibt und dass man auch keine solche Reihenfolge definieren kann. Wie wir anfangs gesagt haben ist die Reihenfolge bei Mengen beliebig. "Beliebig" darf man aber nicht mit "zufällig" verwechseln. Prinzipiell könnte es beispielsweise sein, dass Python die Elemente einer bestimmten Menge immer in der gleichen Reihenfolge intern abspeichert, was man dann bei der Ausgabe der Menge sehen kann. Dies ist aber nicht so. Die Reihenfolge hängt ab von einem Hash, einer speziellen Funktion, die bei Dictionaries und Mengen verwendet wird. Bei verschiedenen Programmläufen kann dies dann zu unterschiedlichen Repräsentationen der Menge führen. Wir demonstrieren dies im folgenden Beispiel:
x = {"a","b","c","d","e"}
x
staedte = {'Hamburg', 'München', 'Frankfurt', 'Berlin'}
x = {"a","b","c","d","e"}
x
staedte = ['Hamburg', 'München', 'Frankfurt', 'Berlin']
x = {"a","b","c","d","e"}
x