Python 3 Funksjon Annotasjoner

Funksjonsannonser er en Python 3-funksjon som lar deg legge til vilkårlig metadata for å fungere argumenter og returverdi. De var en del av den opprinnelige Python 3.0-spesifikasjonen.

I denne opplæringen vil jeg vise deg hvordan du kan dra nytte av generell funksjonalitet og kombinere dem med dekoratører. Du vil også lære om fordeler og ulemper ved funksjonsannonser, når det er hensiktsmessig å bruke dem, og når det er best å bruke andre mekanismer som docstrings og enkle dekoratører.

Funksjon Annotasjoner

Funksjonsannonser er angitt i PEP-3107. Hovedmotivasjonen var å gi en standard måte å knytte metadata til funksjonalargumenter og returverdi. Mange fellesskapsmedlemmer fant nye brukstilfeller, men brukte forskjellige metoder som tilpassede dekoratorer, tilpassede docstringformater og legge til egendefinerte attributter til funksjonsobjektet.

Det er viktig å forstå at Python ikke velsigne merknader med noen semantikk. Den gir rent og pent en syntaktisk støtte for å forbinde metadata, samt en enkel måte å få tilgang til den. Også, merknader er helt valgfrie.

La oss ta en titt på et eksempel. Her er en funksjon foo () som tar tre argumenter kalt a, b og c og skriver ut summen deres. Merk at foo () returnerer ingenting. Det første argumentet en er ikke merket. Det andre argumentet b er merket med strengen 'annotere b' og det tredje argumentet c er merket med type int. Returneringsverdien er merket med typen flyte. Merk "->" syntaksen for å annotere returverdien.

python def foo (a, b: 'annotere b', c: int) -> float: print (a + b + c)

Merknadene har ingen innvirkning på utførelsen av funksjonen. La oss ringe foo () to ganger: en gang med int argumenter og en gang med streng argumenter. I begge tilfeller, foo () gjør det riktige, og annotasjonene bare ignoreres.

"python foo ('Hello', ',', 'World!') Hei, verden!

foo (1, 2, 3) 6 "

Standard argumenter

Standardargumenter angis etter annoteringen:

"python def foo (x: 'et argument som standard til 5' = 5): print (x)

foo (7) 7

foo () 5 "

Å få tilgang til funksjonskommentarer

Funksjonsobjektet har et attributt som heter 'merknader'. Det er en kartlegging som kartlegger hvert argumentnavn til annotasjonen. Avkastningsannonsen er kartlagt til nøkkelen 'retur', som ikke kan komme i konflikt med et argumentnavn fordi «retur» er et reservert ord som ikke kan tjene som argumentnavn. Vær oppmerksom på at det er mulig å sende et søkeord-argument som heter Return til en funksjon:

"python def bar (* args, ** kwargs: 'søkeordets argumenter dikt'): print (kwargs ['return'])

d = 'return': 4 bar (** d) 4 "

La oss gå tilbake til vårt første eksempel og sjekke kommentarene sine:

"python def foo (a, b: 'annotere b', c: int) -> float: print (a + b + c)

print (foo.merknader) 'c': , 'b': 'annotere b', 'retur': "

Dette er ganske grei. Hvis du annoterer en funksjon med et argument array og / eller søkeord arguments array, så kan du selvsagt ikke annotere individuelle argumenter.

"python def foo (* args: 'liste over ikke-navngitte argumenter', ** kwargs: 'dict of named arguments'): print (args, kwargs)

print (foo.merknader) 'args': 'liste over ikke-navngitte argumenter', 'kwargs': 'dict of named arguments' "

Hvis du leser delen om tilgang til funksjonsannonser i PEP-3107, står det at du får tilgang til dem gjennom funksjonen "func_annotations" i funksjonsobjektet. Dette er utdatert fra Python 3.2. Ikke vær forvirret. Det er bare "merknader' Egenskap.

Hva kan du gjøre med notater?

Dette er det store spørsmålet. Merknader har ingen standard mening eller semantikk. Det finnes flere kategorier av generiske bruksområder. Du kan bruke dem som bedre dokumentasjon og flytte argument og returnere verdivaldokumentasjon ut av docstring. For eksempel, denne funksjonen:

python def div (a, b): "" "Del en av b args: a - utbyttet b - divisoren (må være forskjellig fra 0) retur: resultatet av å dele en av b" "tilbake a / b

Kan konverteres til:

