Kako optimizirati svoje Python skripte za bolju izvedbu

Kako Optimizirati Svoje Python Skripte Za Bolju Izvedbu



Optimiziranje Python skripti za bolju izvedbu uključuje prepoznavanje i rješavanje uskih grla u našem kodu, čineći ga bržim i učinkovitijim. Python je popularan i moćan programski jezik koji se danas koristi u brojnim aplikacijama uključujući analizu podataka, ML projekte (strojno učenje), web razvoj i mnoge druge. Optimizacija Python koda je strategija za poboljšanje brzine i učinkovitosti programa razvojnog programera pri izvođenju bilo koje aktivnosti koristeći manje redaka koda, manje memorije ili dodatnih resursa. Velik i neučinkovit kod može usporiti program što može rezultirati slabim zadovoljstvom klijenata i potencijalnim financijskim gubitkom ili potrebom za dodatnim radom na popravljanju i rješavanju problema.

Neophodno je tijekom obavljanja zadatka koji zahtijeva obradu nekoliko radnji ili podataka. Stoga isključivanje i poboljšanje nekih neučinkovitih blokova koda i funkcionalnosti može imati nevjerojatne rezultate poput sljedećih:

  1. Poboljšajte performanse aplikacije
  2. Stvorite čitljiv i organiziran kod
  3. Učinite praćenje pogrešaka i otklanjanje pogrešaka jednostavnijim
  4. Sačuvajte značajnu računsku snagu i tako dalje

Profilirajte svoj kod

Prije nego počnemo optimizirati, bitno je identificirati dijelove koda projekta koji ga usporavaju. Tehnike za profiliranje u Pythonu uključuju cProfile i profile pakete. Upotrijebite takve alate da biste procijenili koliko se brzo izvršavaju određene funkcije i linije koda. Modul cProfile proizvodi izvješće koje detaljno opisuje koliko je vremena potrebno za izvođenje svake funkcije skripte. Ovo nam izvješće može pomoći da pronađemo funkcije koje sporo rade kako bismo ih mogli poboljšati.







Isječak koda:



uvoz cProfil kao cP
def izračunatiZbroj ( ulazniBroj ) :
zbroj_ulaznih_brojeva = 0
dok ulazniBroj > 0 :
zbroj_ulaznih_brojeva + = inputNumber % 10
ulazniBroj // = 10
ispisati ( 'Zbroj svih znamenki u ulaznom broju je: 'zbroj_unesenih_brojeva'' )
povratak zbroj_ulaznih_brojeva
def glavna_funkcija ( ) :
cP. trčanje ( 'izračunajZbroj(9876543789)' )
ako __Ime__ == '__glavni__' :
glavna_funkcija ( )

Program čini ukupno pet poziva funkcija kao što se vidi u prvom retku izlaza. Pojedinosti o svakom pozivu funkcije prikazane su u sljedećih nekoliko redaka, uključujući broj pozivanja funkcije, ukupno trajanje vremena u funkciji, trajanje vremena po pozivu i ukupnu količinu vremena u funkciji (uključujući sve funkcije koje se pozivaju).



Osim toga, program ispisuje izvješće na promptnom zaslonu koje pokazuje da program završava vrijeme izvršenja svih svojih zadataka unutar 0,000 sekundi. Ovo pokazuje koliko je program brz.





Odaberite pravu strukturu podataka

Karakteristike izvedbe ovise o strukturi podataka. Konkretno, rječnici su brži za traženje nego popisi u vezi s pohranom opće namjene. Odaberite strukturu podataka koja je najprikladnija za operacije koje ćemo provesti na vašim podacima ako ih poznajete. Sljedeći primjer istražuje učinkovitost različitih struktura podataka za identičan proces kako bi se utvrdilo je li element u strukturi podataka prisutan.



Procjenjujemo vrijeme potrebno za provjeru je li element prisutan u svakoj podatkovnoj strukturi — popisu, skupu i rječniku — i uspoređujemo ih.

OptimizeDataType.py:

uvoz Timei kao tt
uvoz slučajan kao rndobj
# Generirajte popis cijelih brojeva
popis_slučajnih_podataka = [ rndobj. randint ( 1 , 10000 ) za _ u domet ( 10000 ) ]
# Stvorite skup iz istih podataka
slučajni_skup_podataka = postaviti ( popis_slučajnih_podataka )

# Napravite rječnik s istim podacima kao i ključevi
obj_DataDictionary = { na jedan: Nijedan za na jedan u popis_slučajnih_podataka }

# Element za traženje (postoji u podacima)
slučajni_broj_za_nalaženje = rndobj. izbor ( popis_slučajnih_podataka )

# Izmjerite vrijeme za provjeru članstva na popisu
popis_vrijeme = tt. Timei ( lambda : slučajni_broj_za_nalaženje u popis_slučajnih_podataka , broj = 1000 )

