Hur du optimerar dina Python-skript för bättre prestanda

Hur Du Optimerar Dina Python Skript For Battre Prestanda



Att optimera Python-skripten för bättre prestanda innebär att identifiera och åtgärda flaskhalsarna i vår kod, så att den körs snabbare och mer effektivt. Python är ett populärt och kraftfullt programmeringsspråk som används i många applikationer nuförtiden, inklusive dataanalys, ML-projekt (maskininlärning), webbutveckling och många fler. Python-kodoptimering är en strategi för att förbättra utvecklarprogrammets hastighet och effektivitet när du utför någon aktivitet med färre rader kod, mindre minne eller ytterligare resurser. Stor och ineffektiv kod kan sakta ner programmet, vilket kan resultera i dålig kundnöjdhet och potentiell ekonomisk förlust, eller behovet av mer arbete för att fixa och felsöka.

Det är nödvändigt när du gör en uppgift som kräver behandling av flera åtgärder eller data. Att byta ut och förbättra vissa ineffektiva kodblock och funktioner kan därför ge fantastiska resultat som följande:

  1. Öka applikationens prestanda
  2. Skapa läsbar och organiserad kod
  3. Gör felövervakningen och felsökningen enklare
  4. Spara avsevärd beräkningskraft och så vidare

Profilera din kod

Innan vi börjar optimera är det viktigt att identifiera de delar av projektkoden som saktar ner den. Teknikerna för profilering i Python inkluderar cProfile och profilpaket. Använd sådana verktyg för att mäta hur snabbt vissa funktioner och kodrader körs. Modulen cProfile producerar en rapport som beskriver hur lång tid det tar att köra varje skriptfunktion. Den här rapporten kan hjälpa oss att hitta alla funktioner som går långsamt så att vi kan förbättra dem.







Kodavsnitt:



importera cProfil som cP
def beräkna Summa ( inputNumber ) :
summa_of_input_numbers = 0
medan inputNumber > 0 :
summa_of_input_numbers + = inputNumber % 10
inputNumber // = 10
skriva ut ( 'Summan av alla siffror i inmatat nummer är: 'summa_av_inmatade_nummer'' )
lämna tillbaka summa_of_input_numbers
def main_func ( ) :
cP. springa ( 'beräknaSumma(9876543789)' )
om __namn__ == '__main__' :
main_func ( )

Programmet gör totalt fem funktionsanrop som ses på den första raden i utgången. Detaljerna för varje funktionsanrop visas på följande rader inklusive antalet gånger funktionen anropades, den totala tidslängden i funktionen, varaktigheten av tiden per anrop och den totala tiden i funktionen (inklusive alla funktioner som den kallas).



Dessutom skriver programmet ut en rapport på promptskärmen som visar att programmet slutför exekveringstiden för alla sina uppgifter inom 0,000 sekunder. Detta visar hur snabbt programmet är.





Välj rätt datastruktur

Prestandaegenskaper är beroende av datastrukturen. I synnerhet är ordböcker snabbare för uppslagningar än listor angående allmän lagring. Välj den datastruktur som är mest lämpad för de operationer som vi kommer att utföra på dina uppgifter om du känner till dem. Följande exempel undersöker effektiviteten av olika datastrukturer för en identisk process för att avgöra om ett element i datastrukturen finns.



Vi utvärderar tiden det tar att kontrollera om ett element finns i varje datastruktur – en lista, en uppsättning och en ordbok – och jämför dem.

OptimizeDataType.py:

importera Timei som tt
importera slumpmässig som rndobj
# Skapa en lista med heltal
random_data_list = [ rndobj. randint ( 1 , 10 000 ) för _ i räckvidd ( 10 000 ) ]
# Skapa en uppsättning från samma data
random_data_set = uppsättning ( random_data_list )

# Skapa en ordbok med samma data som nycklar
obj_DataDictionary = { på ett: Ingen för på ett i random_data_list }

# Element att söka efter (finns i data)
slumptal_att_hitta = rndobj. val ( random_data_list )

# Mät tiden för att kontrollera medlemskap i en lista
list_time = tt. Timei ( lambda : random_number_to_find i random_data_list , siffra = 1000 )

# Mät tiden för att kontrollera medlemskap i en uppsättning
Ställ klockan = tt. Timei ( lambda : random_number_to_find i random_data_set , siffra = 1000 )

