Как да оптимизирате вашите Python скриптове за по-добра производителност

Kak Da Optimizirate Vasite Python Skriptove Za Po Dobra Proizvoditelnost



Оптимизирането на скриптовете на Python за по-добра производителност включва идентифициране и адресиране на тесните места в нашия код, което го кара да работи по-бързо и по-ефективно. Python е популярен и мощен език за програмиране, който се използва в много приложения в наши дни, включително анализ на данни, ML проекти (машинно обучение), уеб разработка и много други. Оптимизацията на кода на Python е стратегия за подобряване на скоростта и ефективността на програмата за разработчици при извършване на каквато и да е дейност, като се използват по-малко редове код, по-малко памет или допълнителни ресурси. Големият и неефективен код може да забави програмата, което може да доведе до ниска удовлетвореност на клиента и потенциална финансова загуба или нужда от повече работа за коригиране и отстраняване на неизправности.

Това е необходимо, докато изпълнявате задача, която изисква обработка на няколко действия или данни. Следователно изключването и подобряването на някои неефективни кодови блокове и функционалности може да има невероятни резултати като следните:

  1. Увеличете производителността на приложението
  2. Създайте четим и организиран код
  3. Улеснете мониторинга и отстраняването на грешки
  4. Запазете значителна изчислителна мощност и т.н

Профилирайте своя код

Преди да започнем да оптимизираме, важно е да идентифицираме частите от кода на проекта, които го забавят. Техниките за профилиране в Python включват пакетите cProfile и profile. Използвайте такива инструменти, за да прецените колко бързо се изпълняват определени функции и редове код. Модулът cProfile изготвя отчет, който описва подробно колко време отнема изпълнението на всяка скриптова функция. Този отчет може да ни помогне да намерим функции, които работят бавно, за да можем да ги подобрим.







Кодов фрагмент:



импортиране cProfile като cP
деф изчислиСума ( inputNumber ) :
сбор_на_въведени_числа = 0
докато inputNumber > 0 :
сума_на_въведени_числа + = inputNumber % 10
входно число // = 10
печат ( 'Сумата от всички цифри във въведеното число е: 'sum_of_input_numbers'' )
връщане сбор_на_въведени_числа
деф main_func ( ) :
cP. тичам ( 'calculateSum(9876543789)' )
ако __име__ == '__основен__' :
main_func ( )

Програмата прави общо пет извиквания на функции, както се вижда в първия ред на изхода. Подробностите за всяко извикване на функция са показани в следващите няколко реда, включително броя пъти, когато функцията е била извикана, общата продължителност на времето във функцията, продължителността на времето на повикване и общото време във функцията (включително всички функции, които се извикват).



Освен това програмата отпечатва отчет на екрана с подкана, който показва, че програмата завършва времето за изпълнение на всички свои задачи в рамките на 0,000 секунди. Това показва колко бърза е програмата.





Изберете правилната структура на данните

Характеристиките на производителността зависят от структурата на данните. По-специално, речниците са по-бързи за търсене, отколкото списъците по отношение на съхранението с общо предназначение. Изберете структурата на данните, която е най-подходяща за операциите, които ще извършим върху вашите данни, ако ги знаете. Следващият пример изследва ефективността на различни структури от данни за идентичен процес, за да определи дали даден елемент в структурата от данни присъства.



Ние оценяваме времето, необходимо за проверка дали даден елемент присъства във всяка структура от данни – списък, набор и речник – и ги сравняваме.

OptimizeDataType.py:

импортиране Тимей като tt
импортиране случаен като rndobj
# Генериране на списък от цели числа
списък с произволни_данни = [ rndobj. рандинт ( 1 , 10 000 ) за _ в диапазон ( 10 000 ) ]
# Създайте набор от същите данни
случаен_набор_данни = комплект ( списък с произволни_данни )

# Създайте речник със същите данни като ключовете
obj_DataDictionary = { никой: Нито един за никой в списък с произволни_данни }

# Елемент за търсене (съществува в данните)
произволно_число_за_намиране = rndobj. избор ( списък с произволни_данни )

# Измерете времето за проверка на членството в списък
list_time = tt. Тимей ( ламбда : произволно_число_за_намиране в списък с произволни_данни , номер = 1000 )

# Измерете времето за проверка на членството в набор
зададено_време = tt. Тимей ( ламбда : произволно_число_за_намиране в случаен_набор_данни , номер = 1000 )

