Mer om comprehensions
Listor är kanske det vanligaste målet för comprehensions, men comprehensions fungerar på alla serier av föremål, inklusive strängar. Liksom listexemplen i föregående avsnitt, ifall en list comprehension utförs på en sträng, plockas föremålen (dvs. tecknen) i strängen en efter en, bearbetas enligt det givna uttrycket och lagras i en lista.
namn = "Peter Python"
stora_bokstaver = [tecken.upper() for tecken in namn]
print(stora_bokstaver)
['P', 'E', 'T', 'E', 'R', ' ', 'P', 'Y', 'T', 'H', 'O', 'N']
Resultatet är en lista, vilket dikteras av parentesnotationen runt comprehension-satsen. Om vi ville ha en sträng istället skulle vi kunna använda strängmetoden join
för att tolka listan till en sträng. Kom ihåg att metoden anropas på den sträng som vi vill använda som "lim" mellan tecknen. Låt oss ta en titt på några exempel:
namn = "Peter"
lista = list(namn)
print(lista)
print("".join(lista))
print(" ".join(lista))
print(",".join(lista))
print(" och ".join(lista))
['P', 'e', 't', 'e', 'r'] Peter P e t e r P,e,t,e,r P och e och t och e och r
List comprehensions och join
-metoden gör det enkelt att skapa nya strängar baserade på andra strängar. Vi kan t.ex. skapa en sträng som bara innehåller vokalerna från en annan sträng:
teststrang = "Halloj allihopa, det här är ett test"
vokaler = [tecken for tecken in teststrang if tecken in "aeiouyåäö"]
nystrang = "".join(vokaler)
print(nystrang)
aoaioaeääee
I exemplet ovan står list comprehension och join
-metoden på separata rader, men de kan kombineras till ett enda uttryck:
teststrang = "Halloj allihopa, det här är ett test"
vokalstrang = "".join([tecken for tecken in teststrang if tecken in "aeiouyåäö"])
print(vokalstrang)
Många Python-programmerare står trogna vid dessa oneliners, så det är väl värt besväret att lära sig läsa dem. Vi kan till och med lägga till split
-metoden i mixen, så att vi kan bearbeta hela meningar effektivt med ett enda uttalande. I exemplet nedan tas det första tecknet från varje ord i en mening bort:
mening = "Sju sjösjuka sjömän på skeppet Shang Hai."
mening_utan_initialer = " ".join([ord[1:] for ord in mening.split()])
print(mening_utan_initialer)
ju jösjuka jömän å keppet hang ai
Låt oss gå igenom detta steg för steg:
ord[1:]
extraherar en delsträng från det andra tecknet (vid index 1) och framåtmening.split()
delar upp meningen i avsnitt vid det angivna tecknet. I det här fallet ges inget argument till metoden, så meningen delas upp vid mellanslagstecken som standard" ".join()
kombinerar föremålen i listan till en ny sträng med ett mellanslag mellan föremålen
En mer traditionell iterativ metod skulle kunna se ut så här:
mening = "Sju sjösjuka sjömän på skeppet Shang Hai."
ordlista = []
orden = mening.split()
for ord in orden:
ord_utan_initialer = ord[1:]
ordlista.append(ord_utan_initialer)
mening_utan_initialer = " ".join(ordlista)
print(mening_utan_initialer)
Egna klasser och comprehensions
Comprehensions kan vara ett användbart verktyg för att bearbeta eller formulera instanser av dina egna klasser, vilket vi kommer att se i följande exempel.
Låt oss först ta en titt på klassen Land
som är en enkel modell för ett enda land, med attribut för namn och befolkning. I huvudfunktionen nedan skapar vi först några Land-objekt och använder sedan en list comprehension för att bara välja dem vars befolkning är större än fem miljoner.
class Land:
""" Denna klass modellerar ett enkelt land med befolkning """
def __init__(self, namn: str, befolkningsmangd: int):
self.namn = namn
self.befolkningsmangd = befolkningsmangd
if __name__ == "__main__":
finland = Land("Finland", 6000000)
malta = Land("Malta", 500000)
sverige = Land("Sverige", 10000000)
island = Land("Island", 350000)
lander = [finland, malta, sverige, island]
storre_land = [land.namn for land in lander if land.befolkningsmangd > 5000000]
for land in storre_land:
print(land)
Finland Sverige
I list comprehension ovan valde vi bara namnattributet från Land-objekten, så innehållet i listan kunde skrivas ut direkt. Vi skulle också kunna skapa en ny lista med länderna och komma åt namnattributet i for
-loopen. Detta skulle vara användbart om samma lista med länder skulle användas senare i programmet, eller om vi behövde befolkningsattributet i for
-loopen också:
if __name__ == "__main__":
finland = Land("Finland", 6000000)
malta = Land("Malta", 500000)
sverige = Land("Sverige", 10000000)
island = Land("Island", 350000)
lander = [finland, malta, sverige, island]
storre_land = [land for land in lander if land.befolkningsmangd > 5000000]
for land in storre_land:
print(land.namn)
I nästa exempel har vi en klass som heter Fotlopp
som modellerar ett enskilt lopp med attribut för loppets längd och namn. Vi kommer att använda list comprehension för att skapa Fotlopp
-objekt baserat på en lista med tävlingslängder.
Parametern namn
har ett standardvärde i konstruktorn för Fotlopp
-klassen, vilket är varför vi inte behöver skicka namnet som ett argument.
class Fotlopp:
""" Klassen modellerar ett fotloppsevenemang med längden n meter """
def __init__(self, stracka:int, namn:str = "inget namn"):
self.stracka = stracka
self.namn = namn
def __repr__(self):
return f"{self.stracka} m. ({self.namn})"
if __name__ == "__main__":
langder = [100, 200, 1500, 3000, 42195]
strackor = [Fotlopp(langd) for langd in langder]
# Skriv ut alla
print(strackor)
# Ta en från listan och ge den ett namn
maraton = strackor[-1] # sista föremålet i listan
maraton.namn = "Maraton"
# Skriv ut alla igen, inkluderandes det nya namnet
print(strackor)
[100 m. (inget namn), 200 m. (inget namn), 1500 m. (inget namn), 3000 m. (inget namn), 42195 m. (inget namn)] [100 m. (inget namn), 200 m. (inget namn), 1500 m. (inget namn), 3000 m. (inget namn), 42195 m. (Maraton)]
Låt oss nu ta reda på vad som gör en serie objekt "begripliga" (“comprehendible”). I föregående del lärde vi oss hur vi kan göra våra egna klasser itererbara. Det är exakt samma funktion som också möjliggör list comprehension. Om din egen klass är itererbar kan den användas som grund för en list comprehension. Följande klassdefinitioner är kopierade direkt från modul 10:
class Bok:
def __init__(self, namn: str, forfattare: str, sidor: int):
self.namn = namn
self.forfattare = forfattare
self.sidor = sidor
class Bokhylla:
def __init__(self):
self._bocker = []
def tillsatt_bok(self, bok: Bok):
self._bocker.append(bok)
# Iteratorns initialiseringsmetod
# Här bör iterationsvariabeln(variablerna) initialiseras
def __iter__(self):
self.n = 0
# Metoden returnerar en referens till själva objektet
# eftersom iteratorn är implementerad inom samma klassdefinition
return self
# Denna metod returnerar nästa föremål inom objektet
# Om alla föremål har genomgåtts åstadkomms StopIteration
def __next__(self):
if self.n < len(self._bocker):
# Ta det aktuella föremålet från listan i objektet
bok = self._bocker[self.n]
# Öka räknaren med ett
self.n += 1
# ...och returnera föremålet
return bok
else:
# Inga fler böcker
raise StopIteration
# Testar
if __name__ == "__main__":
b1 = Bok("Livet av en Python", "Peter Python", 123)
b2 = Bok("Den gamle och Java", "Ernest Hemingjava", 204)
b3 = Bok("C-värdheter på nätet", "Karl Kodare", 997)
hylla = Bokhylla()
hylla.tillsatt_bok(b1)
hylla.tillsatt_bok(b2)
hylla.tillsatt_bok(b3)
# Skapa en lista innehållandes namnet på alla böcker
bockernas_namn = [bok.namn for bok in hylla]
print(bockernas_namn)
Comprehensions och ordlistor
Det finns inget i sig "list-aktigt" med comprehensions. Resultatet är en lista eftersom comprehension-satsen är inkapslad i hakparenteser, som indikerar en Python-lista. Förståelser fungerar lika bra med Python-ordlistor om du använder rundparenteser istället. Kom dock ihåg att ordlistor kräver nyckel-värde-par. Båda måste anges när en ordlista skapas, även när det gäller comprehensions.
Grunden för en comprehension kan vara vilken itererbar serie som helst, vare sig det är en lista, en sträng, en tupel, en ordlista, någon av dina egna itererbara klasser och så vidare.
I följande exempel använder vi en sträng som bas för en ordlista. Ordlistan innehåller alla unika tecken i strängen, tillsammans med antalet gånger de förekommer:
mening = "Hej alla"
tecken_antal = {bokstav : mening.count(bokstav) for bokstav in mening}
print(tecken_antal)
{'H': 1, 'e': 1, 'j': 1, ' ': 1, 'a': 2, 'l': 2}
Principen för comprehension-satsen är exakt densamma som för listor, men i stället för ett enda värde består uttrycket nu av en nyckel och ett värde. Den allmänna syntaxen ser ut så här:
{<nyckeluttryck> : <värdeuttryck> för <föremål> i <serie>}
Som avslutning på det här avsnittet tittar vi på faktorialtal igen. Den här gången lagrar vi resultaten i en ordlista. Själva talet är nyckeln, medan värdet är resultatet av faktorn från vår funktion:
def fakultet(n: int):
""" Funktionen beräknar fakulteten n! för positiva heltal """
k = 1
while n >= 2:
k *= n
n -= 1
return k
if __name__ == "__main__":
lista = [-2, 3, 2, 1, 4, -10, 5, 1, 6]
fakultett = {tal : fakultet(tal) for tal in lista if tal > 0}
print(fakulteter)
{3: 6, 2: 2, 1: 1, 4: 24, 5: 120, 6: 720}
Se dina poäng genom att klicka på cirkeln nere till höger av sidan.