Denne artikkelen ble publisert som en del av Data Science Blogathon
Introduksjon
Python Standard Library har mange flotte moduler som hjelper deg med å holde koden din renere og enklere, og funksjonsverktøy er definitivt en av de
caching
La oss starte med noen av de enkleste, men kraftige funksjonene til modulfunksjonsverktøyene. La oss starte med bufringsfunksjonene (så vel som dekoratører) – lru_cache
,cache
og cached_property
. Den første av dem - lru_cache
gir en hurtigbuffer av de siste resultatene av utførelsen av funksjoner, eller med andre ord, husker resultatet av arbeidet deres:
from functools import lru_cache
import requests @lru_cache(maxsize=32)
def get_with_cache(url): try: r = requests.get(url) return r.text except: return "Not Found" for url in ["https://google.com/", "https://reddit.com/", "https://google.com/", "https://google.com/"]: get_with_cache(url) print(get_with_cache.cache_info())
# CacheInfo(hits=2, misses=4, maxsize=32, currsize=4)
print(get_with_cache.cache_parameters())
# {'maxsize': 32, 'typed': False}
I dette eksemplet gjør vi GET-forespørsler og cacher resultatene deres (opptil 32 resultater) ved hjelp av en dekorator @lru_cache
. For å se om caching faktisk fungerer, kan du sjekke funksjonens cacheinformasjon ved hjelp av en metode cache_info
som viser antall cache-treff og -treff. En dekoratør gir også metoder clear_cache
og cache_parameters
for kansellering av henholdsvis de hurtigbufrede resultatene og testparametrene.
Hvis du trenger mer detaljert hurtigbufring, kan du inkludere et valgfritt argument typed=true, som lar deg bufre forskjellige typer argumenter separat.
En annen dekorator for caching i functools er en funksjon som kalles ganske enkelt cache
. Det er en enkel innpakning lru_cache
som utelater argumentet max_size, reduserer det, og fjerner ikke de gamle verdiene.
En annen dekorator du kan bruke for caching er cached_property
. Som navnet antyder, brukes den til å cache resultatene av klasseattributter. Denne mekanikeren er veldig nyttig hvis du har en egenskap som er dyr å beregne, men som forblir den samme.
from functools import cached_property class Page: @cached_property def render(self, value): # Do something with supplied value... # Long computation that renders HTML page... return html
Dette enkle eksemplet viser. Som kan brukes, bufret eiendom, for eksempel, for å bufre en gjengitt HTML-side som må vises til brukeren om og om igjen. Det samme kan gjøres for visse databasespørringer eller lange matematiske beregninger.
En annen skjønnhet cached_property
er at den bare kjører ved oppslag, så den lar oss endre verdien på attributtet. Etter endring av attributtet vil ikke den tidligere bufrede verdien endres, i stedet vil den nye verdien beregnes og bufres. Du kan også tømme hurtigbufferen, og alt du trenger å gjøre er å fjerne attributtet.
Jeg vil avslutte denne delen med et forbehold om alle de ovennevnte dekoratørene – ikke bruk dem hvis funksjonen din har noen bivirkninger eller hvis den lager foranderlige objekter hver gang den kalles, siden dette tydeligvis ikke er funksjonene du vil hurtigbufre. .
Sammenligning og bestilling
Du vet sikkert allerede at du kan implementere sammenligningsoperatorer i Python som f.eks <
, >=
or ==
med lt
, gt
or eq
. Imidlertid kan det være ganske frustrerende å innse hver av disse eq
, lt
, le
, gt
or ge
. Heldigvis funksjonsverktøy det er en dekoratør @total_ordering
som kan hjelpe oss med dette, fordi alt vi trenger å implementere er eq
en av de resterende metodene, og resten av dekoratøren vil bli generert automatisk.
from functools import total_ordering @total_ordering
class Number: def __init__(self, value): self.value = value def __lt__(self, other): return self.value Number(3))
# True
print(Number(1) = Number(15))
# True
print(Number(10) <= Number(2))
# False
På denne måten kan vi implementere alle de utvidede sammenligningsoperasjonene, til tross for at vi bare har eq og for hand lt. Den mest åpenbare fordelen er bekvemmeligheten, som er at du ikke trenger å skrive alle disse ekstra magiske metodene, men det er sannsynligvis viktigere å redusere mengden kode og dens bedre lesbarhet.
Overlast
Vi har nok alle blitt lært at det ikke er noen overbelastning i Python, men det er faktisk en enkel måte å implementere det på ved å bruke to funksjoner fra functools, nemlig single dispatch og/eller single dispatch metoden. Disse funksjonene hjelper oss med å implementere det vi vil kalle en multiple dispatch-algoritme som lar dynamisk skrivede programmeringsspråk som Python skille mellom typer under kjøring.
Delvis
Vi jobber alle med ulike eksterne biblioteker eller rammeverk, hvorav mange gir funksjoner og grensesnitt som krever at vi sender tilbakeringinger, for eksempel for asynkrone operasjoner eller lytting etter hendelser. Dette er ikke noe nytt, men hva om vi også trenger å sende noen argumenter sammen med tilbakeringingen. Det er her det kommer i nyttige funksjonsverktøy.partial
. Kan bli brukt partial
å fryse noen (eller alle) argumentene til en funksjon ved å lage et nytt objekt med en forenklet funksjonssignatur. Forvirret? La oss ta en titt på noen praktiske eksempler:
def output_result(result, log=None): if log is not None: log.debug(f"Result is: {result}") def concat(a, b): return a + b import logging
from multiprocessing import Pool
from functools import partial logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger("default") p = Pool()
p.apply_async(concat, ("Hello ", "World"), callback=partial(output_result, log=logger))
p.close()
p.join()
Koden ovenfor viser hvordan du kan bruke den partial
å sende en funksjon ( output_result
) sammen med et argument ( log=logger
) som en tilbakeringing. I dette tilfellet vil vi bruke multiprocessing.apply_async, som asynkront beregner resultatet av funksjonen ( concat
) og returnerer resultatet av tilbakeringingen. Derimot, apply_async
det vil alltid sende resultatet som det første argumentet, og hvis vi ønsker å inkludere ytterligere argumenter, slik tilfellet er med log=logger, må vi bruke partial.
Vi har vurdert et ganske avansert brukstilfelle, og et enklere eksempel vil være den vanlige opprettelsen av en funksjon som skriver i stderr i stedet for stdout:
import sys
from functools import partial print_stderr = partial(print, file=sys.stderr)
print_stderr("This goes to standard error output")
Med dette enkle trikset har vi laget en ny anropbar funksjon som alltid vil passere file=sys.stderr
som et navngitt argument til utgang, som lar oss forenkle koden vår og ikke trenger å spesifisere verdien av det navngitte argumentet hver gang.
Og et siste godt eksempel. Vi kan bruke partial
i forbindelse med en lite kjent funksjon iter
å lage en iterator ved å sende inn et anropbart objekt og sentinel
in iter
, som kan brukes slik:
from functools import partial RECORD_SIZE = 64 # Read binary file...
with open("file.data", "rb") as file: records = iter(partial(file.read, RECORD_SIZE), b'') for r in records: # Do something with the record...
Vanligvis, når vi leser en fil, ønsker vi å iterere over linjer, men når det gjelder binære data, kan det hende vi må iterere over poster med en fast størrelse. Du kan gjøre dette ved å lage et anropbart objekt ved å bruke partial
som leser den angitte databiten og sender den inn iter
for å lage en iterator. Denne iteratoren kaller deretter lesefunksjonen til den når slutten av filen, og tar alltid bare den angitte stykkestørrelsen ( RECORD_SIZE
). Til slutt, når slutten av filen er nådd, verdien sentinel
(b ”) returneres og iterasjonen stopper.
dekoratører
Vi har allerede snakket om noen dekoratører i de forrige avsnittene, men ikke dekoratører for å lage flere dekoratører. En slik dekoratør er functools.wraps
. For å forstå hvorfor du trenger det, la oss bare se på et eksempel:
def decorator(func): def actual_func(*args, **kwargs): """Inner function within decorator, which does the actual work""" print(f"Before Calling {func.__name__}") func(*args, **kwargs) print(f"After Calling {func.__name__}") return actual_func @decorator
def greet(name): """Says hello to somebody""" print(f"Hello, {name}!") greet("Martin")
# Before Calling greet
# Hello, Martin!
# After Calling greet
Dette eksemplet viser hvordan en enkel dekoratør kan implementeres. Vi pakker inn en funksjon som utfører en spesifikk oppgave ( actual_func
) med en ekstern dekoratør, og den blir selv en dekoratør, som deretter kan brukes på andre funksjoner, for eksempel, slik tilfellet er med greet
. Når du ringer funksjonen, greet
du vil se at den skriver ut meldinger både fra actual_func
og på egen hånd. Ser greit ut, ikke sant? Men hva skjer hvis vi gjør dette:
print(greet.__name__)
# actual_func
print(greet.__doc__)
# Inner function within decorator, which does the actual work
Når vi kaller navn og dokumentasjon på den dekorerte funksjonen, innser vi at de er erstattet med verdier fra dekoratørfunksjonen. Dette er dårlig siden vi ikke kan omskrive alle funksjonsnavn og dokumentasjon når vi bruker en eller annen dekoratør. Hvordan kan dette problemet løses? Selvfølgelig med funksjonsverktøy.wraps
:
from functools import wraps def decorator(func): @wraps(func) def actual_func(*args, **kwargs): """Inner function within decorator, which does the actual work""" print(f"Before Calling {func.__name__}") func(*args, **kwargs) print(f"After Calling {func.__name__}") return actual_func @decorator
def greet(name): """Says hello to somebody""" print(f"Hello, {name}!") print(greet.__name__)
# greet
print(greet.__doc__)
# Says hello to somebody
Funksjonens eneste formål wraps
er å kopiere navn, dokumentasjon, argumentliste osv. for å forhindre overskriving. Vurderer wraps
det er også en dekoratør, du kan ganske enkelt legge det til vår actual_func, og problemet er løst!
Reduser
Sist men ikke minst i modulens funksjonsverktøy er denne reduksjonen. Kanskje fra andre språk, kanskje du kjenner det som fold
(Haskell). Denne funksjonen tar en iterabel og bretter (legger til) alle verdiene til én. Det er mange bruksområder for dette, for eksempel:
from functools import reduce
import operator def product(iterable): return reduce(operator.mul, iterable, 1) def factorial(n): return reduce(operator.mul, range(1, n)) def sum(numbers): # Use `sum` function from standard library instead return reduce(operator.add, numbers, 1) def reverse(iterable): return reduce(lambda x, y: y+x, iterable) print(product([1, 2, 3]))
# 6
print(factorial(5))
# 24
print(sum([2, 6, 8, 3]))
# 20
print(reverse("hello"))
# olleh
Som du kan se av koden, reduce
kan forenkle eller kondensere koden til én linje, som ellers ville vært mye lengre. Når det er sagt, er det vanligvis en dårlig idé å misbruke denne funksjonen bare for å krympe kode, noe som gjør den "smartere", siden den raskt blir skummel og uleselig. Av denne grunn bør den etter min mening brukes sparsomt.
Og hvis du husker det reduce
ofte forkorter alt til en linje, det kan perfekt kombineres med partial
:
product = partial(reduce, operator.mul) print(product([1, 2, 3]))
# 6
Og til slutt, hvis du trenger mer enn bare det endelige "kollapserte" resultatet, kan du bruke accumulate
– fra en annen flott modul itertools
. For å beregne maksimum, kan det brukes som følger:
from itertools import accumulate data = [3, 4, 1, 3, 5, 6, 9, 0, 1] print(list(accumulate(data, max)))
# [3, 4, 4, 4, 5, 6, 9, 9, 9]
konklusjonen
Som du kan se functools, er det mange nyttige funksjoner og dekoratorer som kan gjøre livet ditt enklere, men dette er bare toppen av isfjellet. Som jeg sa innledningsvis er det mange funksjoner i Python standardbiblioteket som hjelper deg med å skrive bedre kode, så i tillegg til funksjonene som vi dekket her, kan du ta hensyn til andre moduler, som f.eks. operator
or itertool.
For eventuelle spørsmål kan du trykke på kommentarfeltet mitt. Jeg vil prøve mitt beste for å løse innspillene dine og håper å gi deg ønskede resultater. Du kan også nå meg på LinkedIn:-
https://www.linkedin.com/in/shivani-sharma-aba6141b6/
Media vist i denne artikkelen eies ikke av Analytics Vidhya og brukes etter forfatterens skjønn.
I slekt
- '
- "
- 9
- Ytterligere
- algoritme
- Alle
- analytics
- søknader
- argumenter
- Artikkel
- Beauty
- BEST
- Eske
- ring
- endring
- kode
- Beregn
- Opprette
- dato
- Database
- Dispatch
- etc
- hendelser
- gjennomføring
- Endelig
- Først
- Frys
- funksjon
- god
- flott
- praktisk
- her.
- Hvordan
- HTTPS
- Tanken
- informasjon
- IT
- holde
- språk
- Bibliotek
- linje
- Liste
- Lytting
- Lang
- oppslag
- Making
- math
- Media
- nemlig
- navn
- tall
- Okay
- Drift
- Mening
- Annen
- Betale
- basseng
- makt
- Programmering
- programmerings språk
- eiendom
- Python
- Lesning
- poster
- redusere
- REST
- Resultater
- avkastning
- Vitenskap
- Enkelt
- Størrelse
- So
- LØSE
- Begynn
- test
- tid
- us
- verdi
- innenfor
- ord
- Arbeid
- virker
- verden
- X