2.10. Množiny

Množina je neuspořádaná kolekce jedinečných položek. Přidání hodnoty, která je již přítomna, nemá žádný efekt; iterace poskytuje každou hodnotu právě jednou. Množiny jsou tím správným nástrojem, když záleží na členství a odstranění duplicit a nezáleží na pořadí.

2.10.1. Vytvoření množiny

Pro neprázdnou množinu použijte složené závorky, nebo set() pro prázdnou:

colours = {"red", "green", "blue"}
empty = set()

Složené závorky vypadají jako literál dict; samotné {} je prázdný slovník, nikoli prázdná množina – jedna z historických nehod Pythonu. Pro prázdný případ použijte set().

set() také vytvoří množinu z libovolného iterovatelného objektu, což je standardní způsob odstranění duplicit z posloupnosti:

nums = [1, 2, 2, 3, 1, 4]
unique = set(nums)
print(unique)

Výstup:

{1, 2, 3, 4}

Pořadí výpisu se může lišit – množiny neslibují iteraci v žádném konkrétním pořadí.

2.10.2. Množina vs slovník

Množiny i slovníky ukládají jedinečné položky v hashovací tabulce. Rozdíl je v tom, co každá položka nese s sebou:

  • dict ukládá dvojice klíč-hodnota. Vyhledání klíče vrátí jeho hodnotu.

  • set ukládá pouze položky. Vyhledání položky vám řekne, zda tam je.

Volba mezi těmito dvěma je o tom, zda hodnota vedle každé položky něco znamená:

  • Sáhněte po množině, když k položce nepatří žádná hodnota – záleží vám pouze na tom, zda je položka přítomna, nebo kombinujete skupiny jedinečných položek pomocí sjednocení / průniku.

  • Sáhněte po slovníku, když je každá položka spárována s daty, která má vyhledání získat – konfigurační mapa, mezipaměť, čítač indexovaný jménem.

Tyto dva typy sdílejí hodně povrchové syntaxe, odkud pochází většina zmatku. Rozdíly v jednom bloku:

množina

slovník

obsahuje

jedinečné položky

jedinečné klíče, každý s hodnotou

naplněný literál

{1, 2, 3}

{"a": 1, "b": 2}

prázdný literál

set()

{}

test členství

x in s

k in d (pouze klíče)

získání hodnoty

nelze

d[k]

přidání položky

s.add(x)

d[k] = v

iterace

poskytuje položky

poskytuje klíče (pro dvojice použijte d.items())

Asymetrie mezi naplněnými a prázdnými literály je záludnost, kterou stojí za to zdůraznit:

  • Složené závorky s položkami uvnitř{1, 2, 3} – jsou literál množiny; složené závorky s dvojicemi klíč-hodnota{"a": 1} – jsou literál slovníku. Parser je rozliší podle toho, co je uvnitř.

  • Složené závorky s ničím uvnitř{} – jsou prázdný slovník, nikoli prázdná množina. Slovníky byly první; prázdný literál patří jim. Prázdná množina nemá žádný literál se závorkami a musí se zapsat jako set().

Běžným vzorem, když se ze slovníku kdy čtou pouze klíče, je přejít na množinu – to činí záměr zřejmým a odstraňuje nevyužité hodnoty z paměti.

2.10.3. Přidávání a odebírání

s = {1, 2, 3}
s.add(4)
s.discard(99)            # silent: 99 not in s
s.remove(2)
print(s)

Výstup:

{1, 3, 4}

2.10.4. Členství

Operátor in testuje členství. U množiny je to zhruba konstantní čas bez ohledu na velikost – což je hlavní důvod, proč zvolit množinu před list, když potřebujete pouze zjistit „je tato hodnota tam“:

if "red" in colours:
    print("colour is allowed")

list se stejným obsahem by procházel od začátku pokaždé, což je v pořádku pro deset položek, ale pomalé pro deset tisíc.

2.10.5. Operace s množinami

Dvě množiny lze kombinovat pomocí obvyklých matematických operací. Každá má jak operátorovou, tak metodovou formu:

  • a | b nebo a.union(b) – vše v kterékoli množině.

  • a & b nebo a.intersection(b) – pouze to, co se vyskytuje v obou.

  • a - b nebo a.difference(b) – v a, ale ne v b.

  • a ^ b nebo a.symmetric_difference(b) – v jedné, ale ne v obou.

a = {1, 2, 3, 4}
b = {3, 4, 5, 6}
print(a | b)
print(a & b)
print(a - b)
print(a ^ b)

Výstup:

{1, 2, 3, 4, 5, 6}
{3, 4}
{1, 2}
{1, 2, 5, 6}

Operátorové formy jsou jen pro čtení; metodové formy přijímají na pravé straně libovolný iterovatelný objekt, nejen jinou množinu (a.union([5, 6])). Vyberte si tu, která se v daném kontextu lépe čte.

2.10.6. Co může jít do množiny

Prvky množiny musí být hashovatelné – stejné omezení jako u klíčů dict. int, float, str, bool, bytes a tuple (pokud je jeho obsah sám hashovatelný) všechny fungují. list a dict nikoli; pokus o přidání některého z nich vyvolá TypeError.

2.10.7. frozenset

Běžná set je měnitelná: každé volání add / remove / discard mění objekt na místě. Tato měnitelnost ji diskvalifikuje z hashovatelnosti, takže množina nemůže být použita jako klíč dict ani jako člen jiné množiny.

frozenset je neměnný protějšek. Má stejná vyhledávání a operátory (in, |, &, -, ^) jako set, ale žádné add / remove a žádné metody, které mutují. Protože jeho obsah se nikdy nemůže změnit, je hash frozenset dobře definovaný – takže je hashovatelný:

primary = frozenset({"red", "green", "blue"})
secondary = frozenset({"yellow", "purple", "orange"})

palettes = {
    primary: "RGB",
    secondary: "mixed",
}

print(palettes[primary])

Výstup:

RGB

Vytvořte frozenset z libovolného iterovatelného objektu – frozenset() pro prázdný případ, frozenset(some_set) pro pořízení neměnného snímku existující množiny:

snapshot = frozenset(s)         # immutable copy of s
s.add("new")                    # snapshot does not change

Dva běžné důvody, proč po něm sáhnout:

  • Použití jako klíč slovníku nebo člen množiny. Všude, kde jediná hodnota nedokáže zachytit to, co potřebujete, dokáže to frozenset hodnot – „množina funkcí podporovaných tímto ovladačem“, „množina pinů, které tento profil používá“.

  • Uzamčení konstanty. Konstantu frozenset povolených jmen na úrovni modulu nemůže volající nechtěně zmutovat; běžnou set ano. Pro cokoli, co má být po vytvoření jen pro čtení, dejte přednost frozenset.