Kako optimizirati svoje skripte Python za boljšo zmogljivost

Kako Optimizirati Svoje Skripte Python Za Boljso Zmogljivost



Optimiziranje skriptov Python za boljše delovanje vključuje prepoznavanje in odpravljanje ozkih grl v naši kodi, zaradi česar bo delovala hitreje in učinkoviteje. Python je priljubljen in zmogljiv programski jezik, ki se danes uporablja v številnih aplikacijah, vključno z analizo podatkov, projekti ML (strojno učenje), spletnim razvojem in številnimi drugimi. Optimizacija kode Python je strategija za izboljšanje hitrosti in učinkovitosti programa za razvijalce pri izvajanju katere koli dejavnosti z uporabo manj vrstic kode, manj pomnilnika ali dodatnih virov. Velika in neučinkovita koda lahko upočasni program, kar lahko povzroči nezadovoljstvo strank in potencialno finančno izgubo ali potrebo po dodatnem delu za popravljanje in odpravljanje težav.

Potreben je pri opravljanju naloge, ki zahteva obdelavo več dejanj ali podatkov. Zato ima lahko izklop in izboljšanje nekaterih neučinkovitih blokov kode in funkcionalnosti neverjetne rezultate, kot so naslednji:

  1. Povečajte delovanje aplikacije
  2. Ustvarite berljivo in organizirano kodo
  3. Poenostavite spremljanje napak in odpravljanje napak
  4. Prihranite precejšnjo računalniško moč in tako naprej

Profilirajte svojo kodo

Preden začnemo z optimizacijo, je nujno identificirati dele kode projekta, ki ga upočasnjujejo. Tehnike za profiliranje v Pythonu vključujejo pakete cProfile in profile. Uporabite takšna orodja, da ocenite, kako hitro se izvajajo določene funkcije in vrstice kode. Modul cProfile izdela poročilo s podrobnostmi o tem, kako dolgo traja izvajanje posamezne funkcije skripta. To poročilo nam lahko pomaga najti vse funkcije, ki delujejo počasi, da jih lahko izboljšamo.







Delček kode:



uvoz cProfil kot cP
def izračunajSum ( inputNumber ) :
vsota_vhodnih_števil = 0
medtem inputNumber > 0 :
vsota_vhodnih_števil + = inputNumber % 10
inputNumber // = 10
tiskanje ( 'Vsota vseh števk v vnesenem številu je: 'sum_of_input_numbers'' )
vrnitev vsota_vhodnih_števil
def main_func ( ) :
cP. teči ( 'izračunajSum(9876543789)' )
če __ime__ == '__glavni__' :
main_func ( )

Program opravi skupno pet funkcijskih klicev, kot je prikazano v prvi vrstici izhoda. Podrobnosti o vsakem klicu funkcije so prikazane v naslednjih nekaj vrsticah, vključno s številom klicev funkcije, skupnim trajanjem časa v funkciji, trajanjem časa na klic in skupnim časom v funkciji (vključno s vse funkcije, ki jih imenuje).



Poleg tega program natisne poročilo na pozivnem zaslonu, ki kaže, da program zaključi čas izvajanja vseh svojih nalog v 0,000 sekundah. To kaže, kako hiter je program.





Izberite pravo strukturo podatkov

Značilnosti delovanja so odvisne od strukture podatkov. Zlasti slovarji so hitrejši za iskanje kot seznami v zvezi s shranjevanjem za splošne namene. Izberite podatkovno strukturo, ki je najprimernejša za operacije, ki jih bomo izvajali z vašimi podatki, če jih poznate. Naslednji primer raziskuje učinkovitost različnih podatkovnih struktur za identičen proces, da se ugotovi, ali je element v podatkovni strukturi prisoten.



Ocenjujemo čas, potreben za preverjanje, ali je element prisoten v vsaki podatkovni strukturi – seznamu, nizu in slovarju – in ju primerjamo.

OptimizeDataType.py:

uvoz Timei kot tt
uvoz naključen kot rndobj
# Ustvarite seznam celih števil
naključni_seznam_podatkov = [ rndobj. randint ( 1 , 10000 ) za _ v obseg ( 10000 ) ]
# Ustvarite niz iz istih podatkov
naključni_nabor_podatkov = set ( naključni_seznam_podatkov )

# Ustvarite slovar z enakimi podatki kot ključi
obj_DataDictionary = { na enem: Noben za na enem v naključni_seznam_podatkov }

# Element za iskanje (obstaja v podatkih)
naključno_število_za_najti = rndobj. izbira ( naključni_seznam_podatkov )

# Izmerite čas za preverjanje članstva na seznamu
seznam_čas = tt. Timei ( lambda : naključno_število_za_najti v naključni_seznam_podatkov , število = 1000 )