# Измерете времето за проверка на членството в речник
dict_време = tt. Тимей ( ламбда : произволно_число_за_намиране в obj_DataDictionary , номер = 1000 )

печат ( f „Време за проверка на членството в списъка: {list_time:.6f} секунди“ )
печат ( f „Задаване на време за проверка на членството: {set_time:.6f} секунди“ )
печат ( f „Време за проверка на членството в речника: {dict_time:.6f} секунди“ )

Този код сравнява производителността на списъци, набори и речници при извършване на проверки за членство. Като цяло наборите и речниците са значително по-бързи от списъците за тестове за членство, тъй като използват търсения, базирани на хеш, така че имат средна времева сложност O(1). Списъците, от друга страна, трябва да извършват линейни търсения, което води до тестове за членство с O(n) времева сложност.

  Екранна снимка на автоматично генерирано описание на компютъра

Използвайте вградените функции вместо цикли

Множество вградени функции или методи в Python могат да се използват за извършване на типични задачи като филтриране, сортиране и картографиране. Използването на тези процедури, вместо създаването на собствени цикли, помага за ускоряване на кода, тъй като те често са оптимизирани за производителност.

Нека изградим примерен код, за да сравним производителността на създаването на потребителски цикли, като използваме вградените функции за типични задачи (като map(), filter() и sorted()). Ще оценим колко добре се представят различните методи за картографиране, филтриране и сортиране.

BuiltInFunctions.py:

импортиране Тимей като tt
# Примерен списък от numbers_list
списък_с_числа = списък ( диапазон ( 1 , 10 000 ) )

# Функция за квадратиране на numbers_list с помощта на цикъл
деф квадрат_с_примка ( списък_с_числа ) :
квадратен_резултат = [ ]
за никой в списък_с_числа:
квадратен_резултат. добавям ( никой ** 2 )
връщане квадратен_резултат
# Функция за филтриране на списък с четни числа с помощта на цикъл
деф filter_even_using_loop ( списък_с_числа ) :
филтър_резултат = [ ]
за никой в списък_с_числа:
ако никой % 2 == 0 :
филтър_резултат. добавям ( никой )
връщане филтър_резултат
# Функция за сортиране на numbers_list с помощта на цикъл
деф сортиране_с_цикъл ( списък_с_числа ) :
връщане сортирани ( списък_с_числа )
# Измерете времето за повдигане на квадрат numbers_list с помощта на map()
map_time = tt. Тимей ( ламбда : списък ( карта ( ламбда x: x ** 2 , списък_с_числа ) ) , номер = 1000 )
# Измерете времето за филтриране на дори numbers_list с помощта на filter()
филтър_време = tt. Тимей ( ламбда : списък ( филтър ( ламбда x: x % 2 == 0 , списък_с_числа ) ) , номер = 1000 )
# Измерете времето за сортиране на numbers_list с помощта на sorted()
сортирано_време = tt. Тимей ( ламбда : сортирани ( списък_с_числа ) , номер = 1000 )
# Измерете времето за повдигане на квадрат numbers_list с помощта на цикъл
loop_map_time = tt. Тимей ( ламбда : square_using_loop ( списък_с_числа ) , номер = 1000 )
# Измерете времето за филтриране на дори numbers_list с помощта на цикъл
loop_filter_time = tt. Тимей ( ламбда : filter_even_using_loop ( списък_с_числа ) , номер = 1000 )
# Измерете времето за сортиране на numbers_list с помощта на цикъл
loop_sorted_time = tt. Тимей ( ламбда : sort_using_loop ( списък_с_числа ) , номер = 1000 )
печат ( „Списъкът с числа съдържа 10 000 елемента“ )
печат ( f „Map() Време: {map_time:.6f} секунди“ )
печат ( f „Filter() Time: {filter_time:.6f} секунди“ )
печат ( f „Sorted() Time: {sorted_time:.6f} секунди“ )
печат ( f „Време за цикъл (карта): {loop_map_time:.6f} секунди“ )
печат ( f „Време за цикъл (филтър): {loop_filter_time:.6f} секунди“ )
печат ( f „Време за цикъл (сортирано): {loop_sorted_time:.6f} секунди“ )