# Izmjerite vrijeme za provjeru pripadnosti skupu
Postavi vrijeme = tt. Timei ( lambda : slučajni_broj_za_nalaženje u slučajni_skup_podataka , broj = 1000 )

# Izmjerite vrijeme za provjeru članstva u rječniku
dict_vrijeme = tt. Timei ( lambda : slučajni_broj_za_nalaženje u obj_DataDictionary , broj = 1000 )

ispisati ( f 'Vrijeme provjere članstva na popisu: {list_time:.6f} sekundi' )
ispisati ( f 'Postavi vrijeme provjere članstva: {set_time:.6f} sekundi' )
ispisati ( f 'Vrijeme provjere članstva u rječniku: {dict_time:.6f} sekundi' )

Ovaj kod uspoređuje izvedbu popisa, skupova i rječnika prilikom provjera članstva. Općenito, skupovi i rječnici znatno su brži od popisa za testove članstva jer koriste pretraživanja temeljena na hash-u, tako da imaju prosječnu vremensku složenost od O(1). Popisi, s druge strane, moraju provoditi linearna pretraživanja koja rezultiraju testovima pripadnosti s O(n) vremenskom složenošću.

  Automatski generirani snimak zaslona računala Opis

Koristite ugrađene funkcije umjesto petlji

Brojne ugrađene funkcije ili metode u Pythonu mogu se koristiti za obavljanje tipičnih zadataka poput filtriranja, sortiranja i mapiranja. Korištenje ovih rutina umjesto stvaranja vlastitih petlji pomaže ubrzati kod jer su često optimizirane za izvedbu.

Napravimo primjer koda za usporedbu izvedbe kreiranja prilagođenih petlji korištenjem ugrađenih funkcija za tipične poslove (kao što su map(), filter() i sorted()). Procijenit ćemo koliko su dobre različite metode mapiranja, filtriranja i sortiranja.

BuiltInFunctions.py:

uvoz Timei kao tt
# Primjer popisa brojeva_popisa
popis_brojeva = popis ( domet ( 1 , 10000 ) )

# Funkcija kvadriranja brojeva_popisa pomoću petlje
def kvadrat_koristeći_petlju ( popis_brojeva ) :
kvadratni_rezultat = [ ]
za na jedan u popis_brojeva:
kvadratni_rezultat. dodati ( na jedan ** 2 )
povratak kvadratni_rezultat
# Funkcija za filtriranje popisa parnih brojeva pomoću petlje
def filter_čak_upotrebom_petlje ( popis_brojeva ) :
filter_rezultat = [ ]
za na jedan u popis_brojeva:
ako na jedan % 2 == 0 :
filter_rezultat. dodati ( na jedan )
povratak filter_rezultat
# Funkcija za sortiranje popisa brojeva pomoću petlje
def sortiranje_upotrebom_petlje ( popis_brojeva ) :
povratak sortirano ( popis_brojeva )
# Izmjerite vrijeme do kvadriranja numbers_list koristeći map()
map_time = tt. Timei ( lambda : popis ( karta ( lambda x: x ** 2 , popis_brojeva ) ) , broj = 1000 )
# Izmjerite vrijeme za filtriranje parnih brojeva_list koristeći filter()
vrijeme_filtra = tt. Timei ( lambda : popis ( filtar ( lambda x: x % 2 == 0 , popis_brojeva ) ) , broj = 1000 )
# Izmjerite vrijeme sortiranja numbers_list koristeći sorted()
sortirano_vrijeme = tt. Timei ( lambda : sortirano ( popis_brojeva ) , broj = 1000 )
# Izmjerite vrijeme do kvadriranja numbers_list pomoću petlje
vrijeme_mape_petlje = tt. Timei ( lambda : kvadrat_koristeći_petlju ( popis_brojeva ) , broj = 1000 )
# Izmjerite vrijeme za filtriranje popisa parnih brojeva pomoću petlje
vrijeme_filtra_petlje = tt. Timei ( lambda : filter_even_using_loop ( popis_brojeva ) , broj = 1000 )
# Izmjerite vrijeme sortiranja numbers_list pomoću petlje
petlja_sortirano_vrijeme = tt. Timei ( lambda : razvrstavanje_upotrebom_petlje ( popis_brojeva ) , broj = 1000 )
ispisati ( 'Popis brojeva sadrži 10000 elemenata' )
ispisati ( f 'Map() Vrijeme: {map_time:.6f} sekundi' )
ispisati ( f 'Filter() Vrijeme: {filter_time:.6f} sekundi' )
ispisati ( f 'Sorted() Vrijeme: {sorted_time:.6f} sekundi' )
ispisati ( f 'Vrijeme petlje (mape): {loop_map_time:.6f} sekundi' )
ispisati ( f 'Vrijeme petlje (filtara): {loop_filter_time:.6f} sekundi' )
ispisati ( f 'Vrijeme petlje (razvrstano): {loop_sorted_time:.6f} sekundi' )