# Mät tiden för att kontrollera medlemskap i en ordbok
dict_time = tt. Timei ( lambda : random_number_to_find i obj_DataDictionary , siffra = 1000 )

skriva ut ( f 'Kontrolltid för listamedlemskap: {list_time:.6f} sekunder' )
skriva ut ( f 'Ställ in medlemskapskontrolltid: {set_time:.6f} sekunder' )
skriva ut ( f 'Kontrolltid för ordboksmedlemskap: {dict_time:.6f} sekunder' )

Den här koden jämför prestanda för listor, uppsättningar och ordböcker när du gör medlemskontroller. I allmänhet är uppsättningar och ordböcker betydligt snabbare än listor för medlemskapstester eftersom de använder hashbaserade uppslagningar, så de har en genomsnittlig tidskomplexitet på O(1). Listor, å andra sidan, måste göra linjära sökningar som resulterar i medlemskapstester med O(n) tidskomplexitet.

  En skärmdump av en dator Beskrivning genereras automatiskt

Använd de inbyggda funktionerna istället för loopar

Många inbyggda funktioner eller metoder i Python kan användas för att utföra typiska uppgifter som filtrering, sortering och kartläggning. Att använda dessa rutiner istället för att skapa sina loopar hjälper till att påskynda koden eftersom de ofta är prestandaoptimerade.

Låt oss bygga lite exempelkod för att jämföra prestandan för att skapa anpassade loopar genom att använda de inbyggda funktionerna för typiska jobb (som map(), filter() och sorted()). Vi kommer att utvärdera hur väl de olika kartläggnings-, filtrerings- och sorteringsmetoderna fungerar.

BuiltInFunctions.py:

importera Timei som tt
# Exempellista på nummerlista
nummerlista = lista ( räckvidd ( 1 , 10 000 ) )

# Funktion till square numbers_list med hjälp av en loop
def square_using_loop ( nummerlista ) :
square_result = [ ]
för på ett i nummerlista:
square_result. bifoga ( på ett ** 2 )
lämna tillbaka square_result
# Funktion för att filtrera jämna nummer_lista med hjälp av en loop
def filter_even_using_loop ( nummerlista ) :
filter_result = [ ]
för på ett i nummerlista:
om på ett % 2 == 0 :
filter_result. bifoga ( på ett )
lämna tillbaka filter_result
# Funktion för att sortera numbers_list med hjälp av en loop
def sort_using_loop ( nummerlista ) :
lämna tillbaka sorterad ( nummerlista )
# Mät tiden till square numbers_list med map()
map_time = tt. Timei ( lambda : lista ( Karta ( lambda x: x ** 2 , nummerlista ) ) , siffra = 1000 )
# Mät tiden för att filtrera jämna nummer_lista med filter()
filter_time = tt. Timei ( lambda : lista ( filtrera ( lambda x: x % 2 == 0 , nummerlista ) ) , siffra = 1000 )
# Mät tiden för att sortera numbers_list med sortered()
sorterad_tid = tt. Timei ( lambda : sorterad ( nummerlista ) , siffra = 1000 )
# Mät tiden till square numbers_list med hjälp av en slinga
loop_map_time = tt. Timei ( lambda : square_using_loop ( nummerlista ) , siffra = 1000 )
# Mät tiden för att filtrera jämna nummer_lista med hjälp av en slinga
loop_filter_time = tt. Timei ( lambda : filter_even_using_loop ( nummerlista ) , siffra = 1000 )
# Mät tiden för att sortera numbers_list med hjälp av en slinga
loop_sorted_time = tt. Timei ( lambda : sort_using_loop ( nummerlista ) , siffra = 1000 )
skriva ut ( 'Sifferlistan innehåller 10 000 element' )
skriva ut ( f 'Map() Tid: {map_time:.6f} sekunder' )
skriva ut ( f 'Filter() Tid: {filter_time:.6f} sekunder' )
skriva ut ( f 'Sorterad() Tid: {sorted_time:.6f} sekunder' )
skriva ut ( f 'Slinga (karta) tid: {loop_map_time:.6f} sekunder' )
skriva ut ( f 'Slinga (filter) tid: {loop_filter_time:.6f} sekunder' )
skriva ut ( f 'Slinga (sorterad) tid: {loop_sorted_time:.6f} sekunder' )

