Hoe u uw Python-scripts kunt optimaliseren voor betere prestaties

Hoe U Uw Python Scripts Kunt Optimaliseren Voor Betere Prestaties



Het optimaliseren van de Python-scripts voor betere prestaties omvat het identificeren en aanpakken van de knelpunten in onze code, waardoor deze sneller en efficiënter werkt. Python is een populaire en krachtige programmeertaal die tegenwoordig in tal van toepassingen wordt gebruikt, waaronder data-analyse, ML-projecten (machine learning), webontwikkeling en nog veel meer. Optimalisatie van Python-code is een strategie om de snelheid en efficiëntie van het ontwikkelaarsprogramma te verbeteren bij het uitvoeren van activiteiten met minder regels code, minder geheugen of extra bronnen. Grote en inefficiënte code kan het programma vertragen, wat kan resulteren in een slechte klanttevredenheid en mogelijk financieel verlies, of de behoefte aan meer werk om het probleem op te lossen en op te lossen.

Het is noodzakelijk tijdens het uitvoeren van een taak waarvoor verschillende acties of gegevens moeten worden verwerkt. Daarom kan het uitschakelen en verbeteren van enkele ineffectieve codeblokken en functionaliteiten verbazingwekkende resultaten opleveren, zoals de volgende:

  1. Verbeter de prestaties van de applicatie
  2. Creëer leesbare en georganiseerde code
  3. Maak het monitoren van fouten en het opsporen van fouten eenvoudiger
  4. Bespaar aanzienlijke rekenkracht, enzovoort

Profileer uw code

Voordat we beginnen met optimaliseren, is het essentieel om de delen van de projectcode te identificeren die dit vertragen. De technieken voor profilering in Python omvatten de cProfile- en profile-pakketten. Gebruik dergelijke tools om te meten hoe snel bepaalde functies en coderegels worden uitgevoerd. De cProfile-module produceert een rapport waarin wordt aangegeven hoe lang het duurt voordat elke scriptfunctie wordt uitgevoerd. Dit rapport kan ons helpen functies te vinden die langzaam werken, zodat we deze kunnen verbeteren.







Codefragment:



importeren cProfiel als cP
zeker berekenSom ( invoerNummer ) :
som_van_invoergetallen = 0
terwijl invoerNummer > 0 :
som_van_invoergetallen + = invoerAantal % 10
invoernummer // = 10
afdrukken ( 'De som van alle cijfers in het invoernummer is: 'sum_of_input_numbers'' )
opbrengst som_van_invoergetallen
zeker hoofd_func ( ) :
cP. loop ( 'berekenSom(9876543789)' )
als __naam__ == '__voornaamst__' :
hoofd_func ( )

Het programma voert in totaal vijf functieaanroepen uit, zoals te zien is in de eerste regel van de uitvoer. De details van elke functieaanroep worden weergegeven in de volgende paar regels, inclusief het aantal keren dat de functie is aangeroepen, de totale tijdsduur in de functie, de tijdsduur per aanroep en de totale hoeveelheid tijd in de functie (inclusief alle functies die het wordt genoemd).



Bovendien drukt het programma een rapport af op het promptscherm waaruit blijkt dat het programma de uitvoeringstijd van al zijn taken binnen 0,000 seconden voltooit. Dit laat zien hoe snel het programma is.





Kies de juiste datastructuur

Prestatiekenmerken zijn afhankelijk van de datastructuur. Met name woordenboeken zijn sneller bij het opzoeken dan lijsten met betrekking tot algemene opslag. Selecteer de datastructuur die het meest geschikt is voor de bewerkingen die wij met uw gegevens zullen uitvoeren, als u die kent. Het volgende voorbeeld onderzoekt de effectiviteit van verschillende datastructuren voor een identiek proces om te bepalen of een element in de datastructuur aanwezig is.



We evalueren de tijd die nodig is om te controleren of een element aanwezig is in elke datastructuur (een lijst, een set en een woordenboek) en vergelijken deze.

OptimizeDataType.py:

importeren Keer dat ik als tt
importeren willekeurig als rndobj
# Genereer een lijst met gehele getallen
willekeurige_data_lijst = [ rndobj. randint ( 1 , 10000 ) voor _ in bereik ( 10000 ) ]
# Maak een set van dezelfde gegevens
willekeurige_data_set = set ( willekeurige_data_lijst )

# Maak een woordenboek met dezelfde gegevens als sleutels
obj_DataDictionary = { op een: Geen voor op een in willekeurige_data_lijst }

# Element waarnaar moet worden gezocht (bestaat in de gegevens)
willekeurig_getal_om_te_vinden = rndobj. keuze ( willekeurige_data_lijst )

# Meet de tijd om het lidmaatschap van een lijst te controleren
lijst_tijd = tt. Keer dat ik ( lambda : willekeurig_getal_naar_vinden in willekeurige_data_lijst , nummer = 1000 )

# Meet de tijd om het lidmaatschap van een set te controleren
tijd instellen = tt. Keer dat ik ( lambda : willekeurig_getal_naar_vinden in willekeurige_data_set , nummer = 1000 )