Vjerojatno ćemo primijetiti da su ugrađene funkcije (map(), filter() i sorted()) brže od prilagođenih petlji za ove uobičajene zadatke. Ugrađene funkcije u Pythonu nude koncizniji i razumljiviji pristup za izvršavanje ovih zadataka i visoko su optimizirane za izvedbu.

Optimizirajte petlje

Ako je pisanje petlji potrebno, postoji nekoliko tehnika koje možemo učiniti da ih ubrzamo. Općenito, petlja range() je brža od ponavljanja unatrag. To je zato što range() generira iterator bez invertiranja popisa što može biti skupa operacija za dugačke popise. Osim toga, budući da range() ne gradi novi popis u memoriji, koristi manje memorije.

OptimizeLoop.py:

uvoz Timei kao tt
# Primjer popisa brojeva_popisa
popis_brojeva = popis ( domet ( 1 , 100 000 ) )
# Funkcija za ponavljanje popisa obrnutim redoslijedom
def obrnuto_iteriranje_petlje ( ) :
rezultat_obrnuti = [ ]
za j u domet ( samo ( popis_brojeva ) - 1 , - 1 , - 1 ) :
rezultat_obrnuti. dodati ( popis_brojeva [ j ] )
povratak rezultat_obrnuti
# Funkcija za ponavljanje po popisu pomoću range()
def iteracija_raspona_petlje ( ) :
raspon_rezultata = [ ]
za k u domet ( samo ( popis_brojeva ) ) :
raspon_rezultata. dodati ( popis_brojeva [ k ] )
povratak raspon_rezultata
# Izmjerite vrijeme potrebno za izvođenje obrnute iteracije
obrnuto_vrijeme = tt. Timei ( obrnuto_iteriranje_petlje , broj = 1000 )
# Izmjerite vrijeme potrebno za izvođenje iteracije raspona
raspon_vrijeme = tt. Timei ( iteracija_raspona_petlje , broj = 1000 )
ispisati ( 'Popis brojeva sadrži 100 000 zapisa' )
ispisati ( f 'Vrijeme obrnute iteracije: {reverse_time:.6f} sekundi' )
ispisati ( f 'Vrijeme ponavljanja raspona: {range_time:.6f} sekundi' )

Izbjegavajte nepotrebne pozive funkcija

Svaki put kad se neka funkcija pozove, postoji nešto dodatnih troškova. Kod se izvodi brže ako se izbjegnu nepotrebni pozivi funkcija. Na primjer, umjesto opetovanog izvršavanja funkcije koja izračunava vrijednost, pokušajte pohraniti rezultat izračuna u varijablu i koristiti ga.

Alati za profiliranje

Kako bismo saznali više o izvedbi vašeg koda, uz ugrađeno profiliranje, možemo koristiti vanjske pakete za profiliranje kao što su cProfile, Pyflame ili SnakeViz.

Predmemorija rezultata

Ako naš kod treba izvesti skupe izračune, mogli bismo razmotriti predmemoriranje rezultata kako bismo uštedjeli vrijeme.

Refactoring koda

Refaktoriranje koda kako bi ga bilo lakše čitati i održavati ponekad je nužan dio njegove optimizacije. Brži program također može biti čišći.

Koristite Just-in-Time kompilaciju (JIT)

Knjižnice poput PyPy ili Numba mogu pružiti JIT kompilaciju koja može značajno ubrzati određene vrste Python koda.

Nadogradite Python

Provjerite koristite li najnoviju verziju Pythona jer novije verzije često uključuju poboljšanja performansi.

Paralelizam i istovremenost

Za procese koji se mogu paralelizirati, istražite paralelne i sinkronizacijske tehnike kao što su višeprocesiranje, threading ili asyncio.

Ne zaboravite da usporedna analiza i profiliranje trebaju biti glavni pokretači optimizacije. Usredotočite se na poboljšanje područja našeg koda koja imaju najznačajniji učinak na izvedbu i stalno testirajte svoja poboljšanja kako biste bili sigurni da imaju željene učinke bez unošenja novih nedostataka.

Zaključak

Zaključno, optimizacija Python koda ključna je za poboljšane performanse i učinkovitost resursa. Programeri mogu uvelike povećati brzinu izvođenja i odziv svojih Python aplikacija koristeći različite tehnike kao što su odabir odgovarajućih struktura podataka, korištenje ugrađenih funkcija, smanjenje dodatnih petlji i učinkovito upravljanje memorijom. Kontinuirani benchmarking i profiliranje trebali bi usmjeriti napore optimizacije, osiguravajući da napredak koda odgovara zahtjevima izvedbe u stvarnom svijetu. Kako bi se zajamčio dugoročni uspjeh projekta i smanjila mogućnost uvođenja novih problema, optimizacija koda treba stalno biti u ravnoteži s ciljevima čitljivosti koda i lakoće održavanja.