Detta skapar en PDF som du sedan kan skriva ut. Du kan även spara ner PDFn och skriva ut senare.
Titel på utskriften?
Tack för ditt bidrag
Om vi kan använda det så lägger vi upp det på sidan. Nedan en länk till ditt bidrag om du vill spara det.
Spara som ...
Du sparar ditt skript under detta namn och kan sedan hämta tillbaka det med samma namn.
Läs in
Läs in ett tidigare sparat skript. Obs att du enbart kan läsa in skript i den webbläsare där du sparade skriptet. Vill du kunna läsa in och spara skript oberoende av webbläsare, så behöver du skaffa ett login (enkelt och gratis).
Skicka in bidrag
Föreslå rubrik
Beskriv vad din kod gör
Skapa kort länk
Använd en kort URL för att skicka länk till koden via SMS eller epost. När mottagaren klickar på länken, så öppnas denna webbsida, med din kod och din text. Länken rensas bort automatiskt om den inte används.
Rubrik (frivilligt)
Beskrivning (frivilligt)
Länk (kopiera hela)
Generatorer
En generator är en maskin som skapar någonting. T.ex. elektricitet. I python skapar generatorer t.ex. listor.
expr for item in iterable
Låt oss titta på nedanstående. Bekant.
for i in range(5):
print(i)
for i in range(5):
print(i)
Men vad gör de olika delarna här?
print(range(5))
print(range(5))
Den lever sitt egna liv! Range är i själva verket ett objekt (ett iterator -objekt) som lämnar ifrån sig en serie tal (med startvärde och slutvärde och steg/step -värde), på begäran av något annat objekt. Range kan användas som samtalspartner till andra objekt t.ex. en lista.
T.ex. kommer range tillsammans med list att skapa en lista ...
print(list(range(5)))
print(list(range(5)))
Vilket leder tillbaka till loopen ovan. Om range kan hjälpa till att skapa en lista, så kanske man borde kunna banka in en lista direkt? Det kan man.
for i in [0,1,2,3,4]:
print(i)
for i in [0,1,2,3,4]:
print(i)
Vilket iofs inte är konstigt då vi lärt oss ...
lista = [0,1,2,3,4]
for i in lista:
print(i)
lista = [0,1,2,3,4]
for i in lista:
print(i)
Men det finns andra automatiska sätt skapa t.ex. listor. Med generatorobjekt. Skrivsättet
x for x in range(5)
ger oss en generator. Vi kan skicka in denna generator i list() och då får vi en lista.
lista = list(x for x in range(5))
print(lista)
lista = list(x for x in range(5))
print(lista)
Vi kan skapa en lista utan att använda list(), genom att istället sätta hakparenteser runt generatoruttrycket.
lista = [x for x in range(5)]
print(lista)
lista = [x for x in range(5)]
print(lista)
Verkar vettigt, gör det inte? Om man fyller hakparenteser med en serie tal - då har man ju en lista?
x for x in [ ... ]
Konceptet här, det är att för varje element i vår lista (eller annan manick som genererar ett flöde av siffror), så bygger vi upp en ny talserie.
Vi ges en möjlighet att göra något med elementet som plockats ut. Vi kan t.ex. ta det i kvadrat.
Eller konvertera det till en sträng.
Detta är ju mycket användbart om vi har en lista och vill konvertera till en sträng. Join som är väldigt kraftfull, tar tyvärr bara strängar. Men vi kan konvertera listans tal till strängar! Join har inget emot att hämta data från ett generatorobjekt.
lst = [1,2,4,5,6,4]
string = " ".join(str(x) for x in lst)
print(string)
lst = [1,2,4,5,6,4]
string = " ".join(str(x) for x in lst)
print(string)
Vi kan köra alla tal genom en helt egen funktion.
lst = [1,2,4,5,6,4]
def dubbla(x):
return(x*2)
string = " ".join(str(dubbla(x)) for x in lst)
print(string)
lst = [1,2,4,5,6,4]
def dubbla(x):
return(x*2)
string = " ".join(str(dubbla(x)) for x in lst)
print(string)
Vi kan också lägga till ett villkor på slutet.
Vi kan t.ex. kräva att talet ska vara udda (om x är udda får divisionen x/2 en rest, dvs x%2 (modulo) blir !=0 dvs True)
lst = [1,2,4,5,6,4]
def dubbla(x):
return(x*2)
string = " ".join(str(dubbla(x)) for x in lst if x%2)
print(string)
lst = [1,2,4,5,6,4]
def dubbla(x):
return(x*2)
string = " ".join(str(dubbla(x)) for x in lst if x%2)
print(string)
loop i en loop
Vi kan istället för if -satsen bygga vidare med for -loopar. Nedan kör en loop i en loop för att returnera en lista av tupler med koordinater.
lst= [(x,y) for x in range(5) for y in range(5)]
print(lst)
lst= [(x,y) for x in range(5) for y in range(5)]
print(lst)
lst= [(x,y,z) for x in range(3) for y in range(3) for z in range(3)]
print(lst)
lst= [(x,y,z) for x in range(3) for y in range(3) for z in range(3)]
print(lst)
skapa dictionary
Det finns 2 sätt skapa en dictionary. Det ena är en generator som ger ifrån sig tupler och att använda dict.
d = dict((x,x) for x in range(5))
print(d)
d = dict((x,x) for x in range(5))
print(d)
Men det bästa sättet är att omge generatorn med { ... } och komma ihåg hur ett key : value ser ut med kolon emellan.
d = {n: n for n in range(5)}
print(d)
d = {n: n for n in range(5)}
print(d)
En dictionary med alla dess tal faktoriserade, om det går.
def faktorisera(tal):
lst = []
for d in range(2,1+tal//2):
if(tal%d == 0):
lst+=[d]+faktorisera(tal//d)
return(lst)
return [tal]
def fakprim(n):
lst = faktorisera(n)
return("Primtal" if len(lst)<2 else lst)
d = {n: fakprim(n) for n in range(1,100)}
print(d)
def faktorisera(tal):
lst = []
for d in range(2,1+tal//2):
if(tal%d == 0):
lst+=[d]+faktorisera(tal//d)
return(lst)
return [tal]
def fakprim(n):
lst = faktorisera(n)
return("Primtal" if len(lst)<2 else lst)
d = {n: fakprim(n) for n in range(1,100)}
print(d)
JSON datafil
Vi kan importera en stor dictionary från en JSON-fil för att ha lite data att leka med. Studera datafilen här som är periodiska systemet (öppnas i nytt fönster).
Säg att vi från denna stora dictionary vill extrahera en lista av alla "element" (grundämnen) som är gas -formiga.
import json
fil = open('/json/periodictable.json')
per = json.load(fil)
fil.close()
gaser = ", ".join(e["name"] for e in per['elements'] if e["phase"] == 'Gas')
print(gaser)
import json
fil = open('/json/periodictable.json')
per = json.load(fil)
fil.close()
gaser = ", ".join(e["name"] for e in per['elements'] if e["phase"] == 'Gas')
print(gaser)
Listor vi skapar med generatorer kan vi också skicka in i de logiska funktionerna all() (alla element är sanna) och any() (någon element är sant)
import json
fil = open('/json/periodictable.json')
per = json.load(fil)
fil.close()
print("Alla ämnen har ett atomnummer:", end="")
print(all(e["number"] >= 1 for e in per['elements']))
print("Alla ämnen har molar_heat:", end="")
print(all(e["molar_heat"] !=None for e in per['elements']))
print("Lista ämnen molar_heat == None :")
print(" ".join(str(e["name"]) for e in per['elements'] if e["molar_heat"]==None))
import json
fil = open('/json/periodictable.json')
per = json.load(fil)
fil.close()
print("Alla ämnen har ett atomnummer:", end="")
print(all(e["number"] >= 1 for e in per['elements']))
print("Alla ämnen har molar_heat:", end="")
print(all(e["molar_heat"] !=None for e in per['elements']))
print("Lista ämnen molar_heat == None :")
print(" ".join(str(e["name"]) for e in per['elements'] if e["molar_heat"]==None))
Vi kan låta en generator läsa vår dictionary och plocka ut lite information och skapa en ny dictionary. T.ex. en dictionary med alla ämnen som har ädelgas -struktur.
import json
fil = open('/json/periodictable.json')
per = json.load(fil)
data = dict((e["name"],e["shells"]) for e in per["elements"] if e["shells"][-1]==8)
print(data)
import json
fil = open('/json/periodictable.json')
per = json.load(fil)
data = dict((e["name"],e["shells"]) for e in per["elements"] if e["shells"][-1]==8)
print(data)
Lista av rader, modifiera varje rad
Säg att vi har en textfil vi önskar läsa in.
f = open('/csv/allcontries.csv', encoding="latin-1")
lines = f.readlines()
print(lines)
f.close()
f = open('/csv/allcontries.csv', encoding="latin-1")
lines = f.readlines()
print(lines)
f.close()
Vi ser att varje rad har en massa radslutstecken vi vill bli av med. Om indata är en lista, utdata en lista och vi ska göra något på varje element - typiskt uppgift för en generator!
f = open('/csv/allcontries.csv', encoding="latin-1")
lines = [x.rstrip() for x in f.readlines()]
print(lines)
f.close()
f = open('/csv/allcontries.csv', encoding="latin-1")
lines = [x.rstrip() for x in f.readlines()]
print(lines)
f.close()
tentafråga
Vad blir utdata? Det är en luring. Så vi har en generator som kommer skapa en serie av bokstäverna i 'typhoon'. Men runt generatorn sitter krullparenteser, dvs det är ett set (en mängd). I en mängd finns inga dubbletter och det finns inte heller någon ordning. Så dubbletter försvinner och ordningen kommer i praktiken bli bokstavsordning, vilket är samma sak som ingen ordning alls här.
a = {c for c in 'typhoon'}
print(a)
a = {c for c in 'typhoon'}
print(a)
Vad blir utdata? Ja, det blir en lista av bokstäver, eller hur?
a = [c for c in 'typhoon']
print(a)
a = [c for c in 'typhoon']
print(a)
initiera en tom 2d -lista
Många luringar på nätet om hur du ska initiera en 2d -lista i python. Rätt svar är nedan, du ska använda generatorer även för detta. En 10x10 lista...
lista2d = [[0]*10 for i in range(10)]
lista2d[3][5] = 10
print(lista2d[3][5])
print(lista2d[4][6])
lista2d = [[0]*10 for i in range(10)]
lista2d[3][5] = 10
print(lista2d[3][5])
print(lista2d[4][6])
läs filer & generator -uttryck
I nedan exempel öppnar vi en fil och läser rad för rad. Fil nedan är ett itererbart objekt, därför kan vi använda det i en for -loop och resultatet är att vi läser en rad i taget och skapar en lista av rader. Vi kan sedan som vanligt lägga till andra saker i vårt generator, t.ex. rensa tecken eller annat, i nedan exempel så plockar rad.rstrip() bort nyradstecknet som avslutar varje rad.
with open('/ex/text.txt') as fil:
rader = [rad.rstrip() for rad in fil]
print(rader)
with open('/ex/text.txt') as fil:
rader = [rad.rstrip() for rad in fil]
print(rader)
yield
Yield i denna kontext översätts kanske bäst med utbyte. När vi skriver yield i koden, så returneras värdet vid yield och sedan tar funktionen en paus och somnar. Dvs, där vi använde yield, exakt just den punkten där vi gjorde yield, där händer inget mer förrän den som använder funktionen gör next().
lat evaluering
Konceptet här - eller begreppet - kallas lat evaluering. Lat, därför att koden vi skapar kommer inte göra mer än nödvändigt i varje vända. Den tillhandahåller data när det behövs, annars inte. På så vis görs inget i onödan. Det kan verka lite omständigt när man tittar på små kodsnuttar, men i verkligheten kanske varje kalkyl eller inhämtning av data (över nätverk) tar lång tid. Då kan lat evaluering bli en jättestor besparing i tid. Exempelvis när du skrollar nedåt på en webbsida i mobiltelefonen, som aldrig tar slut. Här laddas sidan, lite då och då, över nätet, bara när det behövs.
def dum_generator():
for i in range(10):
yield i
tjoho = dum_generator()
print(next(tjoho))
print(next(tjoho))
print(next(tjoho))
print(next(tjoho))
def dum_generator():
for i in range(10):
yield i
tjoho = dum_generator()
print(next(tjoho))
print(next(tjoho))
print(next(tjoho))
print(next(tjoho))
I nedan for -loop görs next() automatiskt i själva for -loopen.
def dum_generator():
yield 1
yield 2
yield 3
yield 4
yield 5
for i in dum_generator():
print(i)
def dum_generator():
yield 1
yield 2
yield 3
yield 4
yield 5
for i in dum_generator():
print(i)
Titta på nedan kod. I dum_generator() finns en loop. Vid första loopvarvet kommer den returnera yield 1 och sedan vänta, tills for -loopen genererar ett next() i sitt nästa varv.
def dum_generator():
for i in range(10):
yield i
for i in dum_generator():
print(i)
def dum_generator():
for i in range(10):
yield i
for i in dum_generator():
print(i)
Vi printar ut lite för att se tydligare.
def dum_generator():
for i in range(10):
print("dum_generator")
yield i
for i in dum_generator():
print("loopen")
print(i)
def dum_generator():
for i in range(10):
print("dum_generator")
yield i
for i in dum_generator():
print("loopen")
print(i)
Som du ser görs ju inte mer än nödvändigt, vilket också är generatorers största vinst. Vi får en lat evaluering, inte mer än nödvändigt görs. Effektivt, minneseffektivt.
def dum_generator():
for i in range(10):
print("dum_generator")
yield i
lista = list(dum_generator())
def dum_generator():
for i in range(10):
print("dum_generator")
yield i
lista = list(dum_generator())
Som du ser ovan, så slurpar list i sig alla tal dum_generator() ger ifrån sig och skapar en lista av det.
egen range
Vi kan skapa en egen range -funktion med yield. Så yield kommer nedan att, vid första frågan om ett tal, ge ifrån sig 0. Nästa gång vi väcker funktionen genom att fråga efter ett tal, så lämnar den ifrån sig 1.
def minRange(t):
num = 0
while num < t:
yield num
num += 1
for i in minRange(10):
print(i)
def minRange(t):
num = 0
while num < t:
yield num
num += 1
for i in minRange(10):
print(i)
Vi kan skapa listor, mängder (set) och annat med vår egen generatorfunktion.
def minRange(t):
num = 0
while num < t:
yield num
num += 1
l = list(minRange(5))
print(l)
s = set(minRange(5))
print(s)
def minRange(t):
num = 0
while num < t:
yield num
num += 1
l = list(minRange(5))
print(l)
s = set(minRange(5))
print(s)
Vill vi lägga till start, stop och step i vår egen range -funktion så gör vi det. Vi trixar lite med standardvärden så att vi kan anropa med 1, 2 eller 3 argument.
def minRange(f,t=None,step=1):
if not t:
t = f
f = 0
num = f
if step<1 or num >= t:
raise Exception("Blir ju en evighetsloop ju!!!")
while num < t:
yield num
num +=step
for i in minRange(2,8,2):
print(i)
def minRange(f,t=None,step=1):
if not t:
t = f
f = 0
num = f
if step<1 or num >= t:
raise Exception("Blir ju en evighetsloop ju!!!")
while num < t:
yield num
num +=step
for i in minRange(2,8,2):
print(i)
Säg att vi vill få en serie av tal som är faktorer av något...
def faktorisera(f):
d = 2
while d < f:
if(f%d == 0):
yield d
f = f//d
d = 2
else:
d += 1
yield f
for i in faktorisera(256):
print(i, end=",")
def faktorisera(f):
d = 2
while d < f:
if(f%d == 0):
yield d
f = f//d
d = 2
else:
d += 1
yield f
for i in faktorisera(256):
print(i, end=",")
Eller säg att vi vill få en serie av tal som flippar 10,0,9,1,8,2,7,3, ...
def flipRange(n):
l,h = 0,n
while l <= h:
yield l
if h != l:
yield h
l += 1
h -= 1
for i in flipRange(10):
print(i, end=",")
def flipRange(n):
l,h = 0,n
while l <= h:
yield l
if h != l:
yield h
l += 1
h -= 1
for i in flipRange(10):
print(i, end=",")
Lite frågor
Vad blir genererar list(x%2 for x in range(5))
[1, 1, 1, 1, 1]
[1, 0, 1, 0, 1]
[0, 1, 0, 1, 0]
[0, 0, 0, 0, 0]
Vad blir genererar list(x for x in range(10) if x%2)
[2, 4, 6, 8, 10]
[1, 3, 5, 7, 9]
[0, 2, 4, 6, 8]
[1, 2, 3, 4, 5]
Vad blir genererar list(x for x in [1,2,4,5,6,4] if x%2)
[2, 4, 6, 4]
[1, 5]
[2, 4, 5, 6, 4]
Vad blir genererar list(x**2 for x in [1,3,4,5,6,4] if x%3==0)
[1, 16, 25, 16]
[1, 9, 16, 25, 36, 16]
[9, 36]
print(len({n==n for n in range (1,5)})
Begrunda ovan luriga tentafråga. Vad blir utskriften?
4
5
1
övningar
■ Skriv en generator som ger en lista med tal 0 till 10.
Visa lösning
Du kan antingen skapa listan genom att omge generatorn med [ ... ] eller använda list()
lst = [x for x in range(10)]
print(lst)
lst = [x for x in range(10)]
print(lst)
■ Skriv en generator som ger en lista av kvadrerade tal (x^2)
Visa lösning
lst = [x**2 for x in range(10)]
print(lst)
lst = [x**2 for x in range(10)]
print(lst)
■ Skriv en generator som ger en lista med nummer mellan 1 och 10 som är udda
Visa lösning
Det finns flera sätt göra detta.
lst = [x for x in range(1,10,2)]
print(lst)
lst = [x for x in range(1,10) if x%2]
print(lst)
lst = [x for x in range(1,10,2)]
print(lst)
lst = [x for x in range(1,10) if x%2]
print(lst)
■ Skriv en generator som ger en lista med nummer mellan 1 och 10 som är jämna
Visa lösning
Här kan du inte använda range step -funktion eftersom vi ska starta på 1.
lst = [x for x in range(1,10)]
print(lst)
lst = [x for x in range(1,10) if x%2==0]
print(lst)
lst = [x for x in range(1,10)]
print(lst)
lst = [x for x in range(1,10) if x%2==0]
print(lst)
■ Skriv en generator som ger en lista av talen 1,10,100,1000,1000 upp till 9 nollor.
Visa lösning
lst = [10**x for x in range(1,10)]
print(lst)
lst = [10**x for x in range(1,10)]
print(lst)
■ Skriv en generator som ger en lista av talen 0-10 konverterade till strängar
Visa lösning
lst = [str(x) for x in range(10)]
print(lst)
lst = [str(x) for x in range(10)]
print(lst)
■ Skriv en generator som ger en lista av udda tal 0-10 konverterade till strängar
Visa lösning
lst = [str(x) for x in range(10) if x%2]
print(lst)
lst = [str(x) for x in range(10) if x%2]
print(lst)
■ Skriv en egen funktion dubbla som dubblar det du skickar in
Visa lösning
def dubbla(x):
return(x*2)
print(dubbla(4))
def dubbla(x):
return(x*2)
print(dubbla(4))
■ Skriv en generator som ger tal 0-10 som dubblar varje tal med funktionen du nyss skrev.
Visa lösning
lst = [dubbla(x) for x in range(10)]
print(lst)
lst = [dubbla(x) for x in range(10)]
print(lst)
■ Join kan bygga ihop en sträng av en lista. Skriv en generator som ger en lista av tal 0-10, som sedan joinar ihop allt till en sträng
Visa lösning
lst = " ".join([str(x) for x in range(10)])
print(lst)
lst = " ".join([str(x) for x in range(10)])
print(lst)
■ Skriv en generator som skapar tal 0-10 men som ger ifrån sig en lista av 0 och 1 beroende på om talet är udda eller inte
Visa lösning
lst = [x%2 for x in range(10)]
print(lst)
lst = [x%2 for x in range(10)]
print(lst)
■ Skriv en generator som skapar tal 0-10 men som ger ifrån sig en lista av 1 och 2 beroende på om talet är udda eller inte
Visa lösning
lst = [x%2+1 for x in range(10)]
print(lst)
lst = [x%2+1 for x in range(10)]
print(lst)
■ Skriv en generator som ger tal 0-10 men ger utdata en lista av texterna "udda" och "jämn" beroende på om talet jämnt eller udda
Visa lösning
lst = ["udda" if x%2 else "jämt" for x in range(10)]
print(lst)
lst = ["udda" if x%2 else "jämt" for x in range(10)]
print(lst)
■ Skriv en generator som ger tal 0-10 men ger utdata en lista av texterna "delbart med 3" och "inte delbart med 3" beroende på om talet är delbart med 3
Visa lösning
lst = [f"{x} inte delbart med 3" if x%3 else f"{x} delbart med 3" for x in range(10)]
print(lst)
lst = [f"{x} inte delbart med 3" if x%3 else f"{x} delbart med 3" for x in range(10)]
print(lst)
■ Skriv en generator som skapar tal 0-10 men som ger ifrån sig en lista av tupler av talet ex [(1,1), (2,2), (3,3), ... ]
Visa lösning
lst = [(x,x) for x in range(10)]
print(lst)
lst = [(x,x) for x in range(10)]
print(lst)
■ Skriv en generator som skapar tal 0-10 men som ger ifrån sig en lista av tupler där ena halvan av tupeln är 0 eller 1 beroende på om det är udda eller jämt [(1,1), (2,0), (3,1), (4,0),... ]
Visa lösning
lst = [(x,x%2) for x in range(10)]
print(lst)
lst = [(x,x%2) for x in range(10)]
print(lst)
■ Skriv en generator som skapar tal 0-10 men som ger ifrån sig en dictionary där key är siffran och value är 0 eller 1 beroende på om talet är udda eller jämt. {1:1, 2:0, 3:1, 4:0}. Tips, du tänker som om du ska skriva en tupel (key, value). Tips, istället för att konvertera till lista () använder du dict()
Visa lösning
dct = {(x,x%2) for x in range(10)}
print(dct )
dct = {(x,x%2) for x in range(10)}
print(dct )
■ Skriv en generator som skapar tal 0-10 men som ger ifrån sig en dictionary där key är siffran och value är en sträng "udda" eller "jämt" beroende på om talet är udda eller jämt.
Visa lösning
dct = {x:("udda" if x%2 else "jämt") for x in range(10)}
print(dct)
dct = {x:("udda" if x%2 else "jämt") for x in range(10)}
print(dct)