Denne artikel blev offentliggjort som en del af Data Science Blogathon
Introduktion
Python Standard Library har mange gode moduler til at hjælpe med at holde din kode renere og enklere, og funktionsværktøjer er helt sikkert en af de
Caching
Lad os starte med nogle af de enkleste, men kraftfulde funktioner i modulfunktionsværktøjerne. Lad os starte med caching-funktionerne (såvel som dekoratører) - lru_cache
,cache
, cached_property
. Den første af dem - lru_cache
giver en cache af de sidste resultater af udførelsen af funktioner, eller med andre ord husker resultatet af deres arbejde:
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 eksempel laver vi GET-anmodninger og cacher deres resultater (op til 32 resultater) ved hjælp af en dekorator @lru_cache
. For at se, om caching rent faktisk virker, kan du kontrollere funktionen cache-oplysninger ved hjælp af en metode cache_info
der viser antallet af cache-hits og -hits. En dekoratør giver også metoder clear_cache
, cache_parameters
til annullering af henholdsvis cachelagrede resultater og testparametre.
Hvis du har brug for mere detaljeret caching, kan du inkludere et valgfrit argument typed=true, som giver dig mulighed for at cache forskellige typer argumenter separat.
En anden dekorator til caching i functools er en funktion, der kaldes simpelt cache
. Det er en simpel indpakning lru_cache
der udelader argumentet max_size, reducerer det og fjerner ikke de gamle værdier.
En anden dekorator du kan bruge til caching er cached_property
. Som navnet antyder, bruges det til at cache resultaterne af klasseattributter. Denne mekaniker er meget nyttig, hvis du har en ejendom, der er dyr at beregne, men som forbliver 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 eksempel viser. Som kan bruges, cachelagret egenskab, for eksempel, til at cache en gengivet HTML-side, der skal vises til brugeren igen og igen. Det samme kan gøres for visse databaseforespørgsler eller lange matematiske beregninger.
Endnu en skønhed cached_property
er, at det kun kører ved opslag, så det giver os mulighed for at ændre værdien af attributten. Efter ændring af attributten vil den tidligere cachelagrede værdi ikke ændre sig, i stedet vil den nye værdi blive beregnet og cachelagret. Du kan også rydde cachen, og alt hvad du skal gøre er at fjerne attributten.
Jeg vil afslutte dette afsnit med en advarsel om alle ovennævnte dekoratører – brug dem ikke, hvis din funktion har nogle bivirkninger, eller hvis den skaber foranderlige objekter, hver gang den kaldes, da det tydeligvis ikke er de funktioner, du vil cache .
Sammenligning og bestilling
Du ved sikkert allerede, at du kan implementere sammenligningsoperatorer i Python som f.eks <
, >=
or ==
, med lt
, gt
or eq
. Det kan dog være ret frustrerende at indse hver af de eq
, lt
, le
, gt
or ge
. Heldigvis, functools der er en dekoratør @total_ordering
det kan hjælpe os med dette, for det eneste vi skal implementere er eq
en af de resterende metoder, og resten af dekoratøren vil blive genereret 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åde kan vi implementere alle de udvidede sammenligningsoperationer, på trods af at vi kun har eq og manuelt lt. Den mest åbenlyse fordel er bekvemmeligheden, som er, at du ikke behøver at skrive alle disse ekstra magiske metoder, men det er nok vigtigere at reducere mængden af kode og dens bedre læsbarhed.
Overbelastning
Vi er nok alle blevet undervist i, at der ikke er nogen overbelastning i Python, men der er faktisk en nem måde at implementere det på ved hjælp af to funktioner fra functools, nemlig single dispatch og/eller single dispatch metode. Disse funktioner hjælper os med at implementere, hvad vi ville kalde en multiple dispatch-algoritme, der tillader dynamisk indtastede programmeringssprog såsom Python at skelne mellem typer under kørsel.
Delvis
Vi arbejder alle med forskellige eksterne biblioteker eller rammer, hvoraf mange leverer funktioner og grænseflader, der kræver, at vi sender tilbagekald, såsom til asynkrone operationer eller lytning efter begivenheder. Dette er ikke noget nyt, men hvad nu hvis vi også skal sende nogle argumenter sammen med tilbagekaldet. Det er her, det kommer i praktiske funktionsværktøjer.partial
. Kan bruges partial
at fryse nogle (eller alle) argumenterne til en funktion ved at oprette et nyt objekt med en forenklet funktionssignatur. Forvirret? Lad os tage et kig på nogle 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 bruge den partial
at bestå en funktion ( output_result
) sammen med et argument ( log=logger
) som et tilbagekald. I dette tilfælde vil vi bruge multiprocessing.apply_async, som asynkront beregner resultatet af funktionen ( concat
) og returnerer resultatet af tilbagekaldet. Imidlertid, apply_async
det vil altid sende resultatet som det første argument, og hvis vi vil inkludere yderligere argumenter, som det er tilfældet med log=logger, skal vi bruge partial.
Vi har overvejet en ret avanceret use case, og et enklere eksempel ville være den sædvanlige oprettelse af en funktion, der 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 trick har vi skabt en ny funktion, der kan kaldes, som altid vil bestå file=sys.stderr
som et navngivet argument til output, hvilket giver os mulighed for at forenkle vores kode og ikke behøver at angive værdien af det navngivne argument hver gang.
Og et sidste godt eksempel. Vi kan bruge partial
i forbindelse med en lidet kendt funktion iter
at oprette en iterator ved at sende et kaldbart objekt ind og sentinel
in iter
, som kan anvendes på denne måde:
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...
Normalt, når vi læser en fil, ønsker vi at iterere over linjer, men i tilfælde af binære data, skal vi muligvis iterere over poster med en fast størrelse. Du kan gøre dette ved at oprette et kaldbart objekt vha partial
der læser den angivne del af data og sender den ind iter
for at oprette en iterator. Denne iterator kalder derefter læsefunktionen, indtil den når slutningen af filen, og tager altid kun den angivne chunkstørrelse ( RECORD_SIZE
). Til sidst, når slutningen af filen er nået, værdien sentinel
(b ”) returneres, og iterationen stopper.
dekoratører
Vi har allerede talt om nogle dekoratører i de foregående afsnit, men ikke dekoratører for at skabe flere dekoratører. En sådan dekoratør er functools.wraps
. For at forstå hvorfor du har brug for det, lad os 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 eksempel viser, hvordan en simpel dekoratør kan implementeres. Vi ombryder en funktion, der udfører en bestemt opgave ( actual_func
) med en ekstern dekoratør, og den bliver selv en dekoratør, som så kan anvendes til andre funktioner, f.eks. som det er tilfældet med greet
. Når du kalder funktionen, greet
du vil se, at den udskriver beskeder både fra actual_func
og på egen hånd. Ser okay ud, gør det ikke? Men hvad sker der, hvis vi gør dette:
print(greet.__name__)
# actual_func
print(greet.__doc__)
# Inner function within decorator, which does the actual work
Når vi kalder den dekorerede funktions navn og dokumentation, indser vi, at de er blevet erstattet med værdier fra dekoratørfunktionen. Dette er dårligt, da vi ikke kan omskrive alle vores funktionsnavne og dokumentation, når vi bruger en eller anden dekoratør. Hvordan kan dette problem løses? Selvfølgelig med functools.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
Funktionens eneste formål wraps
er at kopiere navn, dokumentation, argumentliste osv. for at forhindre overskrivning. Overvejer det wraps
det er også en dekoratør, du kan blot tilføje det til vores actual_func, og problemet er løst!
Reducer
Sidst men ikke mindst i modulets funktionsværktøjer er denne reduktion. Måske fra andre sprog, kender du det måske som fold
(Haskell). Denne funktion tager en iterabel og folder (tilføjer) alle dens værdier til én. Der er mange applikationer til 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 af koden, reduce
kan forenkle eller kondensere koden til én linje, som ellers ville være meget længere. Når det er sagt, er det normalt en dårlig idé at misbruge denne funktion bare for at formindske koden, så den bliver "smartere", da den hurtigt bliver skræmmende og ulæselig. Af denne grund bør det efter min mening bruges sparsomt.
Og hvis du husker det reduce
ofte forkorter alt til én linje, det kan perfekt kombineres med partial
:
product = partial(reduce, operator.mul) print(product([1, 2, 3]))
# 6
Og endelig, hvis du har brug for mere end blot det endelige "kollapserede" resultat, så kan du bruge accumulate
– fra et andet godt modul itertools
. For at beregne maksimum, kan det bruges 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]
Konklusion
Som du kan se functools, er der mange nyttige funktioner og dekoratorer, der kan gøre dit liv lettere, men dette er kun toppen af isbjerget. Som jeg sagde i starten, er der mange funktioner i Python standardbiblioteket, der hjælper dig med at skrive bedre kode, så udover de funktioner, som vi dækkede her, kan du være opmærksom på andre moduler, som f.eks. operator
or itertool.
For eventuelle spørgsmål kan du trykke på mit kommentarfelt. Jeg vil gøre mit bedste for at løse dine input og håber at give dig ønskede output. Du kan også kontakte mig på LinkedIn:-
https://www.linkedin.com/in/shivani-sharma-aba6141b6/
Medierne vist i denne artikel ejes ikke af Analytics Vidhya og bruges efter forfatterens skøn.
Relaterede
- '
- "
- 9
- Yderligere
- algoritme
- Alle
- analytics
- applikationer
- argumenter
- artikel
- Skønhed
- BEDSTE
- Boks
- ringe
- lave om
- kode
- Compute
- Oprettelse af
- data
- Database
- Dispatch
- etc.
- begivenheder
- udførelse
- Endelig
- Fornavn
- Frys
- funktion
- godt
- stor
- praktisk
- link.
- Hvordan
- HTTPS
- idé
- oplysninger
- IT
- holde
- Sprog
- Bibliotek
- Line (linje)
- Liste
- Lytte
- Lang
- kig op
- Making
- matematik
- Medier
- nemlig
- navne
- numre
- Okay
- Produktion
- Udtalelse
- Andet
- Betal
- pool
- magt
- Programmering
- programmeringssprog
- ejendom
- Python
- Læsning
- optegnelser
- reducere
- REST
- Resultater
- afkast
- Videnskab
- Simpelt
- Størrelse
- So
- SOLVE
- starte
- prøve
- tid
- us
- værdi
- inden for
- ord
- Arbejde
- virker
- world
- X