Vanliga frågor om programmering

Allmänna frågor

Finns det en debugger på källkodsnivå med brytpunkter, single-stepping, etc

Ja.

Flera debuggers för Python beskrivs nedan, och den inbyggda funktionen breakpoint() gör att du kan hoppa in i vilken som helst av dem.

Modulen pdb är en enkel men adekvat felsökare i konsolläge för Python. Den är en del av Pythons standardbibliotek och dokumenteras i Library Reference Manual. Du kan också skriva din egen felsökare genom att använda koden för pdb som ett exempel.

Den interaktiva utvecklingsmiljön IDLE, som ingår i standarddistributionen av Python (normalt tillgänglig som Tools/scripts/idle3), innehåller en grafisk felsökare.

PythonWin är en Python IDE som innehåller en GUI-felsökare baserad på pdb. PythonWin-felsökaren färgar brytpunkter och har en hel del coola funktioner, till exempel felsökning av icke-PythonWin-program. PythonWin är tillgängligt som en del av pywin32-projektet och som en del av ActivePython-distributionen.

Eric är en IDE som bygger på PyQt och redigeringskomponenten Scintilla.

trepan3k är en gdb-liknande felsökare.

Visual Studio Code är en IDE med felsökningsverktyg som kan integreras med programvara för versionshantering.

Det finns ett antal kommersiella Python IDE:er som innehåller grafiska debuggers. De inkluderar:

Finns det verktyg som hjälper till att hitta buggar eller utföra statisk analys?

Ja.

Pylint och Pyflakes gör grundläggande kontroller som hjälper dig att hitta buggar snabbare.

Statiska typkontrollprogram som Mypy, Pyre och Pytype kan kontrollera typtips i Pythons källkod.

Hur kan jag skapa en fristående binär från ett Python-skript?

Du behöver inte kunna kompilera Python till C-kod om du bara vill ha ett fristående program som användarna kan ladda ner och köra utan att först behöva installera Python-distributionen. Det finns ett antal verktyg som fastställer den uppsättning moduler som krävs av ett program och binder samman dessa moduler med en Python-binärfil för att producera en enda körbar fil.

Ett sätt är att använda verktyget freeze, som ingår i Pythons källträd som Tools/freeze. Det konverterar Pythons bytekod till C-arrayer; med en C-kompilator kan du bädda in alla dina moduler i ett nytt program, som sedan länkas med Pythons standardmoduler.

Det fungerar genom att skanna din källkod rekursivt efter importmeddelanden (i båda formerna) och leta efter modulerna i Pythons standardsökväg samt i källkatalogen (för inbyggda moduler). Därefter omvandlas bytekoden för moduler skrivna i Python till C-kod (array-initialiserare som kan omvandlas till kodobjekt med hjälp av marshal-modulen) och en skräddarsydd konfigurationsfil skapas som endast innehåller de inbyggda moduler som faktiskt används i programmet. Därefter kompileras den genererade C-koden och länkas med resten av Python-tolken för att bilda en fristående binär fil som fungerar precis som ditt skript.

Följande paket kan hjälpa till att skapa körbara konsol- och GUI-program:

Finns det kodningsstandarder eller en stilguide för Python-program?

Ja. Den kodningsstil som krävs för standardbiblioteksmoduler är dokumenterad som PEP 8.

Kärnspråk

Varför får jag ett UnboundLocalError när variabeln har ett värde?

Det kan vara en överraskning att få UnboundLocalError i tidigare fungerande kod när den ändras genom att lägga till en assignment-sats någonstans i en funktions kropp.

Den här koden:

>>> x = 10
>>> def bar():
...     print(x)
...
>>> bar()
10

fungerar, men den här koden:

>>> x = 10
>>> def foo():
...     print(x)
...     x += 1

resulterar i en UnboundLocalError:

>>> foo()
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'x' referenced before assignment

Det beror på att när du gör en tilldelning till en variabel i ett scope, blir variabeln lokal i detta scope och skuggar alla variabler med liknande namn i det yttre scopet. Eftersom den sista satsen i foo tilldelar ett nytt värde till x, känner kompilatorn igen den som en lokal variabel. Följaktligen när den tidigare print(x) försöker skriva ut den oinitialiserade lokala variabeln och ett fel uppstår.

I exemplet ovan kan du komma åt den yttre scopevariabeln genom att deklarera den som global:

>>> x = 10
>>> def foobar():
...     global x
...     print(x)
...     x += 1
...
>>> foobar()
10

Denna uttryckliga deklaration krävs för att påminna dig om att du faktiskt ändrar variabelns värde i det yttre omfånget (till skillnad från den ytligt sett analoga situationen med klass- och instansvariabler):

>>> print(x)
11

Du kan göra något liknande i ett nästlat scope med hjälp av nyckelordet nonlocal:

>>> def foo():
...    x = 10
...    def bar():
...        nonlocal x
...        print(x)
...        x += 1
...    bar()
...    print(x)
...
>>> foo()
10
11

Vilka är reglerna för lokala och globala variabler i Python?

I Python är variabler som endast refereras till inom en funktion implicit globala. Om en variabel tilldelas ett värde någonstans inom funktionens kropp antas den vara lokal om den inte uttryckligen deklareras som global.

Även om det är lite förvånande i början, förklarar en stunds övervägande detta. Å ena sidan ger kravet på global för tilldelade variabler en spärr mot oavsiktliga bieffekter. Å andra sidan, om global krävdes för alla globala referenser, skulle du använda global hela tiden. Man skulle vara tvungen att deklarera varje referens till en inbyggd funktion eller till en komponent i en importerad modul som global. Denna röra skulle omintetgöra nyttan av global-deklarationen för att identifiera bieffekter.

Varför returnerar lambdas som definieras i en loop med olika värden alla samma resultat?

Antag att du använder en for-slinga för att definiera några olika lambdas (eller till och med vanliga funktioner), t.ex.:

>>> squares = []
>>> for x in range(5):
...     squares.append(lambda: x**2)

Detta ger dig en lista som innehåller 5 lambdas som beräknar x**2. Du kan förvänta dig att när de anropas skulle de returnera 0, 1, 4, 9 och 16. Men när du faktiskt försöker kommer du att se att de alla returnerar 16:

>>> squares[2]()
16
>>> squares[4]()
16

Detta beror på att x inte är lokalt för lambdan utan definieras i det yttre scope, och den nås när lambdan anropas — inte när den definieras. I slutet av slingan är värdet på x 4, så alla funktioner returnerar nu 4**2, dvs 16. Du kan också verifiera detta genom att ändra värdet på x och se hur resultaten av lambdas ändras:

>>> x = 8
>>> squares[2]()
64

För att undvika detta måste du spara värdena i variabler som är lokala för lambdan, så att de inte är beroende av värdet på den globala x:

>>> squares = []
>>> for x in range(5):
...     squares.append(lambda n=x: n**2)

Här skapar n=x en ny variabel n som är lokal för lambdan och som beräknas när lambdan definieras så att den har samma värde som x hade vid den tidpunkten i loopen. Detta innebär att värdet på n kommer att vara 0 i den första lambdan, 1 i den andra, 2 i den tredje och så vidare. Därför kommer varje lambda nu att returnera rätt resultat:

>>> squares[2]()
4
>>> squares[4]()
16

Observera att detta beteende inte är unikt för lambdas, utan även gäller för vanliga funktioner.

Hur delar jag globala variabler mellan moduler?

Det vanligaste sättet att dela information mellan moduler inom ett och samma program är att skapa en speciell modul (ofta kallad config eller cfg). Importera bara config-modulen i alla moduler i ditt program; modulen blir då tillgänglig som ett globalt namn. Eftersom det bara finns en instans av varje modul kommer alla ändringar som görs i modulobjektet att återspeglas överallt. Ett exempel:

config.py:

x = 0 # Standardvärde för konfigurationsinställningen "x

mod.py:

import config
config.x = 1

main.py:

import config
import mod
print(config.x)

Observera att användning av en modul också är grunden för att implementera designmönstret singleton, av samma anledning.

Vilka är de ”bästa metoderna” för att använda import i en modul?

I allmänhet ska du inte använda from modulename import *. Om du gör det blir importörens namnrymd rörig och det blir mycket svårare för linters att upptäcka odefinierade namn.

Importera moduler längst upp i en fil. På så sätt blir det tydligt vilka andra moduler din kod kräver och du undviker frågor om modulnamnet är inom räckvidden. Om du använder en import per rad är det enkelt att lägga till och ta bort modulimporter, men om du använder flera importer per rad tar det mindre plats på skärmen.

Det är bra om du importerar moduler i följande ordning:

  1. standardbiblioteksmoduler – t.ex. sys, os, argparse, re

  2. biblioteksmoduler från tredje part (allt som installeras i Pythons site-packages-katalog) – t.ex. dateutil, requests, PIL.Image

  3. lokalt utvecklade moduler

Det är ibland nödvändigt att flytta import till en funktion eller klass för att undvika problem med cirkulär import. Gordon McMillan säger:

Cirkulär import fungerar bra när båda modulerna använder importformen ”import <module>”. De misslyckas när den andra modulen vill hämta ett namn från den första (”from module import name”) och importen sker på högsta nivån. Det beror på att namnen i den första modulen ännu inte är tillgängliga, eftersom den första modulen är upptagen med att importera den andra.

I det här fallet, om den andra modulen bara används i en funktion, kan importen enkelt flyttas till den funktionen. När importen anropas kommer den första modulen att ha initialiserats färdigt och den andra modulen kan göra sin import.

Det kan också vara nödvändigt att flytta importen från den översta kodnivån om några av modulerna är plattformsspecifika. I så fall kanske det inte ens är möjligt att importera alla moduler högst upp i filen. I så fall är det ett bra alternativ att importera rätt moduler i motsvarande plattformsspecifika kod.

Flytta bara import till ett lokalt scope, t.ex. inuti en funktionsdefinition, om det är nödvändigt för att lösa ett problem, t.ex. för att undvika cirkulär import eller för att minska initialiseringstiden för en modul. Den här tekniken är särskilt användbar om många av importerna är onödiga beroende på hur programmet exekveras. Du kanske också vill flytta importen till en funktion om modulerna bara används i den funktionen. Observera att det kan vara dyrt att ladda en modul första gången eftersom modulen initialiseras en gång, men att ladda en modul flera gånger är praktiskt taget gratis och kostar bara ett par ordboksuppslagningar. Även om modulnamnet har försvunnit finns modulen förmodligen tillgänglig i sys.modules.

Varför delas standardvärden mellan objekt?

Denna typ av bugg drabbar ofta nybörjare inom programmering. Tänk på den här funktionen:

def foo(mydict={}):  # Fara: delad referens till en dict för alla anrop
    ... beräkna något ...
    mydict[nyckel] = värde
    returnera mydict

Första gången du anropar den här funktionen innehåller mydict ett enda objekt. Andra gången innehåller mydict två objekt eftersom när foo() börjar exekveras, börjar mydict med ett objekt redan i den.

Man förväntar sig ofta att ett funktionsanrop skapar nya objekt för standardvärden. Detta är inte vad som händer. Standardvärden skapas exakt en gång, när funktionen definieras. Om det objektet ändras, som ordboken i det här exemplet, kommer efterföljande anrop av funktionen att hänvisa till det ändrade objektet.

Per definition är oföränderliga objekt som tal, strängar, tupler och None säkra från ändringar. Ändringar av föränderliga objekt som ordböcker, listor och klassinstanser kan leda till förvirring.

På grund av denna funktion är det god programmeringspraxis att inte använda föränderliga objekt som standardvärden. Använd istället None som standardvärde och kontrollera i funktionen om parametern är None och skapa en ny lista/dictionary/whatever om den är det. Skriv till exempel inte:

def foo(mydict={}):
    ...

men:

def foo(mydict=None):
    om mydict är None:
        mydict = {} # skapa en ny dict för lokal namnrymd

Den här funktionen kan vara användbar. När du har en funktion som är tidskrävande att beräkna är en vanlig teknik att cachelagra parametrarna och det resulterande värdet för varje anrop till funktionen och returnera det cachelagrade värdet om samma värde begärs igen. Detta kallas ”memoizing” och kan implementeras så här:

# Anroparen kan bara ange två parametrar och eventuellt skicka _cache med nyckelord
def expensive(arg1, arg2, *, _cache={}):
    if (arg1, arg2) i _cache:
        returnera _cache[(arg1, arg2)]

    # Beräkna värdet
    resultat = ... dyr beräkning ...
    _cache[(arg1, arg2)] = result # Lagra resultatet i cacheminnet
    returnera resultat

Du kan använda en global variabel som innehåller en ordbok i stället för standardvärdet; det är en smaksak.

Hur kan jag skicka valfria parametrar eller nyckelordsparametrar från en funktion till en annan?

Samla argumenten med hjälp av specifikatorerna * och ** i funktionens parameterlista, så att du får de positionella argumenten som en tupel och nyckelordsargumenten som en ordbok. Du kan sedan skicka dessa argument när du anropar en annan funktion genom att använda * och **:

def f(x, *args, **kwargs):
    ...
    kwargs['bredd'] = '14.3c'
    ...
    g(x, *args, **kwargs)

Vad är skillnaden mellan argument och parametrar?

Parametrar definieras av de namn som förekommer i en funktionsdefinition, medan argument är de värden som faktiskt skickas till en funktion när den anropas. Parametrar definierar vilken typ av argument som en funktion kan acceptera. Till exempel, givet funktionsdefinitionen:

def func(foo, bar=None, **kwargs):
    pass

foo, bar och kwargs är parametrar för func. Men när man anropar func, till exempel:

func(42, bar=314, extra=somevar)

värdena 42, 314 och somevar är argument.

Varför ändrades listan ”y” samtidigt som listan ”x” ändrades?

Om du skrev kod som:

>>> x = []
>>> y = x
>>> y.append(10)
>>> y
[10]
>>> x
[10]

kanske du undrar varför ett tillägg av ett element till y ändrade x också.

Det finns två faktorer som ger detta resultat:

  1. Variabler är helt enkelt namn som refererar till objekt. Om du gör y = x skapas inte en kopia av listan - det skapas en ny variabel y som refererar till samma objekt som x refererar till. Det betyder att det bara finns ett objekt (listan) och att både x och y refererar till det.

  2. Listor är mutable, vilket innebär att du kan ändra deras innehåll.

Efter anropet till append() har innehållet i det föränderliga objektet ändrats från [] till [10]. Eftersom båda variablerna refererar till samma objekt får man tillgång till det modifierade värdet [10] om man använder något av namnen.

Om vi istället tilldelar ett oföränderligt objekt till x:

>>> x = 5  # ints are immutable
>>> y = x
>>> x = x + 1  # 5 can't be mutated, we are creating a new object here
>>> x
6
>>> y
5

kan vi se att i detta fall är x och y inte lika längre. Detta beror på att heltal är immutable, och när vi gör x = x + 1 muterar vi inte int 5 genom att öka dess värde; istället skapar vi ett nytt objekt (int 6) och tilldelar det till x (det vill säga ändrar vilket objekt x refererar till). Efter denna tilldelning har vi två objekt (int 6 och 5) och två variabler som hänvisar till dem (x hänvisar nu till 6 men y hänvisar fortfarande till 5).

Vissa operationer (till exempel y.append(10) och y.sort()) muterar objektet, medan ytligt sett liknande operationer (till exempel y = y + [10] och sorted(y)) skapar ett nytt objekt. I allmänhet i Python (och i alla fall i standardbiblioteket) kommer en metod som muterar ett objekt att returnera None för att undvika att de två typerna av operationer förväxlas. Så om du felaktigt skriver y.sort() och tror att det kommer att ge dig en sorterad kopia av y, kommer du istället att sluta med None, vilket sannolikt kommer att leda till att ditt program genererar ett lättdiagnostiserat fel.

Det finns dock en klass av operationer där samma operation ibland har olika beteenden med olika typer: de utökade tilldelningsoperatorerna. Till exempel muterar += listor men inte tupler eller ints (a_list += [1, 2, 3] motsvarar a_list.extend([1, 2, 3]) och muterar a_list, medan some_tuple += (1, 2, 3) och some_int += 1 skapar nya objekt).

Med andra ord..:

  • Om vi har ett muterbart objekt (list, dict, set, etc.), kan vi använda vissa specifika operationer för att mutera det och alla variabler som hänvisar till det kommer att se förändringen.

  • Om vi har ett oföränderligt objekt (str, int, tuple, etc.) kommer alla variabler som refererar till det alltid att ha samma värde, men operationer som omvandlar det värdet till ett nytt värde returnerar alltid ett nytt objekt.

Om du vill veta om två variabler refererar till samma objekt eller inte, kan du använda operatorn is eller den inbyggda funktionen id().

Hur skriver jag en funktion med utparametrar (call by reference)?

Kom ihåg att argument skickas via assignment i Python. Eftersom tilldelning bara skapar referenser till objekt finns det inget alias mellan ett argumentnamn i den som anropar och den som tar emot anropet, och därmed inget anrop genom referens i sig. Du kan uppnå önskad effekt på ett antal olika sätt.

  1. Genom att returnera en tupel av resultaten:

    >>> def func1(a, b):
    ...     a = 'new-value'        # a and b are local names
    ...     b = b + 1              # assigned to new objects
    ...     return a, b            # return new values
    ...
    >>> x, y = 'old-value', 99
    >>> func1(x, y)
    ('new-value', 100)
    

    Detta är nästan alltid den tydligaste lösningen.

  2. Genom att använda globala variabler. Detta är inte trådsäkert och rekommenderas inte.

  3. Genom att skicka ett muterbart (förändringsbart på plats) objekt:

    >>> def func2(a):
    ...     a[0] = 'new-value'     # 'a' references a mutable list
    ...     a[1] = a[1] + 1        # changes a shared object
    ...
    >>> args = ['old-value', 99]
    >>> func2(args)
    >>> args
    ['new-value', 100]
    
  4. Genom att skicka in en ordbok som muteras:

    >>> def func3(args):
    ...     args['a'] = 'new-value'     # args is a mutable dictionary
    ...     args['b'] = args['b'] + 1   # change it in-place
    ...
    >>> args = {'a': 'old-value', 'b': 99}
    >>> func3(args)
    >>> args
    {'a': 'new-value', 'b': 100}
    
  5. Eller samla ihop värden i en klassinstans:

    >>> class Namespace:
    ...     def __init__(self, /, **args):
    ...         for key, value in args.items():
    ...             setattr(self, key, value)
    ...
    >>> def func4(args):
    ...     args.a = 'new-value'        # args is a mutable Namespace
    ...     args.b = args.b + 1         # change object in-place
    ...
    >>> args = Namespace(a='old-value', b=99)
    >>> func4(args)
    >>> vars(args)
    {'a': 'new-value', 'b': 100}
    

    Det finns nästan aldrig någon bra anledning att göra det så här komplicerat.