# Meet de tijd om het lidmaatschap in een woordenboek te controleren
dict_time = tt. Keer dat ik ( lambda : willekeurig_getal_naar_vinden in obj_DataDictionary , nummer = 1000 )

afdrukken ( F 'Lijstlidmaatschapscontroletijd: {list_time:.6f} seconden' )
afdrukken ( F 'Stel de controletijd van het lidmaatschap in: {set_time:.6f} seconden' )
afdrukken ( F 'Checktijd lidmaatschap woordenboek: {dict_time:.6f} seconden' )

Deze code vergelijkt de prestaties van lijsten, sets en woordenboeken bij het uitvoeren van lidmaatschapscontroles. Over het algemeen zijn sets en woordenboeken aanzienlijk sneller dan lijsten voor lidmaatschapstests, omdat ze op hash gebaseerde zoekopdrachten gebruiken en dus een gemiddelde tijdscomplexiteit van O(1) hebben. Lijsten daarentegen moeten lineair zoeken, wat resulteert in lidmaatschapstests met O(n) tijdscomplexiteit.

  Een schermafdruk van een computer. Beschrijving automatisch gegenereerd

Gebruik de ingebouwde functies in plaats van loops

Talrijke ingebouwde functies of methoden in Python kunnen worden gebruikt om typische taken uit te voeren, zoals filteren, sorteren en in kaart brengen. Het gebruik van deze routines in plaats van het maken van eigen lussen helpt de code te versnellen, omdat ze vaak prestatie-geoptimaliseerd zijn.

Laten we wat voorbeeldcode bouwen om de prestaties van het maken van aangepaste lussen te vergelijken door gebruik te maken van de ingebouwde functies voor typische taken (zoals map(), filter() en sorted()). We zullen evalueren hoe goed de verschillende mapping-, filtratie- en sorteermethoden presteren.

BuiltInFunctions.py:

importeren Keer dat ik als tt
# Voorbeeldlijst met getallenlijst
getallen_lijst = lijst ( bereik ( 1 , 10000 ) )

# Functie om getallenlijst te kwadrateren met behulp van een lus
zeker vierkante_met_loop ( getallen_lijst ) :
vierkant_resultaat = [ ]
voor op een in getallen_lijst:
vierkant_resultaat. toevoegen ( op een ** 2 )
opbrengst vierkant_resultaat
# Functie om even getallenlijst te filteren met behulp van een lus
zeker filter_even_using_loop ( getallen_lijst ) :
filter_resultaat = [ ]
voor op een in getallen_lijst:
als op een % 2 == 0 :
filter_resultaat. toevoegen ( op een )
opbrengst filter_resultaat
# Functie om getallenlijst te sorteren met behulp van een lus
zeker sort_using_loop ( getallen_lijst ) :
opbrengst gesorteerd ( getallen_lijst )
# Meet de tijd om getallen_lijst te kwadrateren met behulp van map()
kaart_tijd = tt. Keer dat ik ( lambda : lijst ( kaart ( lambda x: x** 2 , getallen_lijst ) ) , nummer = 1000 )
# Meet de tijd om even getallen_lijst te filteren met behulp van filter()
filtertijd = tt. Keer dat ik ( lambda : lijst ( filter ( lambda x: x% 2 == 0 , getallen_lijst ) ) , nummer = 1000 )
# Meet de tijd om getallen_lijst te sorteren met behulp van sort()
gesorteerde_tijd = tt. Keer dat ik ( lambda : gesorteerd ( getallen_lijst ) , nummer = 1000 )
# Meet de tijd tot het kwadrateren van getallen_lijst met behulp van een lus
loop_map_time = tt. Keer dat ik ( lambda : vierkante_met_loop ( getallen_lijst ) , nummer = 1000 )
# Meet de tijd om even getallenlijst te filteren met behulp van een lus
loop_filter_time = tt. Keer dat ik ( lambda : filter_even_using_loop ( getallen_lijst ) , nummer = 1000 )
# Meet de tijd om getallenlijst te sorteren met behulp van een lus
loop_gesorteerde_tijd = tt. Keer dat ik ( lambda : sort_using_loop ( getallen_lijst ) , nummer = 1000 )
afdrukken ( 'Nummerlijst bevat 10.000 elementen' )
afdrukken ( F 'Kaart() Tijd: {map_time:.6f} seconden' )
afdrukken ( F 'Filter() Tijd: {filter_time:.6f} seconden' )
afdrukken ( F 'Gesorteerde() Tijd: {gesorteerde_tijd:.6f} seconden' )
afdrukken ( F 'Loop (kaart) tijd: {loop_map_time:.6f} seconden' )
afdrukken ( F 'Loop (filter) tijd: {loop_filter_time:.6f} seconden' )
afdrukken ( F 'Loop (gesorteerde) tijd: {loop_sorted_time:.6f} seconden' )

