Dekoratører er en av de fineste funksjonene i Python, men for den nybegynnere Python-programmereren, kan de virke som magi. Formålet med denne artikkelen er å forstå, i dybden, mekanismen bak Python-dekoratørene.
Her er hva du skal lære:
Hvis du ikke har sett en enda (eller kanskje du ikke visste at du hadde å gjøre med en), ser dekoratører slik ut:
@decorator def funksjon_to_decorate (): passere
Du støter vanligvis på dem over definisjonen av en funksjon, og de er prefixed av @
. Dekoratører er spesielt gode for å holde koden din Tørk (Gjenta ikke selv), og de gjør det samtidig som du forbedrer lesbarheten av koden din.
Fortsatt uklar? Ikke vær, siden dekoratører bare er Python-funksjoner. Det er riktig! Du vet allerede hvordan du lager en. Faktisk er det grunnleggende prinsippet bak dekoratører funksjonssammensetning. La oss ta et eksempel:
def x_plus_2 (x): return x + 2 print (x_plus_2 (2)) # 2 + 2 == 4 def x_squared (x): retur x * x print (x_squared (3)) # 3 ^ 2 == 9 # La oss komponere de to funksjonene for x = 2 print (x_squared (x_plus_2 (2))) # (2 + 2) ^ 2 == 16 utskrift (x_squared (x_plus_2 (3))) # (3 + 2) ^ 2 == 25 skriv ut (x_squared (x_plus_2 (4))) # (4 + 2) ^ 2 == 36
Hva om vi ønsket å skape en annen funksjon, x_plus_2_squared
? Å prøve å komponere funksjonene ville være ubrukelig:
x_squared (x_plus_2) # TypeError: Ikke-støttet operand type (r) for *: 'funksjon' og 'funksjon'
Du kan ikke komponere funksjoner på denne måten fordi begge funksjonene tar tall som argumenter. Dette vil imidlertid fungere:
# La oss nå lage en riktig funksjonssammensetning uten å faktisk bruke funksjonen x_plus_2_squared = lambda x: x_squared (x_plus_2 (x)) print (x_plus_2_squared (2)) # (2 + 2) ^ 2 == 16 utskrift (x_plus_2_squared (3)) # (3 + 2) ^ 2 == 25 print (x_plus_2_squared (4)) # (4 + 2) ^ 2 == 36
La oss omdefinere hvordan x_squared
virker. Hvis vi vil x_squared
For å være kompositt som standard bør den:
Vi vil navngi den kompositte versjonen av x_squared
ganske enkelt squared
.
def kvadrert (func): retur lambda x: func (x) * func (x) utskrift (kvadrert (x_plus_2) (2)) # (2 + 2) ^ 2 == 16 utskrift (kvadrat (x_plus_2) (3)) # (3 + 2) ^ 2 == 25 utskrift (kvadrat (x_plus_2) (4)) # (4 + 2) ^ 2 == 36
Nå som vi har definert squared
Fungerer på en måte som gjør den komposibel, kan vi bruke den med en hvilken som helst annen funksjon. Her er noen eksempler:
def x_plus_3 (x): return x + 3 def x_times_2 (x): return x * 2 print (kvadrert (x_plus_3) (2)) # (2 + 3) ^ 2 == 25 print (kvadratisk (x_times_2) ) # (2 * 2) ^ 2 == 16
Vi kan si det squared
dekorerer funksjonene x_plus_2
, x_plus_3
, og x_times_2
. Vi er svært nær å oppnå standard dekoratør notasjon. Sjekk ut dette:
x_plus_2 = kvadrert (x_plus_2) # Vi dekorerte x_plus_2 med kvadrert utskrift (x_plus_2 (2)) # x_plus_2 returnerer nå det dekorerte kvadratresultatet: (2 + 2) ^ 2
Det er det! x_plus_2
er en riktig Python dekorert funksjon. Her er hvor @
notat kommer på plass:
def x_plus_2 (x): return x + 2 x_plus_2 = squared (x_plus_2) # ^ Dette er helt ekvivalent med: @squared def x_plus_2 (x): return x + 2
Faktisk er det @
notasjon er en form for syntaktisk sukker. La oss prøve det ut:
@squared def x_times_3 (x): return 3 * x print (x_times_3 (2)) # (3 * 2) ^ 2 = 36. # Det kan være litt forvirrende, men ved å dekorere det med kvadrat, ble x_times_3 faktisk 3 * x) * (3 * x) @squared def x_minus_1 (x): return x - 1 print (x_minus_1 (3)) # (3-1) ^ 2 = 4
Hvis squared
er den første dekoratøren du noen gang har skrevet, gi deg en stor klapp på baksiden. Du har tatt et av de mest komplekse konseptene i Python. Underveis lærte du en annen grunnleggende funksjon i funksjonelle programmeringsspråk: funksjonssammensetning.
En dekoratør er en funksjon som tar en funksjon som et argument og gir en annen funksjon. Når det er sagt, er generisk mal for å definere en dekoratør:
def decorator (function_to_decorate): # ... return decorated_function
I tilfelle du ikke visste, kan du definere funksjoner i funksjoner. I de fleste tilfeller er det decorated_function
vil bli definert inne dekoratør
.
def decorator (function_to_decorate): def decorated_function (* args, ** kwargs): # ... Siden vi dekorere 'function_to_decorate', bør vi bruke det et sted inne her returnere decorated_function
La oss se på et mer praktisk eksempel:
importere pytz fra datetime import datetime def to_utc (function_to_decorate): def decorated_function (): # Få resultatet av function_to_decorate og transform resultatet til UTC return function_to_decorate (). astimezone (pytz.utc) returnered_function @to_utc def package_pickup_time (): " "" Dette kan komme fra en database eller fra en API "" "tz = pytz.timezone ('USA / Pacific') returnerer tz.localize (datetime (2017, 8, 2, 12, 30, 0, 0)) @ to_utc def package_delivery_time (): "" "Dette kan komme fra en database eller fra en API" "" tz = pytz.timezone ('US / Eastern') retur tz.localize (datetime (2017, 8, 2, 12, 30 , 0, 0)) # Hvilket tilfeldig tilfeldighet, samme tidssone! print ("PICKUP:", package_pickup_time ()) # '2017-08-02 19: 30: 00 + 00: 00' print ("LEVERING:", package_delivery_time ()) # '2017-08-02 16:30: 00 + 00: 00'
Søt! Nå kan du være sikker på at alt i appen din er standardisert for UTC-tidssonen.
En annen veldig populær og klassisk brukskasse for dekoratører er caching resultatet av en funksjon:
importtid def cached (function_to_decorate): _cache = # Hvor vi holder resultatene def decorated_function (* args): start_time = time.time () print ('_ cache:', _cache) hvis args ikke er i _cache: _cache [args ] = function_to_decorate (* args) # Utfør beregningen og lagre den i hurtigutskrift ('Beregn tid:% ss'% runde (time.time () - start_time, 2)) returner _cache [args] returned_function @cached def complex_computation (x, y): print ('Behandling ...') time.sleep (2) returnere x + y print (complex_computation (1, 2)) # 3, Utføre den dyre operasjonsutskriften (complex_computation (1, 2)) # 3 , SKIP utfører den dyre operasjonsutskriften (complex_computation (4, 5)) # 9, Utfører den dyre operasjonsutskriften (complex_computation (4, 5)) # 9, SKIP utfører den dyre operasjonsutskriften (complex_computation (1, 2)) # 3 , SKIP utfører den dyre operasjonen
Hvis du ser på koden, kan du motsette seg. Dekoratøren er ikke gjenbrukbar! Hvis vi dekorerer en annen funksjon (si another_complex_computation
) og kalle det med de samme parametrene, så får vi de cached resultatene fra complex_computation funksjon
. Dette skjer ikke. Dekoratøren er gjenbrukbar, og her er hvorfor:
@cached def another_complex_computation (x, y): print ('Behandler ...') time.sleep (2) returner x * y print (another_complex_computation (1, 2)) # 2, Utfører den dyre operasjonsutskrift (another_complex_computation (1, 2 )) # 2, SKIP utfører den dyre operasjonsutskriften (another_complex_computation (1, 2)) # 2, SKIP utfører den dyre operasjonen
De bufret
funksjon kalles en gang for hver funksjon den dekorerer, så en annen _cache
variabel er instantiated hver gang og lever i den sammenhengen. La oss teste dette ut:
skriv ut (complex_computation (10, 20)) # -> 30 print (another_complex_computation (10, 20)) -> 200
Dekoratøren vi nettopp kodet, som du kanskje har lagt merke til, er veldig nyttig. Det er så nyttig at en mer kompleks og robust versjon allerede finnes i standarden functools
modul. Den er oppkalt lru_cache
. LRU er forkortelsen til Minst nylig brukt, en caching strategi.
fra functools importere lru_cache @lru_cache () def complex_computation (x, y): print ('Behandling ...') time.sleep (2) returnere x + y print (complex_computation (1, 2)) # Prosessering ... 3 utskrift (complex_computation 1, 2)) # 3 print (complex_computation (2, 3)) # Behandling ... 5 utskrift (complex_computation (1, 2)) # 3 print (complex_computation (2, 3)) # 5
En av mine favorittbrukere av dekoratører er i rammen for Flask-web. Det er så pent at dette kodestykket er det første du ser på Flaskens nettsted. Her er koden:
fra kolbe import Flask app = Flask (__ name__) @ app.route ("/") def hei (): return "Hello World!" hvis __name__ == "__main__": app.run ()
De app.route
dekoratør tilordner funksjonen Hallo
som forespørselen håndterer for ruten "/"
. Enkelheten er fantastisk.
En annen fin bruk av dekoratører er inne i Django. Vanligvis har webprogrammer to typer sider:
Hvis du prøver å vise en side av sistnevnte type, blir du vanligvis omdirigert til en påloggingsside. Slik implementerer du det i Django:
fra django.http importere HttpResponse fra django.contrib.auth.decorators import login_required # Offentlige sider def home (request): return HttpResponse ("Hjem") def landing (forespørsel): return HttpResponse ("Landing") # Autentiserte sider @login_required (login_url = '/ login') def dashboard (forespørsel): return HttpResponse ("dashbord") @login_required (login_url = '/ login') def profile_settings (request): returner HttpResponse ("Profilinnstillinger")
Vær oppmerksom på hvor pent de private visningene er merket med innlogging kreves
. Mens du går gjennom koden, er det veldig klart for leseren hvilke sider krever at brukeren logger på og hvilke sider som ikke gjør det.
Jeg håper du hadde det gøy å lære om dekoratører fordi de representerer en veldig fin Python-funksjon. Her er noen ting å huske:
Husk å sjekke ut hva vi har tilgjengelig for salg og for studier på Envato Market, og ikke nøl med å stille spørsmål og gi din verdifulle tilbakemelding ved å bruke feedet under.
!