Vi kommer sannolikt att observera att de inbyggda funktionerna (map(), filter() och sortered()) är snabbare än de anpassade looparna för dessa vanliga uppgifter. De inbyggda funktionerna i Python erbjuder en mer kortfattad och begriplig metod för att utföra dessa uppgifter och är mycket optimerade för prestanda.

Optimera slingorna

Om det är nödvändigt att skriva slingorna finns det några tekniker som vi kan göra för att påskynda dem. I allmänhet är range()-loopen snabbare än att iterera bakåt. Detta beror på att range() genererar en iterator utan att invertera listan, vilket kan vara en kostsam operation för långa listor. Dessutom, eftersom range() inte bygger en ny lista i minnet, använder den mindre minne.

OptimizeLoop.py:

importera Timei som tt
# Exempellista på nummerlista
nummerlista = lista ( räckvidd ( 1 , 100 000 ) )
# Funktion för att iterera över listan i omvänd ordning
def loop_reverse_iteration ( ) :
resultat_omvänd = [ ]
för j i räckvidd ( endast ( nummerlista ) - 1 , - 1 , - 1 ) :
resultat_omvänd. bifoga ( nummerlista [ j ] )
lämna tillbaka resultat_omvänd
# Funktion för att iterera över listan med range()
def loop_range_iteration ( ) :
resultatintervall = [ ]
för k i räckvidd ( endast ( nummerlista ) ) :
resultatintervall. bifoga ( nummerlista [ k ] )
lämna tillbaka resultatintervall
# Mät tiden det tar att utföra omvänd iteration
omvänd_tid = tt. Timei ( loop_reverse_iteration , siffra = 1000 )
# Mät tiden det tar att utföra intervalliteration
range_time = tt. Timei ( loop_range_iteration , siffra = 1000 )
skriva ut ( 'Nummerlistan innehåller 100 000 poster' )
skriva ut ( f 'Omvänd iterationstid: {reverse_time:.6f} sekunder' )
skriva ut ( f 'Range Iteration Time: {range_time:.6f} sekunder' )

Undvik onödiga funktionssamtal

Det finns en viss overhead varje gång en funktion anropas. Koden går snabbare om onödiga funktionsanrop undviks. Till exempel, istället för att upprepade gånger utföra en funktion som beräknar ett värde, försök att lagra resultatet av beräkningen i en variabel och använda den.

Verktyg för profilering

För att lära dig mer om prestandan för din kod, förutom inbyggd profilering, kan vi använda externa profileringspaket som cProfile, Pyflame eller SnakeViz.

Cache-resultat

Om vår kod behöver utföra dyra beräkningar kan vi överväga att cachelagra resultaten för att spara tid.

Kodrefaktorering

Att ändra koden för att göra den lättare att läsa och underhålla är ibland en nödvändig del av att optimera den. Ett snabbare program kan också vara renare.

Använd Just-in-Time Compilation (JIT)

Bibliotek som PyPy eller Numba kan tillhandahålla en JIT-kompilering som avsevärt kan påskynda vissa typer av Python-kod.

Uppgradera Python

Se till att du använder den senaste versionen av Python eftersom nyare versioner ofta innehåller prestandaförbättringar.

Parallellism och samtidighet

För processer som kan parallelliseras, undersök parallell- och synkroniseringstekniker som multiprocessing, threading eller asyncio.

Kom ihåg att benchmarking och profilering bör vara de viktigaste drivkrafterna för optimering. Koncentrera dig på att förbättra de områden i vår kod som har de mest betydande effekterna på prestandan, och testa ständigt dina förbättringar för att se till att de har önskad effekt utan att införa fler defekter.

Slutsats

Sammanfattningsvis är Python-kodoptimering avgörande för förbättrad prestanda och resurseffektivitet. Utvecklare kan avsevärt öka exekveringshastigheten och lyhördheten för sina Python-applikationer genom att använda olika tekniker som att välja lämpliga datastrukturer, utnyttja de inbyggda funktionerna, minska de extra looparna och effektivt hantera minnet. Kontinuerlig benchmarking och profilering borde styra optimeringsinsatserna och säkerställa att kodutvecklingen matchar de verkliga prestandakraven. För att garantera en långsiktig projektframgång och minska chansen att introducera nya problem, bör optimering av koden ständigt balanseras med målen för kodläsbarhet och underhållsbarhet.