4. Exekveringsmodell¶
4.1. Uppbyggnad av ett program¶
Ett Python-program är uppbyggt av kodblock. Ett block är en bit Python-programtext som exekveras som en enhet. Följande är block: en modul, en funktionskropp och en klassdefinition. Varje kommando som skrivs interaktivt är ett block. En skriptfil (en fil som ges som standardinmatning till tolken eller som anges som ett kommandoradsargument till tolken) är ett kodblock. Ett skriptkommando (ett kommando som anges på tolkens kommandorad med alternativet -c
) är ett kodblock. En modul som körs som ett toppnivåskript (som modul __main__
) från kommandoraden med -m
-argumentet är också ett kodblock. Strängargumentet som skickas till de inbyggda funktionerna eval()
och exec()
är ett kodblock.
Ett kodblock exekveras i en exekveringsram. En frame innehåller viss administrativ information (används för felsökning) och bestämmer var och hur exekveringen fortsätter efter att kodblockets exekvering har slutförts.
4.2. Namngivning och bindning¶
4.2.1. Bindning av namn¶
Namn refererar till objekt. Namn introduceras genom namnbindningsoperationer.
Följande konstruktioner binder namn:
formella parametrar till funktioner,
klassdefinitioner,
funktionsdefinitioner,
uppdragsuttryck,
targets som är identifierare om de förekommer i ett uppdrag:
import
uttalanden.type
uttalanden.
Satsen import
av formen from ... import *
binder alla namn som definieras i den importerade modulen, utom de som börjar med ett understreck. Denna form får endast användas på modulnivå.
Ett mål som förekommer i en del
-sats anses också vara bundet för detta ändamål (även om den faktiska semantiken är att avbinda namnet).
Varje assignment- eller importsats sker inom ett block som definieras av en klass- eller funktionsdefinition eller på modulnivå (kodblocket på högsta nivån).
Om ett namn är bundet i ett block är det en lokal variabel i det blocket, såvida det inte deklarerats som nonlocal
eller global
. Om ett namn är bundet på modulnivå är det en global variabel. (Variablerna i modulens kodblock är lokala och globala.) Om en variabel används i ett kodblock men inte definieras där, är den en fri variabel.
Varje förekomst av ett namn i programtexten hänvisar till den bindning av namnet som fastställs genom följande regler för namnupplösning.
4.2.2. Upplösning av namn¶
En scope definierar synligheten för ett namn inom ett block. Om en lokal variabel definieras i ett block, omfattar dess scope det blocket. Om definitionen sker i ett funktionsblock, sträcker sig räckvidden till alla block som ingår i det definierande blocket, såvida inte ett block som ingår i blocket inför en annan bindning för namnet.
När ett namn används i ett kodblock löses det upp med hjälp av det närmast omgivande scopet. Uppsättningen av alla sådana scopes som är synliga för ett kodblock kallas blockets environment.
Om ett namn inte hittas alls, uppstår ett NameError
undantag. Om det aktuella omfånget är ett funktionsomfång och namnet hänvisar till en lokal variabel som ännu inte har bundits till ett värde vid den punkt där namnet används, kommer ett UnboundLocalError
undantag att uppstå. UnboundLocalError
är en subklass av NameError
.
Om en namnbindningsoperation sker var som helst inom ett kodblock, behandlas alla användningar av namnet inom blocket som referenser till det aktuella blocket. Detta kan leda till fel när ett namn används inom ett block innan det är bundet. Denna regel är subtil. Python saknar deklarationer och tillåter att namnbindning sker var som helst inom ett kodblock. De lokala variablerna i ett kodblock kan bestämmas genom att skanna hela texten i blocket för namnbindningsoperationer. Se FAQ-posten om UnboundLocalError för exempel.
Om global
-satsen förekommer i ett block, hänvisar alla användningar av de namn som anges i satsen till bindningarna av dessa namn i namnrymden på översta nivån. Namn löses i namnrymden på översta nivån genom att söka i den globala namnrymden, dvs. namnrymden för den modul som innehåller kodblocket, och namnrymden för inbyggda program, namnrymden för modulen builtins
. Den globala namnrymden genomsöks först. Om namnen inte hittas där, söks därefter i builtins-namnrymden. Om namnen inte heller hittas i namnrymden för builtins skapas nya variabler i den globala namnrymden. Global-satsen måste föregå alla användningar av de listade namnen.
Satsen global
har samma omfattning som en namnbindningsoperation i samma block. Om det närmaste omslutande området för en fri variabel innehåller en global-sats, behandlas den fria variabeln som en global.
Satsen nonlocal
gör att motsvarande namn refererar till tidigare bundna variabler i närmaste omslutande funktionsområde. SyntaxError
tas upp vid kompilering om det angivna namnet inte finns i något omslutande funktionsområde. Type parameters kan inte återbindas med satsen nonlocal
.
Namnrymden för en modul skapas automatiskt första gången en modul importeras. Huvudmodulen för ett skript kallas alltid __main__
.
Klassdefinitionsblock och argument till exec()
och eval()
är speciella i samband med namnupplösning. En klassdefinition är en körbar sats som kan använda och definiera namn. Dessa referenser följer de normala reglerna för namnupplösning med undantaget att obundna lokala variabler söks upp i den globala namnrymden. Namnrymden för klassdefinitionen blir klassens attributordbok. Omfattningen av namn som definieras i ett klassblock är begränsad till klassblocket; den omfattar inte metodernas kodblock. Detta inkluderar förståelser och generatoruttryck, men det inkluderar inte annotation scopes, som har tillgång till sina omslutande klassomfång. Detta innebär att följande kommer att misslyckas:
klass A:
a = 42
b = list(a + i för i i intervall(10))
Följande kommer dock att lyckas:
klass A:
typ Alias = Nested
klass Nested: pass
print(A.Alias.__value__) # <typ 'A.Nested'>
4.2.3. Omfattningar av anteckningar¶
Annotations, type parameter lists och type
introducerar annotation scopes, som mestadels beter sig som funktion scopes, men med några undantag som diskuteras nedan.
Annotationsscopes används i följande sammanhang:
Typ-parameterlistor för generiska typalias.
Typ parameterlistor för generiska funktioner. En generisk funktions annotationer exekveras inom annotationens scope, men dess standardvärden och dekoratorer gör det inte.
Typ parameterlistor för generiska klasser. En generisk klass basklasser och nyckelordsargument exekveras inom annotationens scope, men dess dekoratorer gör det inte.
Gränser, begränsningar och standardvärden för typparametrar (slently evaluated).
Värdet av typalias (slentrianmässigt utvärderad).
Annotationsscopes skiljer sig från funktionsscopes på följande sätt:
Annotationsscopes har tillgång till sitt omslutande klassnamnrum. Om ett annotationsscope ligger omedelbart inom ett klass-scope, eller inom ett annat annotationsscope som ligger omedelbart inom ett klass-scope, kan koden i annotationsscopet använda namn som definieras i klass-scopet som om den exekverades direkt i klassens kropp. Detta står i kontrast till vanliga funktioner som definieras inom klasser, vilka inte kan komma åt namn som definieras i klassens scope.
Uttryck i annotationsscopes kan inte innehålla
yield
,yield from
,await
, eller:=
-uttryck. (Dessa uttryck är tillåtna i andra scopes som ingår i annotationsscopet)Namn som definieras i annotationsscopes kan inte reboundas med
nonlocal
-satser i inner scopes. Detta gäller endast typparametrar, eftersom inga andra syntaktiska element som kan förekomma inom annotationsscopes kan introducera nya namn.Även om annotationsscopes har ett internt namn, återspeglas inte detta namn i kvalificerat namn för objekt som definieras inom scopet. Istället är
__qualname__
för sådana objekt som om objektet definierades i det omslutande scopet.
Tillagd i version 3.12: Annotation scopes introducerades i Python 3.12 som en del av PEP 695.
Ändrad i version 3.13: Annotation scopes används också för standardvärden för typparametrar, vilket introducerades av PEP 696.
4.2.4. Ledig utvärdering¶
De flesta annotationsscope utvärderas slentrianmässigt. Detta inkluderar annoteringar, värdena för typalias som skapats genom type
-satsen och gränser, begränsningar och standardvärden för typvariabler som skapats genom type parameter syntax. Detta innebär att de inte utvärderas när typaliaset eller typvariabeln skapas, eller när objektet med annoteringar skapas. Istället utvärderas de bara när det är nödvändigt, till exempel när attributet __value__
på ett typalias används.
Exempel:
>>> type Alias = 1/0
>>> Alias.__value__
Traceback (most recent call last):
...
ZeroDivisionError: division by zero
>>> def func[T: 1/0](): pass
>>> T = func.__type_params__[0]
>>> T.__bound__
Traceback (most recent call last):
...
ZeroDivisionError: division by zero
Här utlöses undantaget endast när attributet __value__
för typaliaset eller attributet __bound__
för typvariabeln används.
Detta beteende är främst användbart för referenser till typer som ännu inte har definierats när typaliaset eller typvariabeln skapas. Latent utvärdering gör det till exempel möjligt att skapa ömsesidigt rekursiva typalias:
from typing import Literal
type SimpleExpr = int | Parenthesized
type Parenthesized = tuple[Literal["("], Expr, Literal[")"]]
type Expr = SimpleExpr | tuple[SimpleExpr, Literal["+", "-"], Expr]
Latent utvärderade värden utvärderas i annotation scope, vilket innebär att namn som förekommer inuti det latent utvärderade värdet slås upp som om de användes i det omedelbart omgivande scopet.
Tillagd i version 3.12.
4.2.5. Inbyggnader och begränsat utförande¶
Användare bör inte röra __builtins__
; det är enbart en implementationsdetalj. Användare som vill åsidosätta värden i namnrymden för builtins bör import
modulen builtins
och ändra dess attribut på lämpligt sätt.
Namnrymden för inbyggda program som är associerad med exekveringen av ett kodblock hittas genom att slå upp namnet __builtins__
i dess globala namnrymd; detta bör vara en ordbok eller en modul (i det senare fallet används modulens ordbok). Som standard är __builtins__
den inbyggda modulen builtins
när den finns i modulen __main__
; när den finns i någon annan modul är __builtins__
ett alias för ordlistan i själva modulen builtins
.
4.2.6. Interaktion med dynamiska funktioner¶
Namnupplösning av fria variabler sker vid körning, inte vid kompilering. Detta innebär att följande kod kommer att skriva ut 42:
i = 10
def f():
print(i)
i = 42
f()
Funktionerna eval()
och exec()
har inte tillgång till hela miljön för att slå upp namn. Namn kan slås upp i anroparens lokala och globala namnrymder. Fria variabler slås inte upp i den närmast omgivande namnrymden, utan i den globala namnrymden. [1] Funktionerna exec()
och eval()
har valfria argument för att åsidosätta det globala och den lokala namnrymden. Om bara en namnrymd anges används det för båda.
4.3. Undantag¶
Undantag är ett sätt att bryta sig ur det normala kontrollflödet i ett kodblock för att hantera fel eller andra exceptionella förhållanden. Ett undantag utlöses vid den punkt där felet upptäcks; det kan hanteras av det omgivande kodblocket eller av något kodblock som direkt eller indirekt åberopade det kodblock där felet inträffade.
Python-tolken skapar ett undantag när den upptäcker ett körtidsfel (t.ex. division med noll). Ett Python-program kan också uttryckligen skapa ett undantag med raise
-satsen. Undantagshanterare specificeras med try
… except
-satsen. Klausulen finally
i en sådan sats kan användas för att specificera upprensningskod som inte hanterar undantaget, men som exekveras oavsett om ett undantag inträffade eller inte i den föregående koden.
Python använder ”termineringsmodellen” för felhantering: en undantagshanterare kan ta reda på vad som hände och fortsätta exekveringen på en yttre nivå, men den kan inte reparera orsaken till felet och försöka igen (förutom genom att skriva in den felande kodbiten från början).
När ett undantag inte hanteras alls, avslutar tolken exekveringen av programmet eller återgår till sin interaktiva huvudslinga. I båda fallen skrivs en stack traceback ut, utom när undantaget är SystemExit
.
Undantag identifieras av klassinstanser. Klausulen except
väljs beroende på klassen i instansen: den måste referera till klassen i instansen eller en icke-virtuell basklass därav. Instansen kan tas emot av hanteraren och kan innehålla ytterligare information om det exceptionella tillståndet.
Anteckning
Undantagsmeddelanden är inte en del av Python API. Deras innehåll kan ändras från en version av Python till nästa utan förvarning och bör inte användas av kod som körs under flera versioner av tolken.
Se även beskrivningen av try
-satsen i avsnitt try-satsen och raise
-satsen i avsnitt raise-satsen.
Fotnoter