Com optimitzar els vostres scripts de Python per obtenir un millor rendiment

Com Optimitzar Els Vostres Scripts De Python Per Obtenir Un Millor Rendiment



Optimitzar els scripts de Python per obtenir un millor rendiment implica identificar i abordar els colls d'ampolla del nostre codi, fent-lo funcionar més ràpid i més eficient. Python és un llenguatge de programació popular i potent que s'utilitza en nombroses aplicacions avui en dia, com ara l'anàlisi de dades, projectes ML (aprenentatge automàtic), desenvolupament web i moltes més. L'optimització del codi Python és una estratègia per millorar la velocitat i l'eficiència del programa de desenvolupament quan es realitza qualsevol activitat utilitzant menys línies de codi, menys memòria o recursos addicionals. El codi gran i ineficient pot alentir el programa, cosa que pot provocar una satisfacció deficient del client i possibles pèrdues financeres, o la necessitat de més treball per solucionar i solucionar problemes.

És necessari mentre es realitza una tasca que requereix processar diverses accions o dades. Per tant, canviar i millorar alguns blocs de codi i funcionalitats ineficaços pot tenir resultats sorprenents com els següents:

  1. Augmenta el rendiment de l'aplicació
  2. Creeu codi llegible i organitzat
  3. Simplifica el seguiment i la depuració d'errors
  4. Conservar una potència de càlcul considerable, etc

Perfil el teu codi

Abans de començar a optimitzar, és essencial identificar les parts del codi del projecte que el frenen. Les tècniques per crear perfils a Python inclouen els paquets cProfile i profile. Utilitzeu aquestes eines per avaluar la rapidesa amb què s'executen determinades funcions i línies de codi. El mòdul cProfile produeix un informe que detalla quant de temps triga a executar-se cada funció d'script. Aquest informe ens pot ajudar a trobar les funcions que s'estan executant lentament per poder millorar-les.







Fragment de codi:



importar cPerfil com cP
def calcula Suma ( inputNumber ) :
suma_de_nombres_d'entrada = 0
mentre inputNumber > 0 :
suma_de_nombres_d'entrada + = inputNumber % 10
inputNumber // = 10
imprimir ( 'La suma de tots els dígits del número d'entrada és: 'sum_of_input_numbers'' )
tornar suma_de_nombres_d'entrada
def funció_principal ( ) :
cP. correr ( 'calculateSum(9876543789)' )
si __nom__ == '__principal__' :
funció_principal ( )

El programa fa un total de cinc trucades de funció tal com es veu a la primera línia de la sortida. Els detalls de cada trucada de funció es mostren a les següents línies, inclòs el nombre de vegades que s'ha invocat la funció, la durada total de la funció, la durada del temps per trucada i la quantitat total de temps a la funció (incloent-hi totes les funcions que s'anomena).



A més, el programa imprimeix un informe a la pantalla de sol·licitud que mostra que el programa completa el temps d'execució de totes les seves tasques en 0.000 segons. Això mostra la velocitat del programa.





Trieu l'estructura de dades adequada

Les característiques de rendiment depenen de l'estructura de dades. En particular, els diccionaris són més ràpids per a les cerques que les llistes pel que fa a l'emmagatzematge de propòsit general. Seleccioneu l'estructura de dades que sigui més adequada per a les operacions que realitzarem sobre les vostres dades si les coneixeu. L'exemple següent investiga l'efectivitat de diferents estructures de dades per a un procés idèntic per determinar si hi ha un element a l'estructura de dades.



Avaluem el temps que triga a comprovar si hi ha un element a cada estructura de dades (una llista, un conjunt i un diccionari) i els comparem.

OptimizeDataType.py:

importar Timei com tt
importar aleatòria com rndobj
# Genera una llista de nombres enters
llista_de_dades_aleatòries = [ rndobj. randint ( 1 , 10000 ) per _ en rang ( 10000 ) ]
# Creeu un conjunt a partir de les mateixes dades
conjunt_de_dades_aleatòries = conjunt ( llista_de_dades_aleatòries )

# Creeu un diccionari amb les mateixes dades que les claus
obj_DataDictionary = { en un: Cap per en una en llista_de_dades_aleatòries }

# Element a cercar (existeix a les dades)
nombre_aleatori_a_trobar = rndobj. elecció ( llista_de_dades_aleatòries )

# Mesureu el temps per comprovar la pertinença a una llista
llista_hora = tt. Timei ( lambda : nombre_aleatori_a_trobar en llista_de_dades_aleatòries , nombre = 1000 )