# Izmerite čas za preverjanje pripadnosti nizu
nastavi čas = tt. Timei ( lambda : naključno_število_za_najti v naključni_nabor_podatkov , število = 1000 )

# Izmerite čas za preverjanje članstva v slovarju
dict_time = tt. Timei ( lambda : naključno_število_za_najti v obj_DataDictionary , število = 1000 )

tiskanje ( f 'Čas preverjanja članstva na seznamu: {list_time:.6f} sekund' )
tiskanje ( f 'Nastavi čas preverjanja članstva: {set_time:.6f} sekund' )
tiskanje ( f 'Čas preverjanja članstva v slovarju: {dict_time:.6f} sekund' )

Ta koda primerja učinkovitost seznamov, nizov in slovarjev pri preverjanju članstva. Na splošno so nizi in slovarji bistveno hitrejši od seznamov za teste članstva, ker uporabljajo iskanje na podlagi zgoščevanja, zato imajo povprečno časovno kompleksnost O(1). Po drugi strani morajo seznami izvajati linearna iskanja, kar ima za posledico teste članstva z O(n) časovno zapletenostjo.

  Posnetek zaslona računalnika, samodejno ustvarjen opis

Namesto zank uporabite vgrajene funkcije

Številne vgrajene funkcije ali metode v Pythonu je mogoče uporabiti za izvajanje tipičnih nalog, kot so filtriranje, razvrščanje in preslikava. Uporaba teh rutin namesto ustvarjanja lastnih zank pomaga pospešiti kodo, ker so pogosto optimizirane za delovanje.

Ustvarimo nekaj vzorčne kode za primerjavo uspešnosti ustvarjanja zank po meri z uporabo vgrajenih funkcij za tipična opravila (kot so map(), filter() in sorted()). Ocenili bomo, kako uspešni so različni načini preslikave, filtriranja in razvrščanja.

BuiltInFunctions.py:

uvoz Timei kot tt
# Vzorec seznama number_list
seznam_številk = seznam ( obseg ( 1 , 10000 ) )

# Funkcija za kvadriranje numbers_list z uporabo zanke
def kvadratna_uporaba_zanke ( seznam_številk ) :
kvadratni_rezultat = [ ]
za na enem v seznam_številk:
kvadratni_rezultat. priložiti ( na enem ** 2 )
vrnitev kvadratni_rezultat
# Funkcija za filtriranje seznama sodih števil z uporabo zanke
def filter_even_using_loop ( seznam_številk ) :
filter_rezultat = [ ]
za na enem v seznam_številk:
če na en % 2 == 0 :
filter_rezultat. priložiti ( na enem )
vrnitev filter_rezultat
# Funkcija za razvrščanje numbers_list z uporabo zanke
def razvrščanje_z_zanko ( seznam_številk ) :
vrnitev razvrščeno ( seznam_številk )
# Izmerite čas do kvadriranja numbers_list z uporabo map()
zemljevid_čas = tt. Timei ( lambda : seznam ( zemljevid ( lambda x: x ** 2 , seznam_številk ) ) , število = 1000 )
# Izmerite čas za filtriranje sodih številk_list z uporabo filter()
filter_time = tt. Timei ( lambda : seznam ( filter ( lambda x: x % 2 == 0 , seznam_številk ) ) , število = 1000 )
# Izmerite čas za razvrščanje numbers_list z uporabo sorted()
razvrščeni_čas = tt. Timei ( lambda : razvrščeno ( seznam_številk ) , število = 1000 )
# Izmerite čas do kvadriranja numbers_list z uporabo zanke
loop_map_time = tt. Timei ( lambda : square_using_loop ( seznam_številk ) , število = 1000 )
# Izmerite čas za filtriranje seznama sodih števil z uporabo zanke
loop_filter_time = tt. Timei ( lambda : filter_even_using_loop ( seznam_številk ) , število = 1000 )
# Izmerite čas za razvrščanje numbers_list z uporabo zanke
loop_sorted_time = tt. Timei ( lambda : sort_using_loop ( seznam_številk ) , število = 1000 )
tiskanje ( 'Seznam številk vsebuje 10000 elementov' )
tiskanje ( f 'Map() Čas: {map_time:.6f} sekund' )
tiskanje ( f 'Čas Filter(): {filter_time:.6f} sekund' )
tiskanje ( f 'Sorted() Čas: {sorted_time:.6f} sekund' )
tiskanje ( f 'Čas zanke (zemljevida): {loop_map_time:.6f} sekund' )
tiskanje ( f 'Čas zanke (filtra): {loop_filter_time:.6f} sekund' )
tiskanje ( f 'Čas zanke (razvrščen): {loop_sorted_time:.6f} sekund' )