Det bästa alternativet är att returnera en tupel som innehåller flera resultat.

Hur gör man en funktion av högre ordning i Python?

Du har två val: du kan använda nästlade scopes eller så kan du använda anropsbara objekt. Anta till exempel att du vill definiera linear(a,b) som returnerar en funktion f(x) som beräknar värdet a*x+b. Använda nästlade scopes:

def linear(a, b):
    def resultat(x):
        returnerar a * x + b
    returnera resultat

Eller använda ett anropsbart objekt:

klass linjär:

    def __init__(self, a, b):
        self.a, self.b = a, b

    def __call__(self, x):
        return self.a * x + self.b

I båda fallen gäller:

skatter = linjär(0,3, 2)

ger ett anropbart objekt där taxes(10e6) == 0.3 * 10e6 + 2.

Metoden med anropsbara objekt har nackdelen att den är lite långsammare och resulterar i något längre kod. Observera dock att en samling callables kan dela sin signatur via arv:

klass exponentiell(linjär):
    # __init__ nedärvd
    def __call__(self, x):
        return self.a * (x ** self.b)

Objekt kan kapsla in tillstånd för flera metoder:

klassräknare:

    värde = 0

    def set(self, x):
        self.value = x

    def up(self):
        self.value = self.value + 1

    def down(self):
        self.value = self.value - 1

count = räknare()
inc, dec, reset = count.up, count.down, count.set

Här fungerar inc(), dec() och reset() som funktioner som delar samma räknevariabel.

Hur kopierar jag ett objekt i Python?

I allmänhet kan du prova copy.copy() eller copy.deepcopy() för det allmänna fallet. Alla objekt kan inte kopieras, men de flesta kan det.

Vissa objekt kan kopieras lättare. Dictionaries har en copy() metod:

newdict = olddict.copy()

Sekvenser kan kopieras genom att skära:

ny_l = l[:]

Hur hittar jag metoder eller attribut för ett objekt?

För en instans x av en användardefinierad klass returnerar dir(x) en alfabetiserad lista över de namn som innehåller de instansattribut, metoder och attribut som definieras av klassen.

Hur kan min kod ta reda på namnet på ett objekt?

Generellt sett kan det inte det, eftersom objekt egentligen inte har några namn. I princip binder assignment alltid ett namn till ett värde; samma sak gäller för def och class statements, men i det fallet är värdet en callable. Tänk på följande kod:

>>> class A:
...     pass
...
>>> B = A
>>> a = B()
>>> b = a
>>> print(b)
<__main__.A object at 0x16D07CC>
>>> print(a)
<__main__.A object at 0x16D07CC>

Man kan hävda att klassen har ett namn: även om den är bunden till två namn och anropas via namnet B rapporteras den skapade instansen fortfarande som en instans av klassen A. Det är dock omöjligt att säga om instansens namn är a eller b, eftersom båda namnen är bundna till samma värde.

Generellt sett bör det inte vara nödvändigt för din kod att ”känna till namnen” på vissa värden. Om du inte avsiktligt skriver introspektiva program är detta vanligtvis en indikation på att det kan vara bra att ändra tillvägagångssätt.

I comp.lang.python gav Fredrik Lundh en gång en utmärkt liknelse som svar på denna fråga:

På samma sätt som du får reda på namnet på katten du hittade på din veranda: katten (objektet) själv kan inte berätta vad den heter, och den bryr sig egentligen inte - så det enda sättet att få reda på vad den heter är att fråga alla dina grannar (namnrymder) om det är deras katt (objekt)…

…. och bli inte förvånad om du upptäcker att den är känd under många namn, eller inget namn alls!

Vad är det med kommatecknets företräde?

Komma är inte en operator i Python. Tänk på denna session:

>>> "a" in "b", "a"
(False, 'a')

Eftersom kommatecknet inte är en operator, utan en separator mellan uttryck, utvärderas ovanstående som om du hade skrivit:

("a" i "b"), "a"

inte:

"a" i ("b", "a")

Detsamma gäller för de olika tilldelningsoperatorerna (=, += etc). De är egentligen inte operatorer utan syntaktiska avgränsare i assignment-satser.

Finns det en motsvarighet till C:s ”?:” ternära operator?

Ja, det finns det. Syntaxen är som följer:

[on_true] if [expression] else [on_false]

x, y = 50, 25
small = x om x < y annars y

Innan denna syntax introducerades i Python 2.5 var ett vanligt idiom att använda logiska operatorer:

[uttryck] och [on_true] eller [on_false]

Detta idiom är dock osäkert, eftersom det kan ge fel resultat när on_true har ett falskt booleskt värde. Därför är det alltid bättre att använda formen ... if ... else ....

Är det möjligt att skriva obfuskerade one-liners i Python?

Ja, det gör jag. Vanligtvis görs detta genom att nesta lambda inom lambda. Se följande tre exempel, något anpassade från Ulf Bartelt:

from functools import reduce

# Primes < 1000
print(list(filter(None,map(lambda y:y*reduce(lambda x,y:x*y!=0,
map(lambda x,y=y:y%x,range(2,int(pow(y,0.5)+1))),1),range(2,1000)))))

# First 10 Fibonacci numbers
print(list(map(lambda x,f=lambda x,f:(f(x-1,f)+f(x-2,f)) if x>1 else 1:
f(x,f), range(10))))

# Mandelbrot set
print((lambda Ru,Ro,Iu,Io,IM,Sx,Sy:reduce(lambda x,y:x+'\n'+y,map(lambda y,
Iu=Iu,Io=Io,Ru=Ru,Ro=Ro,Sy=Sy,L=lambda yc,Iu=Iu,Io=Io,Ru=Ru,Ro=Ro,i=IM,
Sx=Sx,Sy=Sy:reduce(lambda x,y:x+y,map(lambda x,xc=Ru,yc=yc,Ru=Ru,Ro=Ro,
i=i,Sx=Sx,F=lambda xc,yc,x,y,k,f=lambda xc,yc,x,y,k,f:(k<=0)or (x*x+y*y
>=4.0) or 1+f(xc,yc,x*x-y*y+xc,2.0*x*y+yc,k-1,f):f(xc,yc,x,y,k,f):chr(
64+F(Ru+x*(Ro-Ru)/Sx,yc,0,0,i)),range(Sx))):L(Iu+y*(Io-Iu)/Sy),range(Sy
))))(-2.1, 0.7, -1.2, 1.2, 30, 80, 24))
#    \___ ___/  \___ ___/  |   |   |__ lines on screen
#        V          V      |   |______ columns on screen
#        |          |      |__________ maximum of "iterations"
#        |          |_________________ range on y axis
#        |____________________________ range on x axis

Prova inte det här hemma, barn!

Vad betyder snedstrecket(/) i parameterlistan för en funktion?

Ett snedstreck i argumentlistan för en funktion anger att parametrarna före det är enbart positionella. Endast positionella parametrar är de som saknar ett externt användbart namn. När du anropar en funktion som accepterar enbart positionella parametrar mappas argumenten till parametrar enbart baserat på deras position. Exempelvis är divmod() en funktion som accepterar enbart positionella parametrar. Dess dokumentation ser ut så här:

>>> help(divmod)
Help on built-in function divmod in module builtins:

divmod(x, y, /)
    Return the tuple (x//y, x%y).  Invariant: div*y + mod == x.

Snedstrecket i slutet av parameterlistan betyder att båda parametrarna endast är positionella. Således skulle ett anrop av divmod() med nyckelordsargument leda till ett fel:

>>> divmod(x=3, y=4)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: divmod() takes no keyword arguments

Siffror och strängar

Hur anger jag hexadecimala och oktala heltal?

För att ange en oktalsiffra föregår du oktalvärdet med en nolla och sedan ett litet eller stort ”o”. Om du till exempel vill ställa in variabeln ”a” till oktalvärdet ”10” (8 i decimal) skriver du:

>>> a = 0o10
>>> a
8

Hexadecimalt är lika enkelt. Det hexadecimala talet föregås helt enkelt av en nolla och sedan ett ”x” med liten eller stor bokstav. Hexadecimala siffror kan anges med små eller stora bokstäver. Till exempel i Python-tolken:

>>> a = 0xa5
>>> a
165
>>> b = 0XB2
>>> b
178

Varför returnerar -22 // 10 -3?

Den drivs främst av önskemålet att i % j ska ha samma tecken som j. Om du vill ha det, och också vill ha:

i == (i // j) * j + (i % j)

så måste heltalsdivision ge golvet. C kräver också att identiteten ska gälla, och då måste kompilatorer som trunkerar i // j se till att i % j har samma tecken som i.

Det finns få verkliga användningsfall för i % j när j är negativ. När j är positiv finns det många, och i praktiskt taget alla är det mer användbart för i % j att vara >= 0. Om klockan visar 10 nu, vad visade den då för 200 timmar sedan? -190 % 12 == 2 är användbart; -190 % 12 == -10 är en bugg som väntar på att bita.

Hur får jag int literal-attribut istället för SyntaxError?

Att försöka slå upp ett int bokstavligt attribut på normalt sätt ger ett SyntaxError eftersom punkten ses som ett decimaltecken:

>>> 1.__class__
  File "<stdin>", line 1
  1.__class__
   ^
SyntaxError: invalid decimal literal

Lösningen är att skilja bokstav från punkt med antingen ett mellanslag eller en parentes.

>>> 1 .__class__
<class 'int'>
>>> (1).__class__
<class 'int'>

Hur konverterar jag en sträng till ett tal?

För heltal används den inbyggda typkonstruktören int(), t.ex. int('144') == 144. På samma sätt konverterar float() till ett flyttal, t.ex. float('144') == 144.0.

Som standard tolkar dessa talet som decimaltal, så att int('0144') == 144 är sant och int('0x144') ger upphov till ValueError. int(string, base) tar basen att konvertera från som ett andra valfritt argument, så int( '0x144', 16) == 324. Om basen anges som 0 tolkas talet enligt Pythons regler: ett inledande ’0o’ indikerar oktal och ’0x’ indikerar ett hextal.

Använd inte den inbyggda funktionen eval() om allt du behöver är att konvertera strängar till tal. eval() blir betydligt långsammare och det innebär en säkerhetsrisk: någon kan skicka ett Python-uttryck till dig som kan ha oönskade bieffekter. Till exempel skulle någon kunna skicka __import__('os').system("rm -rf $HOME") vilket skulle radera din hemkatalog.

eval() har också effekten att siffror tolkas som Python-uttryck, så att t.ex. eval('09') ger ett syntaxfel eftersom Python inte tillåter inledande ’0’ i ett decimaltal (utom ’0’).

Hur konverterar jag ett tal till en sträng?

För att konvertera t.ex. talet 144 till strängen '144', använd den inbyggda typkonstruktören str(). Om du vill ha en hexadecimal eller oktal representation, använd de inbyggda funktionerna hex() eller oct(). För tjusig formatering, se avsnitten f-strängar och Format String Syntax, t.ex. "{:04d}".format(144) ger '0144' och "{:.3f}".format(1.0/3.0) ger '0.333'.

Hur ändrar jag en sträng på plats?

Det kan du inte, eftersom strängar är oföränderliga. I de flesta situationer bör du helt enkelt konstruera en ny sträng från de olika delar som du vill sätta ihop den av. Men om du behöver ett objekt med möjlighet att modifiera unicode-data på plats kan du försöka använda ett io.StringIO-objekt eller array-modulen:

>>> import io
>>> s = "Hello, world"
>>> sio = io.StringIO(s)
>>> sio.getvalue()
'Hello, world'
>>> sio.seek(7)
7
>>> sio.write("there!")
6
>>> sio.getvalue()
'Hello, there!'

>>> import array
>>> a = array.array('w', s)
>>> print(a)
array('w', 'Hello, world')
>>> a[0] = 'y'
>>> print(a)
array('w', 'yello, world')
>>> a.tounicode()
'yello, world'

Hur använder jag strängar för att anropa funktioner/metoder?

Det finns olika tekniker.

  • Det bästa är att använda en ordbok som mappar strängar till funktioner. Den främsta fördelen med den här tekniken är att strängarna inte behöver matcha namnen på funktionerna. Detta är också den primära teknik som används för att emulera en fallkonstruktion:

    def a():
        pass
    
    def b():
        pass
    
    dispatch = {'go': a, 'stop': b} # Observera avsaknad av parenteser för funcs
    
    dispatch[get_input()]() # Notera efterföljande parenteser för att anropa funktion
    
  • Använd den inbyggda funktionen getattr():

    import foo
    getattr(foo, 'bar')()
    

    Observera att getattr() fungerar på alla objekt, inklusive klasser, klassinstanser, moduler och så vidare.

    Detta används på flera ställen i standardbiblioteket, till exempel så här:

    klass Foo:
        def do_foo(self):
            ...
    
        def do_bar(self):
            ...
    
    f = getattr(foo_instance, 'do_' + opname)
    f()
    
  • Använd locals() för att lösa upp funktionsnamnet:

    def myFunc():
        print("hallå")
    
    fname = "myFunc"
    
    f = locals()[fnamn]
    f()
    

Finns det en motsvarighet till Perls chomp() för att ta bort efterföljande nya rader från strängar?

Du kan använda S.rstrip("\r\n") för att ta bort alla förekomster av en radavslutare från slutet av strängen S utan att ta bort andra efterföljande blanksteg. Om strängen S representerar mer än en rad, med flera tomma rader i slutet, kommer radavslutarna för alla de tomma raderna att tas bort:

>>> lines = ("line 1 \r\n"
...          "\r\n"
...          "\r\n")
>>> lines.rstrip("\n\r")
'line 1 '

Eftersom detta vanligtvis bara är önskvärt när man läser text en rad i taget, fungerar det bra att använda S.rstrip() på detta sätt.

Finns det en motsvarighet till scanf() eller sscanf()?

Inte som sådan.

För enkel parsning av indata är det enklast att dela upp raden i ord som är avgränsade med blanksteg med hjälp av metoden split() för strängobjekt och sedan konvertera decimalsträngar till numeriska värden med hjälp av int() eller float(). split() stöder en valfri parameter ”sep” som är användbar om raden använder något annat än blanksteg som avgränsare.

För mer komplicerad parsning av indata är reguljära uttryck kraftfullare än C:s sscanf och bättre lämpade för uppgiften.

Vad betyder UnicodeDecodeError eller UnicodeEncodeError fel?

Se Unicode HOWTO.

Kan jag avsluta en rå sträng med ett udda antal backslash?

En rå sträng som slutar med ett udda antal backslashes kommer att undkomma strängens quote:

>>> r'C:\this\will\not\work\'
  File "<stdin>", line 1
    r'C:\this\will\not\work\'
    ^
SyntaxError: unterminated string literal (detected at line 1)

Det finns flera lösningar på detta. En är att använda vanliga strängar och dubbla de bakre bindestrecken:

>>> 'C:\\this\\will\\work\\'
'C:\\this\\will\\work\\'

Ett annat sätt är att konkatenera en reguljär sträng som innehåller en undangömd backslash till råsträngen:

>>> r'C:\this\will\work' '\\'
'C:\\this\\will\\work\\'

Det är också möjligt att använda os.path.join() för att lägga till ett backslash i Windows:

>>> os.path.join(r'C:\this\will\work', '')
'C:\\this\\will\\work\\'

Observera att även om en backslash ”escapar” ett citattecken för att avgöra var råsträngen slutar, sker ingen escaping när råsträngens värde tolkas. Det vill säga, det bakre snedstrecket finns kvar i värdet av den råa strängen:

>>> r'backslash\'preserved'
"backslash\\'preserved"

Se även specifikationen i language reference.

Prestanda

Mitt program är för långsamt. Hur snabbar jag upp det?

Det är en svår fråga, generellt sett. Här är en lista på saker att komma ihåg innan du dyker vidare:

  • Prestandaegenskaperna varierar mellan olika Python-implementationer. Denna FAQ fokuserar på CPython.

  • Beteendet kan variera mellan olika operativsystem, särskilt när det gäller I/O eller multi-threading.

  • Du bör alltid hitta de hetaste punkterna i ditt program innan du försöker optimera någon kod (se modulen profile).

  • Genom att skriva benchmark-skript kan du snabbt iterera när du söker efter förbättringar (se modulen timeit).

  • Det är starkt rekommenderat att ha god kodtäckning (genom enhetstestning eller någon annan teknik) innan man eventuellt introducerar regressioner som döljs i sofistikerade optimeringar.

Med detta sagt finns det många knep för att snabba upp Python-kod. Här är några allmänna principer som räcker långt för att nå acceptabla prestandanivåer:

  • Att göra dina algoritmer snabbare (eller byta till snabbare algoritmer) kan ge mycket större fördelar än att försöka strö mikrooptimeringsknep över hela koden.

  • Använd rätt datastrukturer. Studera dokumentationen för modulen Inbyggda typer och collections.

  • När standardbiblioteket tillhandahåller en primitiv metod för att göra något är det troligt (men inte garanterat) att den är snabbare än något alternativ som du kan komma på. Detta gäller i ännu högre grad för primitiver som är skrivna i C, t.ex. inbyggda program och vissa tilläggstyper. Se till exempel till att använda antingen den inbyggda metoden list.sort() eller den relaterade funktionen sorted() för att göra sortering (och se Sorteringstekniker för exempel på måttligt avancerad användning).

  • Abstraktioner tenderar att skapa indirektioner och tvinga tolken att arbeta mer. Om nivåerna av indirekta åtgärder uppväger mängden användbart arbete som utförs, blir ditt program långsammare. Du bör undvika överdriven abstraktion, särskilt i form av små funktioner eller metoder (som också ofta är skadliga för läsbarheten).

Om du har nått gränsen för vad ren Python kan tillåta, finns det verktyg som tar dig längre bort. Till exempel kan Cython kompilera en något modifierad version av Python-kod till ett C-tillägg och kan användas på många olika plattformar. Cython kan dra nytta av kompilering (och valfria typannoteringar) för att göra din kod betydligt snabbare än när den tolkas. Om du är säker på dina färdigheter i C-programmering kan du också skriva en C-tilläggsmodul själv.

Se även

Wikisidan som ägnas åt prestandatips.

Vad är det mest effektiva sättet att konkatenera många strängar tillsammans?

objekten str och bytes är oföränderliga, och därför är det ineffektivt att sammanfoga många strängar eftersom varje sammanfogning skapar ett nytt objekt. I det allmänna fallet är den totala körtidskostnaden kvadratisk i den totala stränglängden.

För att samla många str-objekt är det rekommenderade idiomet att placera dem i en lista och anropa str.join() i slutet:

chunks = []
för s i my_strings:
    chunks.append(s)
resultat = ''.join(chunks)

(ett annat rimligt effektivt idiom är att använda io.StringIO)

För att ackumulera många bytes-objekt är det rekommenderade idiomet att utöka ett bytearray-objekt med hjälp av konkatenering på plats (operatorn +=):

resultat = bytearray()
för b i my_bytes_objects:
    resultat += b

Sekvenser (Tupler/Listor)

Hur konverterar jag mellan tupler och listor?

Typkonstruktören tuple(seq) konverterar vilken sekvens som helst (egentligen vilken iterabel som helst) till en tuple med samma objekt i samma ordning.

Till exempel ger tuple([1, 2, 3]) (1, 2, 3) och tuple('abc') ger ('a', 'b', 'c'). Om argumentet är en tupel görs ingen kopia utan samma objekt returneras, så det är billigt att anropa tuple() när du inte är säker på att ett objekt redan är en tupel.

Typkonstruktören list(seq) konverterar en sekvens eller iterabel till en lista med samma objekt i samma ordning. Till exempel ger list((1, 2, 3)) [1, 2, 3] och list('abc') ger ['a', 'b', 'c']. Om argumentet är en lista gör den en kopia precis som seq[:] skulle göra.

Vad är ett negativt index?

Pythonsekvenser är indexerade med positiva och negativa tal. För positiva tal är 0 det första indexet 1 är det andra indexet och så vidare. För negativa index är -1 det sista indexet och -2 är det näst sista indexet och så vidare. Tänk på seq[-n] som samma sak som seq[len(seq)-n].

Att använda negativa index kan vara mycket praktiskt. Till exempel är S[:-1] hela strängen utom det sista tecknet, vilket är användbart när man vill ta bort den efterföljande nya raden från en sträng.

Hur itererar jag över en sekvens i omvänd ordning?

Använd den inbyggda funktionen reversed():

för x i omvänd(sekvens):
    ...  # gör något med x ...

Detta påverkar inte din ursprungliga sekvens, men skapar en ny kopia med omvänd ordning som du kan iterera över.

Hur tar man bort dubbletter från en lista?

Se Python Cookbook för en lång diskussion om många sätt att göra detta:

Om du inte har något emot att ändra ordning på listan kan du sortera den och sedan skanna från slutet av listan och ta bort dubbletter efter hand:

if mylist:
    mylist.sort()
    sista = mylist[-1]
    for i in range(len(mylist)-2, -1, -1):
        if last == mylist[i]:
            del mylist[i]
        annars:
            last = mylist[i]

Om alla element i listan kan användas som set-nycklar (dvs. de är alla hashable) är detta ofta snabbare

mylist = list(set(mylist))

Detta omvandlar listan till en uppsättning, varvid dubbletter tas bort, och sedan tillbaka till en lista.

Hur tar du bort flera objekt från en lista

Precis som vid borttagning av dubbletter är det en möjlighet att explicit iterera baklänges med ett delete-villkor. Det är dock enklare och snabbare att använda slice replacement med en implicit eller explicit iteration framåt. Här är tre varianter::

mylist[:] = filter(keep_function, mylist)
mylist[:] = (x för x i mylist if keep_condition)
mylist[:] = [x for x in mylist if keep_condition] (x för x i mylist if keep_condition)

Förståelsen av listan kan vara snabbast.

Hur gör du en matris i Python?

Använd en lista:

["detta", 1, "är", "en", "array"]

Listor motsvarar matriser i C eller Pascal när det gäller tidskomplexitet; den främsta skillnaden är att en Python-lista kan innehålla objekt av många olika typer.

Modulen array tillhandahåller också metoder för att skapa matriser av fasta typer med kompakta representationer, men de är långsammare att indexera än listor. Observera också att NumPy och andra tredjepartspaket också definierar array-liknande strukturer med olika egenskaper.

För att få länkade listor i Lisp-stil kan du emulera cons cells med hjälp av tuples:

lisp_list = ("som", ("detta", ("exempel", None) ) )

Om man vill ha mutabilitet kan man använda listor i stället för tupler. Här är motsvarigheten till en Lisp car lisp_list[0] och motsvarigheten till cdr är lisp_list[1]. Gör bara detta om du är säker på att du verkligen behöver det, eftersom det vanligtvis är mycket långsammare än att använda Python-listor.

Hur skapar jag en flerdimensionell lista?

Du har säkert försökt skapa en flerdimensionell matris så här:

>>> A = [[None] * 2] * 3

Detta ser korrekt ut om du skriver ut det:

>>> A
[[None, None], [None, None], [None, None]]

Men när du tilldelar ett värde dyker det upp på flera ställen:

>>> A[0][0] = 5
>>> A
[[5, None], [5, None], [5, None]]

Anledningen är att replikering av en lista med * inte skapar kopior, det skapar bara referenser till de befintliga objekten. Med *3 skapas en lista som innehåller 3 referenser till samma lista med längden två. Ändringar i en rad kommer att visas i alla rader, vilket nästan säkert inte är vad du vill.

Det föreslagna tillvägagångssättet är att först skapa en lista med önskad längd och sedan fylla i varje element med en nyskapad lista:

A = [Ingen] * 3
för i i intervall(3):
    A[i] = [Ingen] * 2

Detta genererar en lista som innehåller 3 olika listor med längden två. Du kan också använda en listkomprehension:

w, h = 2, 3
A = [[None] * w for i in range(h)]

Eller så kan du använda ett tillägg som tillhandahåller en matrisdatatyp; NumPy är den mest kända.

Hur tillämpar jag en metod eller funktion på en sekvens av objekt?

Att anropa en metod eller funktion och ackumulera returvärdena är en lista, en list comprehension är en elegant lösning:

resultat = [obj.method() för obj i mylist]

resultat = [funktion(obj) för obj i mylist]

Om du bara vill köra metoden eller funktionen utan att spara returvärdena räcker det med en vanlig for-loop:

för obj i mylist:
    obj.metod()

för obj i mylist:
    funktion(obj)

Varför ger a_tuple[i] += [’item’] upphov till ett undantag när tillägget fungerar?

Detta beror på en kombination av det faktum att utökade tilldelningsoperatorer är tilldelningsoperatorer och skillnaden mellan muterbara och oföränderliga objekt i Python.

Den här diskussionen gäller i allmänhet när utökade tilldelningsoperatorer tillämpas på element i en tupel som pekar på föränderliga objekt, men vi använder en list och += som vårt exempel.

Om du skrev:

>>> a_tuple = (1, 2)
>>> a_tuple[0] += 1
Traceback (most recent call last):
   ...
TypeError: 'tuple' object does not support item assignment

Anledningen till undantaget bör vara omedelbart klar: 1 läggs till objektet a_tuple[0] pekar på (1), vilket ger resultatobjektet 2, men när vi försöker tilldela resultatet av beräkningen, 2, till elementet 0 i tupeln, får vi ett fel eftersom vi inte kan ändra vad ett element i en tuple pekar på.

Under täckmanteln gör den här utökade uppdragsbeskrivningen ungefär så här:

>>> result = a_tuple[0] + 1
>>> a_tuple[0] = result
Traceback (most recent call last):
  ...
TypeError: 'tuple' object does not support item assignment

Det är tilldelningsdelen av operationen som ger upphov till felet, eftersom en tuple är oföränderlig.

När du skriver något i stil med:

>>> a_tuple = (['foo'], 'bar')
>>> a_tuple[0] += ['item']
Traceback (most recent call last):
  ...
TypeError: 'tuple' object does not support item assignment

Undantaget är lite mer överraskande, och ännu mer överraskande är det faktum att även om det fanns ett fel, fungerade tillägget:

>>> a_tuple[0]
['foo', 'item']

För att förstå varför detta händer måste du veta att (a) om ett objekt implementerar en magisk metod __iadd__(), anropas den när den utökade tilldelningen += utförs, och dess returvärde är det som används i tilldelningsuttalandet; och (b) för listor är __iadd__() likvärdigt med att anropa extend() på listan och returnera listan. Det är därför vi säger att för listor är += en ”kortform” för list.extend():

>>> a_list = []
>>> a_list += [1]
>>> a_list
[1]

Detta är likvärdigt med:

>>> result = a_list.__iadd__([1])
>>> a_list = result

Det objekt som a_list pekar på har muterats och pekaren till det muterade objektet tilldelas tillbaka till a_list. Slutresultatet av tilldelningen är en no-op, eftersom det är en pekare till samma objekt som a_list tidigare pekade på, men tilldelningen sker ändå.

I vårt tuplexempel är det som händer alltså likvärdigt med:

>>> result = a_tuple[0].__iadd__(['item'])
>>> a_tuple[0] = result
Traceback (most recent call last):
  ...
TypeError: 'tuple' object does not support item assignment

__iadd__() lyckas och därmed utökas listan, men även om result pekar på samma objekt som a_tuple[0] redan pekar på, resulterar den slutliga tilldelningen fortfarande i ett fel, eftersom tuples är oföränderliga.

Jag vill göra en komplicerad sortering: kan du göra en Schwartzian-transform i Python?

Tekniken, som tillskrivs Randal Schwartz från Perl-gemenskapen, sorterar elementen i en lista med en metrisk som mappar varje element till dess ”sorteringsvärde”. I Python använder du argumentet key för metoden list.sort():

Isorted = L[:]
Isorted.sort(key=lambda s: int(s[10:15]))

Hur kan jag sortera en lista efter värden från en annan lista?

Slå samman dem till en iterator av tupler, sortera den resulterande listan och välj sedan ut det element du vill ha:

>>> list1 = ["what", "I'm", "sorting", "by"]
>>> list2 = ["something", "else", "to", "sort"]
>>> pairs = zip(list1, list2)
>>> pairs = sorted(pairs)
>>> pairs
[("I'm", 'else'), ('by', 'sort'), ('sorting', 'to'), ('what', 'something')]
>>> result = [x[1] for x in pairs]
>>> result
['else', 'sort', 'to', 'something']

Objekt

Vad är en klass?

En klass är den särskilda objekttyp som skapas genom att en class-sats körs. Klassobjekt används som mallar för att skapa instansobjekt, som innehåller både data (attribut) och kod (metoder) som är specifika för en datatyp.

En klass kan baseras på en eller flera andra klasser, som kallas dess basklass(er). Den ärver då attribut och metoder från sina basklasser. Detta gör att en objektmodell successivt kan förfinas genom arv. Du kanske har en generisk klass Mailbox som tillhandahåller grundläggande åtkomstmetoder för en brevlåda, och underklasser som MboxMailbox, MaildirMailbox, OutlookMailbox som hanterar olika specifika brevlådeformat.

Vad är en metod?

En metod är en funktion på något objekt x som du normalt anropar som x.name(arguments...). Metoder definieras som funktioner inuti klassdefinitionen:

klass C:
    def meth(self, arg):
        return arg * 2 + self.attribute

Vad är jaget?

Self är bara ett konventionellt namn för det första argumentet i en metod. En metod som definieras som meth(self, a, b, c) bör anropas som x.meth(a, b, c) för någon instans x av den klass där definitionen förekommer; den anropade metoden kommer att tro att den anropas som meth(x, a, b, c).

Se även Varför måste ”self” användas explicit i metoddefinitioner och anrop?.

Hur kontrollerar jag om ett objekt är en instans av en viss klass eller av en underklass av den?

Använd den inbyggda funktionen isinstance(obj, cls). Du kan kontrollera om ett objekt är en instans av någon av ett antal klasser genom att ange en tupel istället för en enda klass, t.ex. isinstance(obj, (class1, class2, ...)), och du kan också kontrollera om ett objekt är en av Pythons inbyggda typer, t.ex. isinstance(obj, str) eller isinstance(obj, (int, float, complex)).

Observera att isinstance() även kontrollerar för virtuellt arv från en abstrakt basklass. Så testet kommer att returnera True för en registrerad klass även om den inte direkt eller indirekt har ärvt från den. För att testa för ”true inheritance”, skanna klassens MRO:

from collections.abc import Mapping

class P:
     pass

class C(P):
    pass

Mapping.register(P)
>>> c = C()
>>> isinstance(c, C)        # direct
True
>>> isinstance(c, P)        # indirect
True
>>> isinstance(c, Mapping)  # virtual
True

# Actual inheritance chain
>>> type(c).__mro__
(<class 'C'>, <class 'P'>, <class 'object'>)

# Test for "true inheritance"
>>> Mapping in type(c).__mro__
False

Observera att de flesta program inte använder isinstance() på användardefinierade klasser särskilt ofta. Om du utvecklar klasserna själv är en mer korrekt objektorienterad stil att definiera metoder på klasserna som kapslar in ett visst beteende, istället för att kontrollera objektets klass och göra en annan sak baserat på vilken klass det är. Om man t.ex. har en funktion som gör något:

def search(obj):
    if isinstance(obj, Brevlåda):
        ...  # kod för att söka i en brevlåda
    elif isinstance(obj, Dokument):
        ...  # kod för att söka i ett dokument
    elif ...

Ett bättre tillvägagångssätt är att definiera en search()-metod för alla klasser och bara anropa den:

klass Brevlåda:
    def search(self):
        ...  # kod för att söka i en brevlåda

klass Dokument:
    def search(self):
        ...  # kod för att söka i ett dokument

obj.search()

Vad är delegering?

Delegering är en objektorienterad teknik (även kallad designmönster). Låt oss säga att du har ett objekt x och vill ändra beteendet hos bara en av dess metoder. Du kan skapa en ny klass som tillhandahåller en ny implementering av den metod du är intresserad av att ändra och delegerar alla andra metoder till motsvarande metod i x.

Python-programmerare kan enkelt implementera delegering. Till exempel, följande klass implementerar en klass som beter sig som en fil men konverterar all skriven data till versaler:

klass UpperOut:

    def __init__(self, outfile):
        self._outfile = utfil

    def write(self, s):
        self._outfile.write(s.upper())

    def __getattr__(self, namn):
        return getattr(self._outfile, namn)

Här omdefinierar klassen UpperOut metoden write() så att argumentsträngen konverteras till versaler innan den underliggande metoden self._outfile.write() anropas. Alla andra metoder delegeras till det underliggande objektet self._outfile. Delegeringen sker via metoden __getattr__(); se språkreferensen för mer information om hur man kontrollerar attributåtkomst.

Observera att för mer allmänna fall kan delegering bli knepigare. När attribut måste ställas in såväl som hämtas måste klassen definiera en __setattr__()-metod också, och den måste göra det noggrant. Den grundläggande implementationen av __setattr__() är ungefär likvärdig med följande:

klass X:
    ...
    def __setattr__(self, namn, värde):
        self.__dict__[name] = värde
    ...

Många __setattr__()-implementationer anropar object.__setattr__() för att sätta ett attribut på self utan att orsaka oändlig rekursion:

klass X:
    def __setattr__(self, namn, värde):
        # Egen logik här...
        object.__setattr__(self, namn, värde)

Alternativt är det möjligt att ställa in attribut genom att infoga poster i self.__dict__ direkt.

Hur anropar jag en metod som är definierad i en basklass från en härledd klass som utökar den?

Använd den inbyggda funktionen super():

klass Avledda(Bas):
    def meth(self):
        super().meth() # anropar Base.meth

I exemplet kommer super() automatiskt att bestämma från vilken instans den anropades (värdet self), leta upp method resolution order (MRO) med type(self).__mro__ och returnera nästa i raden efter Derived i MRO: Base.

Hur kan jag organisera min kod för att göra det lättare att ändra basklassen?

Du kan tilldela basklassen till ett alias och härleda från aliaset. Då behöver man bara ändra det värde som tilldelats aliaset. För övrigt är detta trick också praktiskt om du vill bestämma dynamiskt (t.ex. beroende på tillgängliga resurser) vilken basklass som ska användas. Exempel:

klass Bas:
    ...

BaseAlias = Bas

klass Avledda(BaseAlias):
    ...

Hur skapar jag statiska klassdata och statiska klassmetoder?

Både statiska data och statiska metoder (i den mening som avses i C++ eller Java) stöds i Python.

För statiska data definierar du helt enkelt ett klassattribut. För att tilldela ett nytt värde till attributet måste du uttryckligen använda klassnamnet i tilldelningen:

klass C:
    count = 0 # antal gånger C.__init__ anropas

    def __init__(self):
        C.count = C.count + 1

    def getcount(self):
        return C.count # eller return self.count

c.count refererar också till C.count för varje c så att isinstance(c, C) gäller, såvida det inte åsidosätts av c själv eller av någon klass på basklasssökvägen från c.__class__ tillbaka till C.

Varning: Inom en metod i C skapar en tilldelning som self.count = 42 en ny och orelaterad instans med namnet ”count” i self egen dict. Ombindning av ett klass-statiskt datanamn måste alltid specificera klassen oavsett om det är inom en metod eller inte:

C.count = 314

Statiska metoder är möjliga:

klass C:
    @statiskmetod
    def static(arg1, arg2, arg3):
        # Ingen parameter 'self'!
        ...

Ett mycket enklare sätt att få effekten av en statisk metod är dock via en enkel funktion på modulnivå:

def getcount():
    returnera C.count

Om din kod är strukturerad så att du definierar en klass (eller en nära relaterad klasshierarki) per modul, ger detta den önskade inkapslingen.

Hur kan jag överbelasta konstruktörer (eller metoder) i Python?

Det här svaret gäller egentligen alla metoder, men frågan dyker oftast upp först i samband med konstruktörer.

I C++ skulle du skriva

klass C {
    C() { cout << "Inga argument\n"; }
    C(int i) { cout << "Argumentet är " << i << "\n"; }
}

I Python måste man skriva en enda konstruktor som fångar upp alla fall med hjälp av standardargument. Till exempel:

klass C:
    def __init__(self, i=None):
        om i är None:
            print("Inga argument")
        else:
            print("Argumentet är", i)

Detta är inte helt likvärdigt, men tillräckligt nära i praktiken.

Du kan också prova en argumentlista med variabel längd, t.ex.

def __init__(self, *args):
    ...

Samma tillvägagångssätt fungerar för alla metoddefinitioner.

Jag försöker använda __spam och jag får ett fel om _SomeClassName__spam.

Variabelnamn med dubbla inledande understreck ”manglas” för att ge ett enkelt men effektivt sätt att definiera privata klassvariabler. Alla identifierare av formen __spam (minst två inledande understrykningar, högst en efterföljande understrykning) ersätts textuellt med _classname__spam, där classname är det aktuella klassnamnet med eventuella inledande understrykningar borttagna.

Identifieraren kan användas oförändrad inom klassen, men för att komma åt den utanför klassen måste det manglade namnet användas:

klass A:
    def __one(self):
        returnera 1
    def två(self):
        return 2 * self.__one()

klass B(A):
    def three(self):
        return 3 * self._A__one()

four = 4 * A()._A__one()

I synnerhet garanterar detta inte sekretess eftersom en utomstående användare fortfarande avsiktligt kan komma åt det privata attributet; många Python-programmerare bryr sig aldrig om att använda privata variabelnamn alls.

Se även

Specifikationerna för privat namnmangling för detaljer och specialfall.

Min klass definierar __del__ men den anropas inte när jag tar bort objektet.

Det finns flera möjliga orsaker till detta.

Satsen del anropar inte nödvändigtvis __del__() – den minskar helt enkelt objektets referensantal, och om det når noll anropas __del__().

Om dina datastrukturer innehåller cirkulära länkar (t.ex. ett träd där varje barn har en föräldrareferens och varje förälder har en lista med barn) kommer referensantalet aldrig att gå tillbaka till noll. Då och då kör Python en algoritm för att upptäcka sådana cykler, men skräpsamlaren kan köras en tid efter att den sista referensen till din datastruktur försvinner, så din __del__()-metod kan anropas vid en obekväm och slumpmässig tidpunkt. Detta är obekvämt om du försöker återskapa ett problem. Ännu värre är att ordningen i vilken objektets __del__()-metoder utförs är godtycklig. Du kan köra gc.collect() för att tvinga fram en insamling, men det finns patologiska fall där objekt aldrig kommer att samlas in.

Trots cykelinsamlaren är det fortfarande en bra idé att definiera en explicit close() -metod på objekt som ska anropas när du är klar med dem. Metoden close() kan sedan ta bort attribut som refererar till underobjekt. Anropa inte __del__() direkt – __del__() ska anropa close() och close() ska se till att den kan anropas mer än en gång för samma objekt.

Ett annat sätt att undvika cykliska referenser är att använda modulen weakref, som gör att du kan peka på objekt utan att öka deras referensantal. Träddatastrukturer, till exempel, bör använda svaga referenser för sina föräldra- och syskonreferenser (om de behöver dem!).

Slutligen, om din __del__()-metod ger upphov till ett undantag, skrivs ett varningsmeddelande ut till sys.stderr.

Hur får jag en lista över alla instanser av en viss klass?

Python håller inte reda på alla instanser av en klass (eller av en inbyggd typ). Du kan programmera klassens konstruktor att hålla reda på alla instanser genom att hålla en lista med svaga referenser till varje instans.

Varför verkar resultatet av id() inte vara unikt?

Inbyggda id() returnerar ett heltal som garanterat är unikt under objektets livstid. Eftersom detta i CPython är objektets minnesadress händer det ofta att efter att ett objekt har raderats från minnet, allokeras nästa nyskapade objekt på samma position i minnet. Detta illustreras av detta exempel:

>>> id(1000)
13901272
>>> id(2000)
13901272

De två id:na tillhör olika heltalsobjekt som skapas före och raderas omedelbart efter att anropet id() har utförts. För att vara säker på att objekt vars id du vill undersöka fortfarande är vid liv, skapar du en annan referens till objektet:

>>> a = 1000; b = 2000
>>> id(a)
13901272
>>> id(b)
13891296

När kan jag förlita mig på identitetstester med operatorn is?

Operatorn is testar objektets identitet. Testet a is b är likvärdigt med id(a) == id(b).

Den viktigaste egenskapen hos ett identitetstest är att ett objekt alltid är identiskt med sig självt, a is a returnerar alltid True. Identitetstester är vanligtvis snabbare än likhetstester. Och till skillnad från likhetstester är identitetstester garanterade att returnera en boolean True eller False.

Identitetstester kan dock endast ersättas med likhetstester när objektets identitet är säkerställd. I allmänhet finns det tre omständigheter där identiteten är garanterad:

  1. Tilldelningar skapar nya namn men ändrar inte objektets identitet. Efter tilldelningen new = old är det garanterat att new is old.

  2. Att placera ett objekt i en behållare som lagrar objektreferenser ändrar inte objektets identitet. Efter listtilldelningen s[0] = x är det garanterat att s[0] is x.

  3. Om ett objekt är en singleton betyder det att det bara kan finnas en instans av objektet. Efter tilldelningarna a = None och b = None är det garanterat att a is b eftersom None är en singleton.

Under de flesta andra omständigheter är identitetstester inte tillrådliga och likhetstester är att föredra. I synnerhet bör identitetstester inte användas för att kontrollera konstanter som int och str som inte garanterat är singletoner:

>>> a = 1000
>>> b = 500
>>> c = b + 500
>>> a is c
False

>>> a = 'Python'
>>> b = 'Py'
>>> c = b + 'thon'
>>> a is c
False

På samma sätt är nya instanser av mutabla behållare aldrig identiska:

>>> a = []
>>> b = []
>>> a is b
False

I standardbibliotekskoden ser du flera vanliga mönster för korrekt användning av identitetstester:

  1. Som rekommenderas av PEP 8, är ett identitetstest det föredragna sättet att kontrollera för None. Detta låter som vanlig engelska i koden och undviker förvirring med andra objekt som kan ha booleska värden som utvärderas till false.

  2. Det kan vara svårt att upptäcka valfria argument när None är ett giltigt inmatningsvärde. I sådana situationer kan du skapa ett singleton sentinel-objekt som garanterat skiljer sig från andra objekt. Så här kan du till exempel implementera en metod som beter sig som dict.pop():

    _sentinel = objekt()
    
    def pop(self, key, default=_sentinel):
        if nyckel i self:
            värde = self[nyckel]
            del self[nyckel]
            returnera värde
        om standard är _sentinel:
            raise KeyError(nyckel)
        returnera standard
    
  3. Containerimplementationer behöver ibland komplettera likhetstester med identitetstester. Detta förhindrar att koden blir förvirrad av objekt som float('NaN') som inte är lika med sig själva.

Här är till exempel implementeringen av collections.abc.Sequence.__contains__():

def __contains__(self, värde):
    for v in self:
        om v är värde eller v == värde:
            return True
    returnera Falskt

Hur kan en underklass styra vilka data som lagras i en oföränderlig instans?

När du subklassar en oföränderlig typ ska du åsidosätta metoden __new__() i stället för metoden __init__(). Den senare körs bara efter att en instans har skapats, vilket är för sent för att ändra data i en oföränderlig instans.

Alla dessa oföränderliga klasser har en annan signatur än sin överordnade klass:

from datetime import date

class FirstOfMonthDate(date):
    "Always choose the first day of the month"
    def __new__(cls, year, month, day):
        return super().__new__(cls, year, month, 1)

class NamedInt(int):
    "Allow text names for some numbers"
    xlat = {'zero': 0, 'one': 1, 'ten': 10}
    def __new__(cls, value):
        value = cls.xlat.get(value, value)
        return super().__new__(cls, value)

class TitleStr(str):
    "Convert str to name suitable for a URL path"
    def __new__(cls, s):
        s = s.lower().replace(' ', '-')
        s = ''.join([c for c in s if c.isalnum() or c == '-'])
        return super().__new__(cls, s)

Klasserna kan användas på följande sätt:

>>> FirstOfMonthDate(2012, 2, 14)
FirstOfMonthDate(2012, 2, 1)
>>> NamedInt('ten')
10
>>> NamedInt(20)
20
>>> TitleStr('Blog: Why Python Rocks')
'blog-why-python-rocks'

Hur cachar jag metodanrop?

De två viktigaste verktygen för att cachelagra metoder är functools.cached_property() och functools.lru_cache(). Det förstnämnda lagrar resultat på instansnivå och det sistnämnda på klassnivå.

Metoden cached_property fungerar bara med metoder som inte tar några argument. Den skapar inte någon referens till instansen. Det cachade metodresultatet sparas bara så länge som instansen är vid liv.

Fördelen är att när en instans inte längre används kommer det cachade metodresultatet att släppas direkt. Nackdelen är att om instanser ackumuleras, så kommer även de ackumulerade metodresultaten att göra det. De kan växa utan gräns.

Metoden lru_cache fungerar med metoder som har hashable-argument. Den skapar en referens till instansen om inte särskilda ansträngningar görs för att skicka in svaga referenser.

Fördelen med den senast använda algoritmen är att cacheminnet begränsas av den angivna maxstorleken. Nackdelen är att instanser hålls vid liv tills de åldras ur cacheminnet eller tills cacheminnet rensas.

Detta exempel visar de olika teknikerna:

klass Väder:
    "Leta upp väderinformation på en statlig webbplats"

    def __init__(self, station_id):
        self._station_id = station_id
        # _station_id är privat och oföränderligt

    def current_temperature(self):
        "Senaste timobservationen"
        # Cacha inte detta eftersom gamla resultat
        # kan vara föråldrade.

    @cachad_egenskap
    def plats(själv):
        "Returnera stationens longitud/latitudkoordinater"
        # Resultatet beror bara på station_id

    @lru_cache(maxstorlek=20)
    def historic_rainfall(self, date, units='mm'):
        "Nederbörd på ett givet datum"
        # Beror på station_id, datum och enheter.

I exemplet ovan antas att station_id aldrig ändras. Om de relevanta instansattributen är föränderliga kan cached_property-metoden inte fungera eftersom den inte kan upptäcka ändringar av attributen.

För att lru_cache-strategin ska fungera när station_id är föränderligt måste klassen definiera metoderna __eq__() och __hash__() så att cachen kan upptäcka relevanta attributuppdateringar:

klass Väder:
    "Exempel med en föränderlig stationsidentifierare"

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

    def change_station(self, station_id):
        self.station_id = station_id

    def __eq__(self, other):
        return self.station_id == other.station_id

    def __hash__(self):
        return hash(self.station_id)

    @lru_cache(maxstorlek=20)
    def historic_rainfall(self, date, units='cm'):
        "Nederbörd på ett givet datum
        # Beror på station_id, datum och enheter.

Moduler

Hur skapar jag en .pyc-fil?

När en modul importeras för första gången (eller när källfilen har ändrats sedan den aktuella kompilerade filen skapades) ska en fil med namnet .pyc som innehåller den kompilerade koden skapas i en underkatalog med namnet __pycache__ i katalogen som innehåller filen .py. Filen .pyc har ett filnamn som börjar med samma namn som filen .py och slutar med .pyc, med en mittdel som beror på den specifika python-binärfil som skapade den. (Se PEP 3147 för mer information.)

En anledning till att en .pyc -fil inte skapas kan vara ett behörighetsproblem med katalogen som innehåller källfilen, vilket innebär att underkatalogen __pycache__ inte kan skapas. Detta kan till exempel inträffa om du utvecklar som en användare men kör som en annan, till exempel om du testar med en webbserver.

Om inte miljövariabeln PYTHONDONTWRITEBYTECODE är inställd, skapas en .pyc-fil automatiskt om du importerar en modul och Python har möjlighet (behörigheter, ledigt utrymme, etc…) att skapa en __pycache__ underkatalog och skriva den kompilerade modulen till den underkatalogen.

Att köra Python på ett skript på högsta nivå betraktas inte som en import och ingen .pyc kommer att skapas. Om du till exempel har en modul på högsta nivå foo.py som importerar en annan modul xyz.py, kommer en .pyc att skapas för xyz när du kör foo (genom att skriva python foo.py som ett skal-kommando) skapas en .pyc för xyz eftersom xyz importeras, men ingen .pyc-fil skapas för foo eftersom foo.py inte importeras.

Om du behöver skapa en .pyc-fil för foo – det vill säga skapa en .pyc-fil för en modul som inte importeras – kan du göra det med modulerna py_compile och compileall.

Modulen py_compile kan manuellt kompilera valfri modul. Ett sätt är att använda compile()-funktionen i den modulen interaktivt:

>>> import py_compile
>>> py_compile.compile('foo.py')

Detta kommer att skriva .pyc till en __pycache__ underkatalog på samma plats som foo.py (eller så kan du åsidosätta det med den valfria parametern cfile).

Du kan också automatiskt kompilera alla filer i en katalog eller flera kataloger med modulen compileall. Du kan göra det från skalprompten genom att köra compileall.py och ange sökvägen till en katalog som innehåller Python-filer som ska kompileras:

python -m compileall .

Hur hittar jag det aktuella modulnamnet?

En modul kan ta reda på sitt eget modulnamn genom att titta på den fördefinierade globala variabeln __name__. Om denna har värdet '__main__' körs programmet som ett skript. Många moduler som vanligtvis används genom att importera dem tillhandahåller också ett kommandoradsgränssnitt eller ett självtest, och kör endast denna kod efter att ha kontrollerat __name__:

def main():
    print('Kör test...')
    ...

if __name__ == '__main__':
    main()

Hur kan jag ha moduler som ömsesidigt importerar varandra?

Anta att du har följande moduler:

foo.py:

from bar import bar_var
foo_var = 1

bar.py:

from foo import foo_var
bar_var = 2

Problemet är att tolken kommer att utföra följande steg:

  • main imports foo

  • Tomma globaler för foo skapas

  • foo kompileras och börjar exekveras

  • foo imports bar

  • Tomma globaler för bar skapas

  • bar kompileras och börjar exekveras

  • bar imports foo (vilket är en no-op eftersom det redan finns en modul som heter foo)

  • Importmekanismen försöker läsa foo_var från foo globaler, för att ställa in bar.foo_var = foo.foo_var

Det sista steget misslyckas, eftersom Python inte är klar med tolkningen av foo ännu och den globala symbolordboken för foo fortfarande är tom.

Samma sak händer när man använder import foo och sedan försöker komma åt foo.foo_var i global kod.

Det finns (minst) tre möjliga lösningar på det här problemet.

Guido van Rossum rekommenderar att man undviker all användning av from <module> import ... och att all kod placeras i funktioner. Initialiseringar av globala variabler och klassvariabler bör endast använda konstanter eller inbyggda funktioner. Detta innebär att allt från en importerad modul refereras som <module>.<name>.

Jim Roskind föreslår att du utför stegen i följande ordning i varje modul:

  • export (globaler, funktioner och klasser som inte behöver importerade basklasser)

  • import statements

  • aktiv kod (inklusive globaler som initieras från importerade värden).

Van Rossum tycker inte så mycket om den här metoden eftersom importen hamnar på ett konstigt ställe, men den fungerar.

Matthias Urlichs rekommenderar att du omstrukturerar din kod så att den rekursiva importen inte är nödvändig i första hand.

Dessa lösningar utesluter inte varandra.

__import__(’x.y.z’) returnerar <modul ’x’>; hur får jag z?

Överväg att använda bekvämlighetsfunktionen import_module() från importlib istället:

z = importlib.import_module('x.y.z')

När jag redigerar en importerad modul och importerar den på nytt visas inte ändringarna. Varför händer detta?

Av både effektivitets- och konsekvensskäl läser Python bara modulfilen första gången en modul importeras. Om den inte gjorde det, i ett program som består av många moduler där var och en importerar samma grundmodul, skulle grundmodulen analyseras och omanalyseras många gånger. Gör så här för att tvinga fram omläsning av en ändrad modul:

import importlib
import modname
importlib.reload(modname)

Varning: denna teknik är inte 100% fool-säker. I synnerhet moduler som innehåller uttalanden som

from modname import some_objects

kommer att fortsätta att fungera med den gamla versionen av de importerade objekten. Om modulen innehåller klassdefinitioner kommer befintliga klassinstanser inte att uppdateras för att använda den nya klassdefinitionen. Detta kan resultera i följande paradoxala beteende:

>>> import importlib
>>> import cls
>>> c = cls.C()                # Create an instance of C
>>> importlib.reload(cls)
<module 'cls' from 'cls.py'>
>>> isinstance(c, cls.C)       # isinstance is false?!?
False

Problemets natur klargörs om man skriver ut klassobjektens ”identitet”:

>>> hex(id(c.__class__))
'0x7352a0'
>>> hex(id(cls.C))
'0x4198d0'