# Mesureu el temps per comprovar la pertinença a un conjunt
set_time = tt. Timei ( lambda : nombre_aleatori_a_trobar en conjunt_de_dades_aleatòries , nombre = 1000 )

# Mesureu el temps per comprovar la pertinença a un diccionari
dict_time = tt. Timei ( lambda : nombre_aleatori_a_trobar en obj_DataDictionary , nombre = 1000 )

imprimir ( f 'Temps de comprovació de la pertinença a la llista: {list_time:.6f} segons' )
imprimir ( f 'Estableix el temps de comprovació de la pertinença: {set_time:.6f} segons' )
imprimir ( f 'Temps de comprovació de la pertinença al diccionari: {dict_time:.6f} segons' )

Aquest codi compara el rendiment de llistes, conjunts i diccionaris quan es fan comprovacions de pertinença. En general, els conjunts i els diccionaris són substancialment més ràpids que les llistes per a les proves de pertinença perquè utilitzen les cerques basades en hash, de manera que tenen una complexitat de temps mitjana de O(1). Les llistes, en canvi, han de fer cerques lineals que resultin en proves de pertinença amb complexitat de temps O(n).

  Captura de pantalla d'un ordinador Descripció generada automàticament

Utilitzeu les funcions integrades en lloc de bucles

Es poden utilitzar nombroses funcions o mètodes integrats a Python per dur a terme tasques típiques com ara filtrar, ordenar i mapejar. L'ús d'aquestes rutines en lloc de crear els propis bucles ajuda a accelerar el codi perquè sovint s'optimitzen el rendiment.

Construïm una mica de codi de mostra per comparar el rendiment de la creació de bucles personalitzats utilitzant les funcions integrades per a treballs típics (com ara map(), filter() i sortd()). Avaluarem el rendiment dels diferents mètodes de mapatge, filtració i classificació.

BuiltInFunctions.py:

importar Timei com tt
# Llista d'exemple de llista_números
llista_números = llista ( rang ( 1 , 10000 ) )

# Funció per quadrar numbers_list mitjançant un bucle
def quadrat_usant_bucle ( llista_números ) :
resultat_quadrat = [ ]
per en una en llista_números:
resultat_quadrat. adjuntar ( en un ** 2 )
tornar resultat_quadrat
# Funció per filtrar números parells_list mitjançant un bucle
def filter_even_using_loop ( llista_números ) :
resultat_filtre = [ ]
per en una en llista_números:
si en un % 2 == 0 :
resultat_filtre. adjuntar ( en una )
tornar resultat_filtre
# Funció per ordenar numbers_list mitjançant un bucle
def sort_usant_bucle ( llista_números ) :
tornar ordenat ( llista_números )
# Mesureu el temps per quadrar numbers_list utilitzant map()
mapa_hora = tt. Timei ( lambda : llista ( mapa ( lambda x: x ** 2 , llista_números ) ) , nombre = 1000 )
# Mesureu el temps per filtrar números parells_list mitjançant filter()
temps_filtre = tt. Timei ( lambda : llista ( filtre ( lambda x: x % 2 == 0 , llista_números ) ) , nombre = 1000 )
# Mesureu el temps per ordenar numbers_list mitjançant sorted()
temps_ordenat = tt. Timei ( lambda : ordenat ( llista_números ) , nombre = 1000 )
# Mesureu el temps per quadrar nombres_lista mitjançant un bucle
loop_map_time = tt. Timei ( lambda : quadrat_usant_bucle ( llista_números ) , nombre = 1000 )
# Mesureu el temps per filtrar la llista de números parells mitjançant un bucle
loop_filter_time = tt. Timei ( lambda : filter_even_using_loop ( llista_números ) , nombre = 1000 )
# Mesureu el temps per ordenar numbers_list mitjançant un bucle
loop_sorted_time = tt. Timei ( lambda : sort_using_loop ( llista_números ) , nombre = 1000 )
imprimir ( 'La llista de números conté 10.000 elements' )
imprimir ( f 'Map() Temps: {map_time:.6f} segons' )
imprimir ( f 'Filter() Time: {filter_time:.6f} segons' )
imprimir ( f 'Temps ordenat(): {sorted_time:.6f} segons' )
imprimir ( f 'Temps del bucle (mapa): {loop_map_time:.6f} segons' )
imprimir ( f 'Temps de bucle (filtre): {loop_filter_time:.6f} segons' )
imprimir ( f 'Temps de bucle (ordenat): {loop_sorted_time:.6f} segons' )