Verjetno bomo opazili, da so vgrajene funkcije (map(), filter() in sorted()) hitrejše od zank po meri za ta običajna opravila. Vgrajene funkcije v Pythonu ponujajo bolj jedrnat in razumljiv pristop za izvajanje teh nalog in so zelo optimizirane za delovanje.

Optimizirajte zanke

Če je pisanje zank potrebno, obstaja nekaj tehnik, s katerimi jih lahko pospešimo. Na splošno je zanka range() hitrejša od ponavljanja nazaj. To je zato, ker range() ustvari iterator brez obračanja seznama, kar je lahko draga operacija za dolge sezname. Poleg tega, ker range() ne ustvari novega seznama v pomnilniku, uporablja manj pomnilnika.

OptimizeLoop.py:

uvoz Timei kot tt
# Vzorec seznama number_list
seznam_številk = seznam ( obseg ( 1 , 100000 ) )
# Funkcija za ponavljanje po seznamu v obratnem vrstnem redu
def loop_reverse_iteration ( ) :
rezultat_obraten = [ ]
za j v obseg ( samo ( seznam_številk ) - 1 , - 1 , - 1 ) :
rezultat_obraten. priložiti ( seznam_številk [ j ] )
vrnitev rezultat_obraten
# Funkcija za ponavljanje po seznamu z uporabo range()
def iteracija_razpona_zanke ( ) :
obseg_rezultata = [ ]
za k v obseg ( samo ( seznam_številk ) ) :
obseg_rezultata. priložiti ( seznam_številk [ k ] )
vrnitev obseg_rezultata
# Izmerite čas, potreben za izvedbo obratne iteracije
obratni_čas = tt. Timei ( loop_reverse_iteration , število = 1000 )
# Izmerite čas, potreben za izvedbo iteracije obsega
obseg_čas = tt. Timei ( iteracija_razpona_zanke , število = 1000 )
tiskanje ( 'Seznam številk vsebuje 100000 zapisov' )
tiskanje ( f 'Čas povratne iteracije: {reverse_time:.6f} sekund' )
tiskanje ( f 'Čas ponovitve obsega: {range_time:.6f} sekund' )

Izogibajte se nepotrebnim klicem funkcij

Ob vsakem klicu funkcije je nekaj dodatnih stroškov. Koda deluje hitreje, če se izognemo nepotrebnim klicem funkcij. Na primer, namesto da večkrat izvajate funkcijo, ki izračuna vrednost, poskusite rezultat izračuna shraniti v spremenljivko in ga uporabiti.

Orodja za profiliranje

Če želite izvedeti več o učinkovitosti vaše kode, lahko poleg vgrajenega profiliranja uporabimo zunanje pakete za profiliranje, kot so cProfile, Pyflame ali SnakeViz.

Predpomnilnik rezultatov

Če mora naša koda izvajati drage izračune, lahko razmislimo o predpomnjenju rezultatov, da prihranimo čas.

Preoblikovanje kode

Preoblikovanje kode za lažje branje in vzdrževanje je včasih nujen del njene optimizacije. Hitrejši program je lahko tudi čistejši.

Uporabite Just-in-Time Compilation (JIT)

Knjižnice, kot sta PyPy ali Numba, lahko zagotovijo kompilacijo JIT, ki lahko znatno pospeši nekatere vrste kode Python.

Nadgradite Python

Prepričajte se, da uporabljate najnovejšo različico Pythona, saj novejše različice pogosto vključujejo izboljšave zmogljivosti.

Paralelizem in sočasnost

Za procese, ki jih je mogoče paralelizirati, raziščite vzporedne in sinhronizacijske tehnike, kot so večprocesiranje, navoji ali asyncio.

Ne pozabite, da bi morala biti primerjalna analiza in profiliranje glavna gonila optimizacije. Osredotočite se na izboljšanje področij naše kode, ki najbolj vplivajo na zmogljivost, in nenehno preizkušajte svoje izboljšave, da se prepričate, ali imajo želene učinke, ne da bi pri tem povzročili dodatne napake.

Zaključek

Skratka, optimizacija kode Python je ključnega pomena za izboljšano zmogljivost in učinkovitost virov. Razvijalci lahko močno povečajo hitrost izvajanja in odzivnost svojih aplikacij Python z uporabo različnih tehnik, kot so izbiranje ustreznih podatkovnih struktur, izkoriščanje vgrajenih funkcij, zmanjšanje dodatnih zank in učinkovito upravljanje pomnilnika. Nenehno primerjalno preizkušanje in profiliranje bi moralo usmerjati prizadevanja za optimizacijo in zagotoviti, da se napredek kode ujema z zahtevami glede zmogljivosti v resničnem svetu. Da bi zagotovili dolgoročni uspeh projekta in zmanjšali možnost uvajanja novih težav, je treba optimizacijo kode nenehno uravnotežiti s cilji berljivosti kode in vzdržljivosti.