python def div (a: "dividendet", b: "divisoren (må være forskjellig fra 0) ') ->' resultatet av å dele en ved b ':" "" Del en av b " b

Mens den samme informasjonen er tatt, er det flere fordeler med annotasjonsversjonen:

  1. Hvis du omdøper et argument, kan dokumentasjons docstring-versjonen være utdatert.
  2. Det er lettere å se om et argument ikke er dokumentert.
  3. Det er ikke nødvendig å komme opp med et spesielt format for argumentdokumentasjon inne i dokumentasjonen som skal analyseres av verktøy. De merknader Attributt gir en direkte, standardmekanisme for tilgang.

En annen bruk som vi skal snakke om senere, er valgfri skriving. Python er dynamisk skrevet, noe som betyr at du kan passere ethvert objekt som et argument av en funksjon. Men ofte funksjoner vil kreve argumenter å være av en bestemt type. Med merknader kan du spesifisere typen rett ved siden av argumentet på en veldig naturlig måte.

Husk at bare å spesifisere typen ikke vil håndheve den, og det vil være behov for ekstra arbeid (mye arbeid). Likevel, selv om du bare angir typen, kan hensikten bli mer lesbar enn å spesifisere typen i docstring, og det kan hjelpe brukerne til å forstå hvordan man ringer funksjonen.

En annen fordel med annoteringer over docstring er at du kan legge til ulike typer metadata som tuples eller dicts. Igjen kan du gjøre det med doktrering også, men det vil være tekstbasert og vil kreve spesiell parsing.

Til slutt kan du legge ved mange metadata som vil bli brukt av spesielle eksterne verktøy eller på kjøretid via dekoratører. Jeg vil utforske dette alternativet i neste avsnitt.

Flere merknader

Anta at du vil annotere et argument med både sin type og en hjelpestreng. Dette er veldig enkelt med merknader. Du kan bare annotere argumentet med en dikt som har to nøkler: 'type' og 'hjelp'.

"dyp (type = float, help =" divisoren (må være forskjellig fra 0) ")> -> dict (type = float, help =" dividend " float, help = "resultatet av å dele en etter b"): "" "Del en av b" "" returnere a / b

print (div.merknader) '' '' '' '' divisjonen (må være forskjellig fra 0) ',' type ': flyte , 'return': 'help': 'resultatet av å dele en etter b', 'type': float "

Kombinerer Python-notater og dekoratører

Merknader og dekoratører går hånd i hånd. For en god introduksjon til Python-dekoratører, sjekk ut mine to veiledninger: Deep Dive Into Python Decorators og skriv dine egne Python Decorators.

For det første kan merknader bli fullt implementert som dekoratører. Du kan bare definere en @annotate dekoratør og få det til å ta et argumentnavn og et Python-uttrykk som argumenter og lagre dem i målfunksjonen merknader Egenskap. Dette kan også gjøres for Python 2.

Imidlertid er den virkelige kraften til dekoratører at de kan handle på kommentarene. Dette krever selvfølgelig samordning av semantikken til merknader.

La oss se på et eksempel. Anta at vi vil verifisere at argumentene er i et bestemt område. Merknaden vil være en tuple med minimum og maksimum verdi for hvert argument. Deretter trenger vi en dekoratør som vil sjekke annoteringen av hvert søkeordargument, verifisere at verdien ligger innenfor rekkevidden, og oppnå et unntak ellers. La oss starte med dekoratøren:

python def check_range (f): def dekorert (* args, ** kwargs): for navn, rekkevidde i f .__ annoteringer __. elementer (): min_value, max_value = rekkevidde hvis ikke <= 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

La oss nå definere vår funksjon og dekorere den med @check_range dekoratører.

python @check_range def foo (a: (0, 8), b: (5, 9), c: (10, 20)): return a * b - c

La oss ringe foo () med forskjellige argumenter og se hva som skjer. Når alle argumenter er innenfor sitt område, er det ikke noe problem.

python foo (a = 4, b = 6, c = 15) 9

Men hvis vi setter c til 100 (utenfor rekkevidden (10, 20)), blir et unntak hevet:

python foo (a = 4, b = 6, c = 100) ValueError: argument c er utenfor rekkevidde [10-20]

Når skal du bruke dekoratører i stedet for notater?

Det er flere situasjoner hvor dekoratører er bedre enn merknader for vedlegg av metadata.

En åpenbar sak er at koden din må være kompatibel med Python 2.

Et annet tilfelle er hvis du har mye metadata. Som du så tidligere, mens det er mulig å feste noen mengde metadata ved å bruke dikt som annoteringer, er det ganske tungvint og faktisk vondt lesbarhet.

Endelig, hvis metadataene skal drives av en bestemt dekoratør, kan det være bedre å knytte metadataene som argumenter for selve dekoratøren.

Dynamiske annotasjoner

Merknader er bare en diktattributt til en funksjon.

python type (foo .__ annotations__) dict

Dette betyr at du kan endre dem på fluen mens programmet kjører. Hva er noen brukstilfeller? Anta at du vil finne ut om en standardverdi av et argument noen gang er brukt. Når funksjonen kalles med standardverdien, kan du øke verdien av en merknad. Eller kanskje du vil oppsummere alle returverdiene. Det dynamiske aspektet kan gjøres inne i selve funksjonen eller av en dekoratør.

"python def add (a, b) -> 0: resultat = a + b legg til.merknader['return'] + = resultat returresultat

print (legg.merknader['retur']) 0

legg til (3, 4) 7 skriv ut (legg til.merknader['retur']) 7

legg til (5, 5) 10 print (legg til.merknader['retur']) 17 "

Konklusjon

Funksjonsannonser er allsidige og spennende. De har potensial til å innlede en ny epoke med introspektive verktøy som hjelper utviklere å mestre flere og mer komplekse systemer. De tilbyr også den mer avanserte utvikleren en standard og lesbar måte å knytte metadata direkte til med argumenter og returverdi for å lage tilpassede verktøy og samhandle med dekoratører. Men det tar litt arbeid å dra nytte av dem og utnytte deres potensial.

Lær python

Lær Python med vår komplette pythonveiledning, enten du er bare i gang eller du er en erfaren coder som ønsker å lære nye ferdigheter..