Göm meny

Extra material om Python


Här hittar du trevliga Python-konstruktioner som inte nödvändigtvis finns i andra programmeringsspråk.

Läsa från textfil

Det finns många olika sätt att läsa data från en fil i Python. Vilket sätt som är “bäst” beror på kontexten.

I lektion 2 används följande som “standardsätt” att läsa och bearbeta rader från en fil:

file = open(path)
lines = file.readlines()
file.close()
for line in lines:
    # skriv din kod här
    pass

Loopa genom öppen fil

Vi kan också använda en for-loop direkt på en öppen textfil:

file = open(path)
for line in file:
    # skriv din kod här
    pass
# stäng filen efter när du är klar
file.close()

Det pythonska sättet

Ett mer pythonskt sätt är att använda kontexthanterare i Python (context manager):

with open(path) as file:
    for line in file:
        # skriv din kod här
        pass

Konstruktionen bygger på att objektet (den öppna filen i detta fall) implementerar metoderna __enter__() och __exit__(). Dessa metoder anropas när vi går in i with-blocket (__enter__()), samt när vi lämnar with-blocket (__exit__()).

Med hjälp av denna konstruktion kan den öppna filen stänga sig själv när vi går ut ur kontexten. Koden blir också mer lättläst och det blir tydligt var filen används.

Villkor på en rad

if x == y:
    z = 10
else:
    z = 0

kan skrivas som

z = 10 if x == y else 0

Mer generellt:

<uttryck A> if <sanningsvärde> else <uttryck B>

I första exemplet motsvaras <uttryck A> av värdet 10, inte z = 10.

dict.get() och dict.setdefault()

Det är vanligt att man behöver undersöka om en nyckel finns eller inte finns i ett dictionary innan man antingen hämtar dess värde eller uppdaterar dess värde. Om nyckeln k inte finns i dictionaryt d får man ett key error om man försöker hämta dess värde; d[k] -> KeyError. För att undvika detta kan man använda en villkorssats för att undersöka detta först:

d = {}
# låt x = "fallback" om inte nyckeln key finns i d, i så fall använder vi dess
# värdet som är associerat till nyckeln
x = "fallback"
if key in d:
    x = d[key]

# om d[key] inte har något värde, sätt dess värde till 1. Om d[key] finns, låt
# det behålla sitt värde
if key not in d:
    d[key] = 1

Eftersom detta är så vanligt förekommande fall har man i Python lagt till metoderna .get() och .setdefault(). Använder vi dessa metoder kan vi både skriva mindre kod och göra den mer lättläst:

d = {}
# låt x = "fallback" om inte nyckeln key finns i d, i så fall använder vi dess
# värdet som är associerat till nyckeln
x = d.get(key, "fallback")

# om d[key] inte har något värde, sätt dess värde till 1. Om d[key] finns, låt
# det behålla sitt värde
d.setdefault(key, 1) 

Se dict.get() och dict.setdefault().

Skriva ut en lista som innehåller egendefinerade klasser

TLDR; list.__str__() använder värdenas __repr__()-metod, inte deras __str__()-metod. Implementera __repr__() för styra vad som syns. Om du redan har implementerat __str__() kan du låta __repr__() returnera self.__str__(). Mer nyanserad förklaring nedan.

Säg att vi definerar följande klass:

class MyClass:

    def __init__(self, value):
        self.value = value

    def __str__(self):
        return str(self.value)

Vi skapar 5 instanser och lägger dem i listan l, men när vi gör print(l) så får vi något i stil med nedanstående, varför?

[<__main__.MyClass object at 0x1108786a0>, <__main__.MyClass object at 0x1107ac310>,
<__main__.MyClass object at 0x1107acd60>, <__main__.MyClass object at 0x1107ac0d0>,
<__main__.MyClass object at 0x1107ac1c0>]

list.__str__()

Det som händer ovan är att Python använder den __str__() som är definierad i list och det den gör är att använda sina värdens __repr__()-metoder för att visa dem.

För att print(l) ska fungera för en lista med instanser av en egendefinierad klass behöver vi alltså se till att vår klass implementerar __repr__(). Om vi redan har en __str__() som vi använder för att göra fina utskrifter av vår klass (se nedan), så kan vi låta __repr__() returnera self.__str__().

Om vi inte har en __str__() än, kan vi definera __repr__(), eller så kan vi se till att vi både implementerar __str__() och __repr__(). Se nedan för skillnaden i användning av dessa.

Skillnaden mellan __repr__() och __str__()

Metoderna __repr__() och __str__() finns dokumenterade i beskrivningen av Pythons datamodell. Tanken är att __repr__() används när vi felsöker vår kod, “debuggar” den, medan __str__() används när vi t.ex. vi vill visa objektet för en användare.

__str__() används av Python vid print(), str() eller när vi formatterar en sträng med .format() eller använder en formatterad sträng; f"Värde: {value}".

__repr__() används av funktionen repr() (Notera att det finns en funktion för att använda __repr__(), dvs vi anropar inte __repr__() explicit utanför klassen)

Användningområden är är tydligt om vi t.ex. tittar på skillnaden mellan __str__() och __repr__() för den inbyggda datatypen str (Ja, de finns även för str, alla objekt behandlas lika!):

>>> s = "Hello\nWorld"
>>> str(s)
'Hello\nWorld'
>>> repr(s)
"'Hello\\nWorld'"
>>> print(s)
Hello
World
>>> print(repr(s))
'Hello\nWorld'
>>> print(s)
Hello
World
>>> print(repr(s))
'Hello\nWorld'
>>>

Vi ser att här att repr(s) returnerar en sträng som om vi skriver ut den vi faktiskt ser radbrytstecknet (vi skriver "\\" för att få ut ett \ med print()).

Till sist är det värt att notera att pythontolken kommer använda försöka använda __repr__() om en klass inte implementerar __str__(), men inte tvärt om.


Sidansvarig: Johan Falkenjack
Senast uppdaterad: 2021-08-18