We zullen waarschijnlijk zien dat de ingebouwde functies (map(), filter() en sort()) sneller zijn dan de aangepaste lussen voor deze algemene taken. De ingebouwde functies in Python bieden een beknoptere en begrijpelijkere aanpak om deze taken uit te voeren en zijn sterk geoptimaliseerd voor prestaties.

Optimaliseer de lussen

Als het schrijven van de lussen nodig is, zijn er een paar technieken die we kunnen gebruiken om ze te versnellen. Over het algemeen is de range()-lus sneller dan achteruit itereren. Dit komt omdat range() een iterator genereert zonder de lijst om te keren, wat een kostbare bewerking kan zijn voor lange lijsten. Omdat range() geen nieuwe lijst in het geheugen bouwt, gebruikt het bovendien minder geheugen.

OptimizeLoop.py:

importeren Keer dat ik als tt
# Voorbeeldlijst met getallenlijst
getallen_lijst = lijst ( bereik ( 1 , 100000 ) )
# Functie om de lijst in omgekeerde volgorde te doorlopen
zeker loop_reverse_iteratie ( ) :
resultaat_omgekeerd = [ ]
voor J in bereik ( alleen ( getallen_lijst ) - 1 , - 1 , - 1 ) :
resultaat_omgekeerd. toevoegen ( getallen_lijst [ J ] )
opbrengst resultaat_omgekeerd
# Functie om door de lijst te itereren met behulp van range()
zeker loop_range_iteration ( ) :
resultaat_bereik = [ ]
voor k in bereik ( alleen ( getallen_lijst ) ) :
resultaat_bereik. toevoegen ( getallen_lijst [ k ] )
opbrengst resultaat_bereik
# Meet de tijd die nodig is om omgekeerde iteratie uit te voeren
omgekeerde_tijd = tt. Keer dat ik ( loop_reverse_iteratie , nummer = 1000 )
# Meet de tijd die nodig is om bereikiteratie uit te voeren
bereik_tijd = tt. Keer dat ik ( loop_range_iteration , nummer = 1000 )
afdrukken ( 'De nummerlijst bevat 100.000 records' )
afdrukken ( F 'Omgekeerde iteratietijd: {reverse_time:.6f} seconden' )
afdrukken ( F 'Bereikiteratietijd: {range_time:.6f} seconden' )

Vermijd onnodige functieaanroepen

Elke keer dat een functie wordt aangeroepen, is er enige overhead. De code wordt sneller uitgevoerd als onnodige functieaanroepen worden vermeden. In plaats van bijvoorbeeld herhaaldelijk een functie uit te voeren die een waarde berekent, kunt u proberen het resultaat van de berekening op te slaan in een variabele en deze te gebruiken.

Hulpmiddelen voor profilering

Voor meer informatie over de prestaties van uw code kunnen we, naast de ingebouwde profilering, gebruik maken van externe profileringspakketten zoals cProfile, Pyflame of SnakeViz.

Resultaten cachen

Als onze code dure berekeningen moet uitvoeren, kunnen we overwegen om de resultaten in de cache op te slaan om tijd te besparen.

Code refactoring

Het herstructureren van de code om deze gemakkelijker te kunnen lezen en onderhouden, is soms een noodzakelijk onderdeel van het optimaliseren ervan. Een sneller programma kan ook schoner zijn.

Gebruik de Just-in-Time Compilatie (JIT)

Bibliotheken zoals PyPy of Numba kunnen een JIT-compilatie bieden die bepaalde soorten Python-code aanzienlijk kan versnellen.

Upgrade Python

Zorg ervoor dat u de nieuwste versie van Python gebruikt, aangezien nieuwere versies vaak prestatieverbeteringen bevatten.

Parallellisme en gelijktijdigheid

Voor processen die kunnen worden geparallelliseerd, onderzoekt u de parallelle en synchronisatietechnieken zoals multiprocessing, threading of asyncio.

Houd er rekening mee dat benchmarking en profilering de belangrijkste drijfveren voor optimalisatie moeten zijn. Concentreer u op het verbeteren van de gebieden van onze code die de grootste invloed hebben op de prestaties, en test voortdurend uw verbeteringen om er zeker van te zijn dat ze de gewenste effecten hebben zonder nog meer defecten te introduceren.

Conclusie

Concluderend: optimalisatie van Python-code is cruciaal voor verbeterde prestaties en resource-effectiviteit. Ontwikkelaars kunnen de uitvoeringssnelheid en het reactievermogen van hun Python-applicaties aanzienlijk verhogen met behulp van verschillende technieken, zoals het selecteren van de juiste datastructuren, het benutten van de ingebouwde functies, het verminderen van de extra lussen en het effectief beheren van het geheugen. Continue benchmarking en profilering moeten de optimalisatie-inspanningen sturen en ervoor zorgen dat de codeverbeteringen overeenkomen met de prestatie-eisen in de echte wereld. Om projectsucces op lange termijn te garanderen en de kans op het introduceren van nieuwe problemen te verkleinen, moet het optimaliseren van de code voortdurend in evenwicht worden gebracht met de doelstellingen van leesbaarheid en onderhoudbaarheid van de code.