Funktionsannonser är en Python 3-funktion som låter dig lägga till godtyckliga metadata för att fungera som argument och returnera värde. De var en del av den ursprungliga Python 3.0-specifikationen.
I den här handledningen visar jag dig hur du kan utnyttja funktionella noteringar och kombinera dem med dekoratörer. Du kommer också att lära dig om fördelarna och nackdelarna med funktionsannonser, när det är lämpligt att använda dem, och när det är bäst att använda andra mekanismer som docstrings och vanliga dekoratorer.
Funktionsannonser anges i PEP-3107. Huvudmotivationen var att tillhandahålla ett vanligt sätt att associera metadata för att fungera med argument och returvärde. En hel del medlemmar i samhället hittade nya användarfall men använde olika metoder som anpassade dekoratörer, anpassade docstringformat och lägga till anpassade attribut till funktionobjektet.
Det är viktigt att förstå att Python inte välsignar annotationerna med någon semantik. Det ger rent ut ett gott syntaktiskt stöd för att associera metadata samt ett enkelt sätt att komma åt det. Annoteringar är också helt frivilliga.
Låt oss ta en titt på ett exempel. Här är en funktion foo () Det tar tre argument som heter a, b och c och skriver ut summan. Observera att foo () returnerar ingenting. Det första argumentet en är inte antecknad. Det andra argumentet b är kommenterad med strängen "annotera b" och det tredje argumentet c är annoterad med typ int. Returvärdet är annoterat med typen flyta. Notera syntaxen "-> för att kommentera returvärdet.
python def foo (a, b: 'annotera b', c: int) -> float: print (a + b + c)
Anmärkningarna har ingen inverkan på utförandet av funktionen. Låt oss ringa foo () två gånger: en gång med int-argument och en gång med strängargument. I båda fallen, foo () är den rätta saken och annotationerna helt enkelt ignorerade.
"python foo (" Hej ",", "," Värld! ") Hej, Världen!
foo (1, 2, 3) 6 "
Standardargument anges efter anmärkningen:
"python def foo (x:" ett argument som standard till 5 '= 5): print (x)
foo (7) 7
foo () 5 "
Funktionsobjektet har ett attribut som heter "annoteringar'. Det är en kartläggning som kartlägger varje argumentnamn till dess anteckning. Returvärdesannotationen är mappad till nyckeln "return", som inte kan störa något argumentnamn eftersom "return" är ett reserverat ord som inte kan fungera som argumentnamn. Observera att det är möjligt att skicka ett nyckelordsargument som heter Returnera till en funktion:
"python def bar (* args, ** kwargs:" nyckelordets argument dict "): print (kwargs ['return'])
d = 'return': 4 bar (** d) 4 "
Låt oss gå tillbaka till vårt första exempel och kontrollera dess kommentarer:
"python def foo (a, b:" annotera b, c: int) -> float: print (a + b + c)
trycket (foo.annoteringar) 'c':
Det här är ganska enkelt. Om du annoterar en funktion med en argumentmatris och / eller sökordsargument, så kan du självklart inte annotera enskilda argument.
"python def foo (* args:" lista med namngivna argument ", ** kwargs:" dict of named arguments "): print (args, kwargs)
trycket (foo.annoteringar) 'args': 'lista över namngivna argument', 'kwargs': 'dict of named arguments' "
Om du läser avsnittet om att få åtkomst till funktionskommentarer i PEP-3107 står det att du får åtkomst till dem via func_annotations-attributet i funktionsobjektet. Detta är föråldrat från Python 3.2. Var inte förvirrad. Det är helt enkelt "annoteringar"attributet.
Det här är den stora frågan. Anteckningar har ingen standard mening eller semantik. Det finns flera kategorier av generiska användningsområden. Du kan använda dem som bättre dokumentation och flytta argument och returnera värdedokumentation ur docstring. Till exempel, denna funktion:
python def div (a, b): "" "Dela en av b args: a - utdelningen b - divisorn (måste vara annorlunda än 0) returnera: resultatet av att dela en av b" "" returnera a / b
Kan konverteras till:
python def div (a: "utdelningen", b: "divisor (måste vara annorlunda än 0) ') ->' resultatet av att dela en av b ':" "" Dela en av b "" "returnera a / b
Medan samma information tas, finns det flera fördelar med annoteringsversionen:
En annan användning som vi kommer att tala om senare är valfri typing. Python skrivs dynamiskt, vilket innebär att du kan skicka ett objekt som ett argument för en funktion. Men ofta funktioner kräver att argument är av en viss typ. Med anteckningar kan du ange typen precis intill argumentet på ett mycket naturligt sätt.
Kom ihåg att bara specificera typen kommer inte att genomdriva det, och ytterligare arbete (mycket arbete) kommer att behövas. Men även om du bara anger typen kan du göra det mer lättläsligt än att ange typen i docstring och det kan hjälpa användarna att förstå hur man ringer funktionen.
En annan fördel med annoteringar över docstring är att du kan bifoga olika typer av metadata som tupler eller dikter. Återigen kan du göra det med docstring också, men det kommer att vara textbaserat och kräver särskild analysering.
Slutligen kan du bifoga en hel del metadata som kommer att användas av speciella externa verktyg eller vid körning via dekoratörer. Jag kommer att undersöka det här alternativet i nästa avsnitt.
Antag att du vill ange ett argument med både dess typ och en hjälpsträng. Det här är väldigt enkelt med anteckningar. Du kan helt enkelt kommentera argumentet med en dikt som har två nycklar: "typ" och "hjälp".
"dikt (typ = float, help =" divisor (måste vara annorlunda än 0) ")> -> dict (typ = float, help =" dividend " float, help = "resultatet av att dela en med b"): "" "Dela en av b" "" returnera a / b
trycket (div.annoteringar) '' '': 'divisor (måste vara annorlunda än 0)', 'typ': float , 'return': 'help': 'resultatet av att dela en efter b', 'typ': float "
Anteckningar och dekoratörer går hand i hand. För en bra introduktion till Python-dekoratörer, kolla in mina två handledning: Deep Dive Into Python Decorators och skriv dina egna Python Decorators.
För det första kan noteringar helt implementeras som dekoratörer. Du kan bara definiera en @kommentera dekoratorn och få det att ta ett argumentnamn och ett Python-uttryck som argument och lagra dem sedan i målfunktionens annoteringar attribut. Detta kan göras för Python 2 också.
Emellertid är dekoratorns verkliga kraft att de kan agera på anteckningarna. Detta kräver naturligtvis samordning av semantiken i anteckningar.
Låt oss titta på ett exempel. Antag att vi vill verifiera att argumenten ligger i ett visst intervall. Anteckningen kommer att vara en tupel med minimi- och maximivärdet för varje argument. Då behöver vi en dekoratör som kommer att kontrollera annoteringen av varje sökordsargument, verifiera att värdet ligger inom intervallet och höja ett undantag på annat sätt. Låt oss börja med dekoratören:
python def check_range (f): def dekorerad (* args, ** kwargs): för namn, intervall i f .__ annoteringar __. items (): min_value, max_value = range if not (min_value <= kwargs[name] <= max_value): msg = 'argument is out of range [ - ]' raise ValueError(msg.format(name, min_value, max_value)) return f(*args, **kwargs) return decorated
Nu, låt oss definiera vår funktion och dekorera den med @check_range dekoratörer.
python @check_range def foo (a: (0, 8), b: (5, 9), c: (10, 20)): returnera a * b - c
Låt oss ringa foo () med olika argument och se vad som händer. När alla argument ligger inom deras intervall är det inga problem.
python foo (a = 4, b = 6, c = 15) 9
Men om vi ställer c till 100 (utanför området (10, 20)) tas ett undantag upp:
python foo (a = 4, b = 6, c = 100) ValueError: argumentet c ligger utanför intervallet [10-20]
Det finns flera situationer där dekoratörer är bättre än annoteringar för att fästa metadata.
Ett uppenbart fall är om din kod måste vara kompatibel med Python 2.
Ett annat fall är om du har mycket metadata. Som du såg tidigare, medan det är möjligt att bifoga någon mängd metadata med hjälp av dikter som anteckningar, är det ganska besvärligt och gör verkligen ont.
Slutligen, om metadata ska drivas på av en specifik dekoratör, kan det vara bättre att associera metadata som argument för dekoratören själv.
Anteckningar är bara en diktattribut av en funktion.
python typ (foo .__ annotations__) dict
Det innebär att du kan modifiera dem på flugan medan programmet körs. Vad är några användarfall? Antag att du vill ta reda på om ett standardvärde för ett argument någonsin används. När funktionen heter med standardvärdet kan du öka värdet på en anteckning. Eller kanske vill du summera alla returvärden. Den dynamiska aspekten kan göras inom själva funktionen eller av en dekoratör.
"python def add (a, b) -> 0: result = a + b lägg till.annoteringar['return'] + = resultatavkastning
trycket (lägg.annoteringar['return']) 0
lägg till (3, 4) 7 skriv ut (lägg till.annoteringar['return']) 7
lägg till (5, 5) 10 print (lägg till.annoteringar['retur']) 17 "
Funktionsannonser är mångsidiga och spännande. De har potential att inleda en ny era med inåtvända verktyg som hjälper utvecklare att mäta mer och mer komplexa system. De erbjuder också den mer avancerade utvecklaren ett standard och läsbart sätt att associera metadata direkt med argument och returvärde för att skapa anpassade verktyg och interagera med dekoratörer. Men det tar lite arbete att dra nytta av dem och utnyttja deras potential.
Lär dig Python med vår kompletta handledning för pythonhandledning, oavsett om du bara har börjat eller du är en erfaren kodare som vill lära dig nya färdigheter.