Вероятно ще забележим, че вградените функции (map(), filter() и sorted()) са по-бързи от персонализираните цикли за тези общи задачи. Вградените функции в Python предлагат по-кратък и разбираем подход за изпълнение на тези задачи и са силно оптимизирани за производителност.

Оптимизирайте циклите

Ако писането на циклите е необходимо, има няколко техники, които можем да направим, за да ги ускорим. Като цяло цикълът range() е по-бърз от итерацията назад. Това е така, защото range() генерира итератор без да обръща списъка, което може да бъде скъпа операция за дълги списъци. Освен това, тъй като range() не създава нов списък в паметта, той използва по-малко памет.

OptimizeLoop.py:

импортиране Тимей като tt
# Примерен списък от numbers_list
списък_с_числа = списък ( диапазон ( 1 , 100 000 ) )
# Функция за итериране на списъка в обратен ред
деф loop_reverse_iteration ( ) :
обратен_резултат = [ ]
за й в диапазон ( само ( списък_с_числа ) - 1 , - 1 , - 1 ) :
обратен_резултат. добавям ( списък_с_числа [ й ] )
връщане обратен_резултат
# Функция за итерация в списъка с помощта на range()
деф цикъл_обхват_итерация ( ) :
резултат_диапазон = [ ]
за к в диапазон ( само ( списък_с_числа ) ) :
резултат_диапазон. добавям ( списък_с_числа [ к ] )
връщане резултат_диапазон
# Измерете времето, необходимо за извършване на обратна итерация
обратно_време = tt. Тимей ( loop_reverse_iteration , номер = 1000 )
# Измерете времето, необходимо за извършване на итерация на диапазона
диапазон_време = tt. Тимей ( цикъл_обхват_итерация , номер = 1000 )
печат ( „Списъкът с номера съдържа 100 000 записа“ )
печат ( f „Време за обратна итерация: {reverse_time:.6f} секунди“ )
печат ( f „Време на итерация на диапазон: {range_time:.6f} секунди“ )

Избягвайте ненужните извиквания на функции

Има известно натоварване при всяко извикване на функция. Кодът работи по-бързо, ако се избегнат ненужни извиквания на функции. Например, вместо многократно да изпълнявате функция, която изчислява стойност, опитайте да съхраните резултата от изчислението в променлива и да го използвате.

Инструменти за профилиране

За да научите повече за ефективността на вашия код, в допълнение към вграденото профилиране, можем да използваме външни пакети за профилиране като cProfile, Pyflame или SnakeViz.

Кеширане на резултатите

Ако нашият код трябва да извършва скъпи изчисления, може да обмислим кеширане на резултатите, за да спестим време.

Рефакторинг на код

Рефакторингът на кода, за да бъде по-лесен за четене и поддръжка, понякога е необходима част от оптимизирането му. По-бързата програма може да бъде и по-чиста.

Използвайте компилацията точно навреме (JIT)

Библиотеки като PyPy или Numba могат да предоставят JIT компилация, която може значително да ускори определени типове код на Python.

Надстройте Python

Уверете се, че използвате най-новата версия на Python, тъй като по-новите версии често включват подобрения в производителността.

Паралелизъм и паралелност

За процеси, които могат да бъдат паралелизирани, проучете техниките за паралел и синхронизация като многопроцесорна обработка, резба или асинхронизация.

Не забравяйте, че бенчмаркингът и профилирането трябва да бъдат основните двигатели на оптимизацията. Концентрирайте се върху подобряването на областите от нашия код, които имат най-значителен ефект върху производителността, и постоянно тествайте вашите подобрения, за да сте сигурни, че имат желаните ефекти, без да въвеждате повече дефекти.

Заключение

В заключение, оптимизирането на кода на Python е от решаващо значение за подобрена производителност и ефективност на ресурсите. Разработчиците могат значително да увеличат скоростта на изпълнение и отзивчивостта на своите приложения на Python, като използват различни техники като избор на подходящи структури от данни, използване на вградените функции, намаляване на допълнителните цикли и ефективно управление на паметта. Непрекъснатият бенчмаркинг и профилиране трябва да насочват усилията за оптимизация, като гарантират, че подобренията в кода отговарят на изискванията за производителност в реалния свят. За да се гарантира дългосрочен успех на проекта и да се намали вероятността от въвеждане на нови проблеми, оптимизирането на кода трябва постоянно да се балансира с целите за четливост и поддръжка на кода.