Probablement observem que les funcions integrades (map(), filter() i sorted()) són més ràpides que els bucles personalitzats per a aquestes tasques habituals. Les funcions integrades a Python ofereixen un enfocament més concís i entenedor per dur a terme aquestes tasques i estan molt optimitzades per al rendiment.

Optimitzar els bucles

Si cal escriure els bucles, hi ha algunes tècniques que podem fer per accelerar-los. En general, el bucle range() és més ràpid que la iteració cap enrere. Això es deu al fet que range() genera un iterador sense invertir la llista, cosa que pot ser una operació costosa per a llistes llargues. A més, com que range() no crea una llista nova a la memòria, utilitza menys memòria.

OptimizeLoop.py:

importar Timei com tt
# Llista d'exemple de llista_números
llista_números = llista ( rang ( 1 , 100000 ) )
# Funció per repetir la llista en ordre invers
def loop_reverse_iteration ( ) :
resultat_revers = [ ]
per j en rang ( només ( llista_números ) - 1 , - 1 , - 1 ) :
resultat_revers. adjuntar ( llista_números [ j ] )
tornar resultat_revers
# Funció per iterar per la llista utilitzant range()
def loop_range_iteration ( ) :
interval_resultats = [ ]
per k en rang ( només ( llista_números ) ) :
interval_resultats. adjuntar ( llista_números [ k ] )
tornar interval_resultats
# Mesureu el temps que triga a realitzar la iteració inversa
temps_revers = tt. Timei ( loop_reverse_iteration , nombre = 1000 )
# Mesureu el temps que triga a realitzar la iteració de l'interval
interval_temps = tt. Timei ( loop_range_iteration , nombre = 1000 )
imprimir ( 'La llista de números conté 100.000 registres' )
imprimir ( f 'Temps d'iteració inversa: {reverse_time:.6f} segons' )
imprimir ( f 'Temps d'iteració de l'interval: {range_time:.6f} segons' )

Eviteu trucades de funcions innecessàries

Hi ha una mica de sobrecàrrega cada vegada que es crida una funció. El codi s'executa més ràpidament si s'evitan les trucades de funcions innecessàries. Per exemple, en lloc d'executar repetidament una funció que calcula un valor, proveu d'emmagatzemar el resultat del càlcul en una variable i utilitzar-lo.

Eines per perfilar

Per obtenir més informació sobre el rendiment del vostre codi, a més dels perfils integrats, podem utilitzar els paquets de perfils externs com cProfile, Pyflame o SnakeViz.

Resultats de la memòria cau

Si el nostre codi necessita realitzar càlculs costosos, podríem considerar la memòria cau dels resultats per estalviar temps.

Refactorització de codi

Refactoritzar el codi per facilitar-ne la lectura i el manteniment és de vegades una part necessària per optimitzar-lo. Un programa més ràpid també pot ser més net.

Utilitzeu la compilació just-in-time (JIT)

Biblioteques com PyPy o Numba poden proporcionar una compilació JIT que pot accelerar significativament certs tipus de codi Python.

Actualitza Python

Assegureu-vos que feu servir la darrera versió de Python, ja que les versions més noves sovint inclouen millores de rendiment.

Paral·lelisme i concurrència

Per als processos que es poden paral·lelitzar, investigueu les tècniques paral·leles i de sincronització com ara el multiprocessament, el threading o l'asyncio.

Recordeu que el benchmarking i l'elaboració de perfils haurien de ser els principals motors d'optimització. Concentreu-vos a millorar les àrees del nostre codi que tenen els efectes més significatius en el rendiment i comproveu constantment les vostres millores per assegurar-vos que tenen els efectes desitjats sense introduir més defectes.

Conclusió

En conclusió, l'optimització del codi Python és crucial per millorar el rendiment i l'eficàcia dels recursos. Els desenvolupadors poden augmentar molt la velocitat d'execució i la capacitat de resposta de les seves aplicacions Python mitjançant diverses tècniques, com ara seleccionar les estructures de dades adequades, aprofitar les funcions integrades, reduir els bucles addicionals i gestionar de manera eficaç la memòria. El benchmarking i el perfil continus haurien de dirigir els esforços d'optimització, assegurant que els avenços del codi coincideixen amb els requisits de rendiment del món real. Per garantir l'èxit del projecte a llarg termini i reduir la possibilitat d'introduir nous problemes, l'optimització del codi s'ha d'equilibrar constantment amb els objectius de llegibilitat i manteniment del codi.