2.10. Sets¶
En mängd (set) är en oordnad samling av unika element. Att lägga till ett värde som redan finns har ingen effekt; iterering ger varje värde exakt en gång. Mängder är rätt verktyg när medlemskap och borttagning av dubbletter är viktigt, men ordning inte är det.
2.10.1. Skapa en mängd¶
Använd klammerparenteser för en icke-tom mängd, eller set() för en tom:
colours = {"red", "green", "blue"}
empty = set()
Klammerparenteserna ser ut som en dict-literal; {} för sig självt är en tom dict, inte en tom mängd – en av Pythons historiska olyckor. Använd set() för det tomma fallet.
set() bygger också en mängd från valfri itererbar, vilket är det vanliga sättet att ta bort dubbletter från en följd:
nums = [1, 2, 2, 3, 1, 4]
unique = set(nums)
print(unique)
Utdata:
{1, 2, 3, 4}
Utskriftsordningen kan variera – mängder lovar inte att iterera i någon särskild ordning.
2.10.2. Mängd kontra dict¶
Både mängder och dictionaries lagrar unika element i en hashtabell. Skillnaden är vad varje element bär med sig:
En
dictlagrar nyckel-värde-par. Att slå upp en nyckel returnerar dess värde.En
setlagrar enbart elementen. Att slå upp ett element talar om huruvida det finns där.
Valet mellan de två handlar om huruvida värdet bredvid varje element betyder något:
Välj en mängd när inget värde hör hemma bredvid varje element – du bryr dig bara om huruvida elementet finns, eller du kombinerar grupper av unika element med union/snitt.
Välj en dict när varje element är parat med data som uppslagningen är tänkt att hämta – en konfigurationskarta, en cache, en räknare indexerad med namn.
De två typerna delar mycket av ytsyntaxen, vilket är där det mesta av förvirringen kommer ifrån. Skillnaderna i ett block:
set |
dict |
|
|---|---|---|
rymmer |
unika element |
unika nycklar, var och en med ett värde |
fylld literal |
|
|
tom literal |
|
|
medlemskapstest |
|
|
hämta ett värde |
ej tillämpligt |
|
lägga till ett element |
|
|
iterera |
ger element |
ger nycklar (använd |
Asymmetrin mellan de fyllda och tomma literalerna är fallgropen värd att lyfta fram:
Klammerparenteser med element i sig –
{1, 2, 3}– är en mängdliteral; klammerparenteser med nyckel-värde-par –{"a": 1}– är en dict-literal. Parsern skiljer dem åt utifrån vad som finns inuti.Klammerparenteser med ingenting inuti –
{}– är en tom dict, inte en tom mängd. Dictionaries kom först; den tomma literalen tillhör dem. En tom mängd har ingen klammerliteral alls och måste skrivasset().
Ett vanligt mönster när endast nycklarna i en dict någonsin läses är att byta till en mängd – det gör avsikten uppenbar och rensar bort de oanvända värdena ur minnet.
2.10.3. Lägga till och ta bort¶
set.add()– infogar ett element.set.discard()– tar bort ett element om det finns, gör ingenting om det inte gör det.set.remove()– tar bort ett element; utlöserKeyErrorom det saknas.set.clear()– tömmer mängden.
s = {1, 2, 3}
s.add(4)
s.discard(99) # silent: 99 not in s
s.remove(2)
print(s)
Utdata:
{1, 3, 4}
2.10.4. Medlemskap¶
Operatorn in testar medlemskap. På en mängd är det ungefär konstant tid oavsett storlek – vilket är huvudskälet att välja en mängd framför en list när du bara behöver fråga ”finns detta värde där”:
if "red" in colours:
print("colour is allowed")
En list med samma innehåll skulle söka igenom från början varje gång, vilket är okej för tio element men långsamt för tiotusen.
2.10.5. Mängdoperationer¶
Två mängder kan kombineras med de vanliga matematiska operationerna. Var och en har både en operatorform och en metodform:
a | bellera.union(b)– allt i någon av mängderna.a & bellera.intersection(b)– endast det som förekommer i båda.a - bellera.difference(b)– iamen inte ib.a ^ bellera.symmetric_difference(b)– i den ena men inte i båda.
a = {1, 2, 3, 4}
b = {3, 4, 5, 6}
print(a | b)
print(a & b)
print(a - b)
print(a ^ b)
Utdata:
{1, 2, 3, 4, 5, 6}
{3, 4}
{1, 2}
{1, 2, 5, 6}
Operatorformerna är skrivskyddade; metodformerna accepterar valfri itererbar på höger sida, inte bara en annan mängd (a.union([5, 6])). Välj den som läser bäst i sammanhanget.
2.10.6. Vad som kan ligga i en mängd¶
Element i en mängd måste vara hashbara – samma begränsning som för nycklar i dict. int, float, str, bool, bytes och tuple (när dess innehåll själv är hashbart) fungerar alla. list och dict gör det inte; att försöka lägga till en sådan utlöser TypeError.
2.10.7. frozenset¶
En vanlig set är muterbar: varje anrop till add / remove / discard ändrar objektet på plats. Den muterbarheten diskvalificerar den från att vara hashbar, så en mängd kan inte användas som en nyckel i dict eller som medlem i en annan mängd.
frozenset är den oföränderliga motsvarigheten. Den har samma uppslagningar och operatorer (in, |, &, -, ^) som set, men inget add / remove och inga metoder som muterar. Eftersom ingenting någonsin kan ändra dess innehåll är hashen för en frozenset väldefinierad – så den är hashbar:
primary = frozenset({"red", "green", "blue"})
secondary = frozenset({"yellow", "purple", "orange"})
palettes = {
primary: "RGB",
secondary: "mixed",
}
print(palettes[primary])
Utdata:
RGB
Konstruera en frozenset från valfri itererbar – frozenset() för det tomma fallet, frozenset(some_set) för att ta en oföränderlig stillbild av en befintlig mängd:
snapshot = frozenset(s) # immutable copy of s
s.add("new") # snapshot does not change
Två vanliga skäl att ta till den:
Använd som nyckel i en dict eller medlem i en mängd. Överallt där ett enskilt värde inte kan fånga det du behöver kan en
frozensetav värden göra det – ”mängden av särdrag som denna drivrutin stöder”, ”mängden av stift som denna profil använder”.Lås fast en konstant. En
frozensetpå modulnivå av tillåtna namn kan inte muteras av misstag av en anropare; en vanligsetkan det. Föredrafrozensetför allt som är tänkt att vara skrivskyddat efter konstruktion.