2.10. Mulțimi¶
O mulțime este o colecție neordonată de elemente unice. Adăugarea unei valori care este deja prezentă nu are niciun efect; iterarea produce fiecare valoare exact o dată. Mulțimile sunt instrumentul potrivit atunci când apartenența și eliminarea duplicatelor contează, iar ordonarea nu.
2.10.1. Crearea unei mulțimi¶
Folosește acolade pentru o mulțime nevidă sau set() pentru una vidă:
colours = {"red", "green", "blue"}
empty = set()
Acoladele arată ca un literal dict; {} de unul singur este un dict vid, nu o mulțime vidă – unul dintre accidentele istorice ale Python. Folosește set() pentru cazul vid.
set() construiește de asemenea o mulțime dintr-un iterabil oarecare, ceea ce reprezintă modul standard de a elimina duplicatele dintr-o secvență:
nums = [1, 2, 2, 3, 1, 4]
unique = set(nums)
print(unique)
Rezultat:
{1, 2, 3, 4}
Ordinea de afișare poate varia – mulțimile nu promit să itereze într-o anumită ordine.
2.10.2. Mulțime vs dict¶
Atât mulțimile, cât și dicționarele stochează elemente unice într-un tabel de dispersie. Diferența o face ceea ce poartă cu sine fiecare element:
Un
dictstochează perechi cheie-valoare. Căutarea unei chei returnează valoarea acesteia.O
setstochează doar elementele. Căutarea unui element îți spune dacă acesta există acolo.
Alegerea între cele două se reduce la dacă valoarea de lângă fiecare element înseamnă ceva:
Apelează la o mulțime când nicio valoare nu trebuie să stea lângă fiecare element – te interesează doar dacă elementul este prezent sau combini grupuri de elemente unice prin reuniune / intersecție.
Apelează la un dict când fiecare element este împerecheat cu date pe care căutarea trebuie să le recupereze – o hartă de configurare, un cache, un contor indexat după nume.
Cele două tipuri împărtășesc multă sintaxă de suprafață, de unde provine cea mai mare parte a confuziei. Diferențele într-un singur bloc:
set |
dict |
|
|---|---|---|
conține |
elemente unice |
chei unice, fiecare cu o valoare |
literal populat |
|
|
literal vid |
|
|
test de apartenență |
|
|
obține o valoare |
n/a |
|
adaugă un element |
|
|
iterare |
produce elemente |
produce chei (folosește |
Asimetria dintre literalii populați și cei vizi este capcana care merită semnalată:
Acoladele cu elemente în interior –
{1, 2, 3}– sunt un literal de mulțime; acoladele cu perechi cheie-valoare –{"a": 1}– sunt un literal de dict. Parserul le diferențiază după ceea ce se află în interior.Acoladele cu nimic în interior –
{}– sunt un dict vid, nu o mulțime vidă. Dicționarele au apărut primele; literalul vid le aparține. O mulțime vidă nu are deloc un literal cu acolade și trebuie scrisăset().
Un șablon comun atunci când doar cheile unui dict sunt vreodată citite este trecerea la o mulțime – aceasta face intenția evidentă și elimină valorile neutilizate din memorie.
2.10.3. Adăugare și eliminare¶
set.add()– inserează un element.set.discard()– elimină un element dacă este prezent, nu face nimic dacă nu este.set.remove()– elimină un element; declanșeazăKeyErrordacă lipsește.set.clear()– golește mulțimea.
s = {1, 2, 3}
s.add(4)
s.discard(99) # silent: 99 not in s
s.remove(2)
print(s)
Rezultat:
{1, 3, 4}
2.10.4. Apartenență¶
Operatorul in testează apartenența. Pe o mulțime, acesta are un timp aproximativ constant, indiferent de dimensiune – ceea ce este motivul principal pentru a alege o mulțime în locul unei list atunci când trebuie doar să întrebi „se află această valoare acolo”:
if "red" in colours:
print("colour is allowed")
O list cu același conținut ar scana de la început de fiecare dată, ceea ce este în regulă pentru zece elemente, dar lent pentru zece mii.
2.10.5. Operații pe mulțimi¶
Două mulțimi pot fi combinate cu operațiile matematice obișnuite. Fiecare are atât o formă de operator, cât și o formă de metodă:
a | bsaua.union(b)– tot ce se află în oricare dintre mulțimi.a & bsaua.intersection(b)– doar ceea ce apare în ambele.a - bsaua.difference(b)– înadar nu înb.a ^ bsaua.symmetric_difference(b)– într-una, dar nu în ambele.
a = {1, 2, 3, 4}
b = {3, 4, 5, 6}
print(a | b)
print(a & b)
print(a - b)
print(a ^ b)
Rezultat:
{1, 2, 3, 4, 5, 6}
{3, 4}
{1, 2}
{1, 2, 5, 6}
Formele de operator sunt doar pentru citire; formele de metodă acceptă orice iterabil în dreapta, nu doar o altă mulțime (a.union([5, 6])). Alege oricare se citește mai bine în context.
2.10.6. Ce poate intra într-o mulțime¶
Elementele unei mulțimi trebuie să fie hashable – aceeași constrângere ca pentru cheile dict. int, float, str, bool, bytes și tuple (când conținutul său este la rândul lui hashable) funcționează toate. list și dict nu; încercarea de a adăuga una declanșează TypeError.
2.10.7. frozenset¶
O set obișnuită este mutabilă: fiecare apel la add / remove / discard schimbă obiectul pe loc. Această mutabilitate o descalifică de la a fi hashable, așa că o mulțime nu poate fi folosită ca o cheie dict sau ca membru al altei mulțimi.
frozenset este omologul imuabil. Are aceleași căutări și operatori (in, |, &, -, ^) ca set, dar fără add / remove și fără metode care modifică. Deoarece nimic nu îi poate schimba vreodată conținutul, hash-ul unui frozenset este bine definit – așa că este hashable:
primary = frozenset({"red", "green", "blue"})
secondary = frozenset({"yellow", "purple", "orange"})
palettes = {
primary: "RGB",
secondary: "mixed",
}
print(palettes[primary])
Rezultat:
RGB
Construiește un frozenset dintr-un iterabil oarecare – frozenset() pentru cazul vid, frozenset(some_set) pentru a face un instantaneu imuabil al unei mulțimi existente:
snapshot = frozenset(s) # immutable copy of s
s.add("new") # snapshot does not change
Două motive comune pentru a apela la el:
Utilizare ca cheie de dict sau membru de mulțime. Oriunde o singură valoare nu poate captura ceea ce ai nevoie, un
frozensetde valori poate – „mulțimea de caracteristici suportate de acest driver”, „mulțimea de pini pe care îi folosește acest profil”.Blocarea unei constante. Un
frozensetla nivel de modul de nume permise nu poate fi modificat accidental de un apelant; osetobișnuită poate. Preferăfrozensetpentru orice este menit să fie doar pentru citire după construcție.