Göm menyn

Listbyggare

I många fall när nan programmerar vill man bygga upp en lista med hjälp av en for-loop.

Man kanske vill ha en lista av kvadraten av alla siffror

numbers = [1,2,3,4,5]

squared = []
for number in numbers:
    squared.append(number**2)

print(squared) # [1,4,9,16,25]

Eller en lista med alla primtal som finns i en annan lista

candidates = [1,2,4,7,12,15]

primes = []
for num in candidate:
    if is_prime(num):
        primes.append(num)

print(primes) #[1,2,7]

Eftersom det är en så vanlig operation har python ett sätt att göra den typen av operation med ett uttryck, en listbyggare (eng. list comprehension).

Syntaxen för listbyggare ser ut på följande sätt:

[uttryck_av_x for x in iterator if uttryck_av_x]

Vilket ungefär kan expanderas till

def fake_list_comprehension():
    result=[]
    for x in iterator:
        if condition(x):
            result.append(value(x))
    return result

Exemplet ovan där en lista av tal kvadrerades kan alltså skrivas som

numbers = [1,2,3,4,5]

squared = [i**2 for i in numbers]

Och alla primtal i en lista kan sorteras ut med

candidates = [1,2,4,7,12,15]

primes = [i for i in candidates if is_prime(i)]

När och varför?

Det kanske inte är helt självklart varför listbyggare finns och när de ska användas eftersom de nästan bara är en förkortad for-loop. De har dock två stora fördelar:

  1. Det är uppenbart att de skapar en ny lista och inte gör något annat
  2. De kan inte modifiera utomstående icke-globala variabler
  3. De är snabbare än for-loopar

Självklart kan- och ska man dock inte alltid använda listbyggare. Om långa beräkningar måste göras i uttrycken för värdet eller if-delen av listbyggaren så blir det svårt att läsa vad koden gör. Det går inte heller att tilldela variabller eller liknade inuti en listbyggare så det är inte alltid möjligt att utnyttja dem.

En tumregel för när de ska användas är alltså när man vill skapa en ny lista från en iterator och när beräkningarna eller kraven på vilka element som ska med i listan inte är för långa.

Tuple- och Dictionary-byggare

Listbyggare är den vanligaste byggar-strukturen i python, men språket har även tuple- och dictionary-byggare.

Tuple-byggare

För att bygga en tupel kan man skapa en lista som man sedan konverterar till en tupel.

# Syntax
tuple([value for x in iterator if condition])


# Exempel:
tuple([255 for i in range(3)]) # (255, 255, 255)

Detta gör det möjligt att dynamiskt skapa tupler med okänd längd.

Dictionary-byggare

En dictionary-byggare ser ungefär ut som listbyggare men använder {} istället för []. Eftersom varje "element" i en dictionary är en nyckel och ett värde har uttrycket för elementen två delar som är separerade med :

# Syntax
{key_from_x: value_from_x for x in iterator if condition}

# Exempel
## Skapa en dictionary från en lista nyckel-värde-par i en tuple

key_values = [("python", 1), ("java", 2), ("c", 3)]

{key: value for (key, value) in key_values} #{"python": 1, "java": 2, "c": 3}

Generatoruttryck

Listbyggare använder hakparenteser, som i följande exempel:

primes = [i for i in range(1, 1000000000) if is_prime(i)]
num_primes = 0
for prime in primes:
    print(prime)
    num_primes += 1
    if num_primes == 1000:
        break

Här vill vi hitta de 1000 första primtalen. Vi vet inte hur många tal vi måste söka genom innan vi hittar 1000 primtal, så för säkerhetsskull tar vi i och letar efter alla primtal upp till 1 miljard.

Detta har vissa nackdelar -- det tar väldigt lång tid att söka genom 1 miljard tal, och det är först när detta är klart som vi kan börja söka genom dem. Dessutom tar det mycket minne att lagra alla primtal.

Men vi behöver ju faktiskt bara ett primtal i taget, inte hela listan. Då kan vi istället använda vanliga parenteser istället för hakparenteser, så blir uttrycket ett generatoruttryck:

primes = (i for i in range(1, 1000000000) if is_prime(i))
num_primes = 0
for prime in primes:
    print(prime)
    num_primes += 1
    if num_primes == 1000:
        break

Det här ser nästan ut som tidigare, men nu är primes inte alls en lista utan en generator. Det är inte förrän vi ber om ett element från generatorn -- i for-loopen -- som den börjar testa olika värden på i. Den testar bara till den hittar nästa heltal som uppfyller is_prime(i) och returnerar sedan detta. Nästa gång man ber om ett heltal fortsätter den leta där den slutade förra gången.

I den här kursen behöver du inte kunna skapa egna generatoruttryck, men det kan vara bra att känna igen dem om du stöter på dem i annan kod.


Sidansvarig: Peter Dalenius
Senast uppdaterad: 2021-12-03