Робота за принципом єдиної відповідальності (SRP) в Python, коли дзвінки дорогі


12

Деякі базові моменти:

  • Виклики методу Python є "дорогими" через інтерпретований характер . Теоретично, якщо ваш код досить простий, руйнування коду Python має негативний вплив, крім читабельності та повторного використання ( що є великим виграшем для розробників, а не для користувачів ).
  • Принцип єдиної відповідальності (SRP) підтримує читабельність коду, його легше перевірити та підтримувати.
  • Проект має особливий фон, де ми хочемо читати код, тести та час роботи.

Наприклад, такий код, який викликає кілька методів (x4), повільніше, ніж наступний, який є лише одним.

from operator import add

class Vector:
    def __init__(self,list_of_3):
        self.coordinates = list_of_3

    def move(self,movement):
        self.coordinates = list( map(add, self.coordinates, movement))
        return self.coordinates

    def revert(self):
        self.coordinates = self.coordinates[::-1]
        return self.coordinates

    def get_coordinates(self):
        return self.coordinates

## Operation with one vector
vec3 = Vector([1,2,3])
vec3.move([1,1,1])
vec3.revert()
vec3.get_coordinates()

Порівняно з цим:

from operator import add

def move_and_revert_and_return(vector,movement):
    return list( map(add, vector, movement) )[::-1]

move_and_revert_and_return([1,2,3],[1,1,1])

Якщо я маю паралелізувати щось подібне, я досить об'єктивно втрачаю продуктивність. Розум - це лише приклад; в моєму проекті є кілька міні-процедур з математикою, такою як. - Хоча з цим набагато простіше працювати, але наші анкери не люблять це.


Як і де ми можемо сприйняти SRP, не порушуючи ефективність роботи в Python, оскільки його притаманна реалізація безпосередньо впливає на нього?

Чи є обхідні шляхи, як-то якийсь попередній процесор, який передає речі в оренду для випуску?

Або Python просто поганий при обробці розбиття коду?



19
Оскільки це варте, ваші два приклади коду не відрізняються за кількістю обов'язків. SRP не є методом підрахунку методу.
Роберт Харві

2
@RobertHarvey Ви маєте рацію, вибачте за поганий приклад, і я відредагую кращу, коли матиму час. У будь-якому випадку читабельність та ремонтопридатність страждають, і, зрештою, SRP виходить з ладу в кодовій базі, коли ми скорочуємо класи та їх методи.
lucasgcb

4
зауважте, що функціональні дзвінки коштують будь-якою мовою , хоча компілятори AOT мають розкішну форму
Eevee

6
Використовуйте JITted реалізацію python, наприклад PyPy. Слід в основному виправити цю проблему.
Бакуріу

Відповіді:


17

Python просто поганий при обробці розбиття коду?

На жаль, так, Python повільний, і існує багато анекдотів про те, що люди різко підвищують продуктивність, вбудовуючи функції та роблячи їх код некрасивим.

Є робота навколо Cython, яка є компільованою версією Python і набагато швидшою.

--За редагу я просто хотів звернутися до деяких коментарів та інших відповідей. Хоча поштовх з них не є, можливо, специфічним для пітона. але більш загальна оптимізація.

  1. Не оптимізуйте, поки у вас є проблеми, а потім шукайте вузькі місця

    Взагалі хороша порада. Але припущення полягає в тому, що "нормальний" код зазвичай виконується. Це не завжди так. Окремі мови та рамки мають свою власну ідентифікацію. У цьому випадку функція викликає.

  2. Це всього кілька мілісекунд, інші речі будуть повільнішими

    Якщо ви користуєтеся кодом на потужному настільному комп’ютері, вам, ймовірно, все одно, поки ваш єдиний код користувача виконається за кілька секунд.

    Але бізнес-код, як правило, працює для декількох користувачів і вимагає більше однієї машини для підтримки навантаження. Якщо ваш код працює вдвічі швидше, це означає, що ви можете мати вдвічі більше користувачів або половину кількості машин.

    Якщо ви володієте своїми машинами та центром обробки даних, то, як правило, у вас є великий набір витрат на процесор. Якщо ваш код працює трохи повільно, ви можете поглинути його, принаймні, поки вам не потрібно буде придбати другу машину.

    У ці дні хмарних обчислень, де ви використовуєте лише саме потрібну обчислювальну потужність, і не більше, існує пряма вартість за невикористовуваний код.

    Підвищення продуктивності може різко скоротити основні витрати на хмарний бізнес, а продуктивність дійсно повинна бути передньою та центральною.


1
У той час як відповідь Роберта допомагає висвітлити деякі підстави для можливих непорозумінь, пов'язаних з оптимізацією (що відповідає цьому питанню ), я вважаю, що це відповідає трохи більше безпосередньо та відповідно до контексту Python.
lucasgcb

2
вибачте його дещо короткий. У мене немає часу писати більше. Але я думаю, що Роберт помиляється на цьому. Найкращою порадою з python, здається, є профілювання під час кодування. Не припускайте, що вона буде ефективною та оптимізується лише у разі виявлення проблеми
Еван,

2
@Ewan: Вам не потрібно спочатку писати всю програму, щоб дотримуватися моїх порад. Метод або два більш ніж достатньо для отримання адекватного профілювання.
Роберт Харві

1
Ви також можете спробувати pypy, що є JITted python
Eevee

2
@Ewan Якщо ви дійсно переживаєте за ефективність накладних викликів функцій, все, що ви робите, ймовірно, не підходить для python. Але тоді я справді не можу придумати багато прикладів. Переважна більшість ділових кодів обмежена IO, а важкі речі процесора зазвичай обробляються за допомогою виклику до рідних бібліотек (numpy, tensorflow тощо).
Voo

50

Багато потенційних проблем щодо ефективності насправді не є проблемою на практиці. Питання, яке ви піднімаєте, може бути одним із них. У прямому розумінні ми називаємо турбуватися про ці проблеми без доказів того, що вони є фактичними проблемами передчасної оптимізації.

Якщо ви пишете фронтальний веб-сервіс, на його ефективність не впливатимуть значні виклики функцій, оскільки вартість передачі даних по мережі значно перевищує час, необхідний для здійснення виклику методу.

Якщо ви пишете щільну петлю, яка оновлює відеоекран шістдесят разів на секунду, це може мати значення. Але в цей момент я стверджую, що у вас виникають більші проблеми, якщо ви намагаєтесь використовувати для цього Python, робота, для якої Python, ймовірно, недостатньо підходить.

Як завжди, спосіб, який ви дізнаєтесь, - це вимірювання. Запустіть профайлер продуктивності або деякі таймери над своїм кодом. Подивіться, чи це реальна проблема на практиці.


Принцип єдиної відповідальності - це не закон чи мандат; це керівництво або принцип. Дизайн програмного забезпечення завжди стосується компромісів; немає абсолютів. Не рідкість торгувати читальністю та / або ремонтопридатністю для швидкості, тому вам, можливо, доведеться пожертвувати SRP на вівтарі виконання. Але не робіть цього компромісу, якщо не знаєте, що у вас є проблеми з продуктивністю.


3
Я думаю, що це було правдою, поки ми не винайшли хмарні обчислення. Зараз одна з двох функцій ефективно коштує в 4 рази дорожче, ніж інша
Еван

2
@Ewan 4 рази може не мати значення, поки ви не виміряєте, щоб це було достатньо значним. Якщо Foo займає 1 мс, а Бар займає 4 мс, це не добре. Поки ви не зрозумієте, що передача даних по мережі займає 200 мс. У той момент, коли бар повільніше не має значення. (Лише один можливий приклад того, що те, що X-х разів повільніше, не робить помітної чи вражаючої різниці, не означає, що це обов'язково супер реалістично.)
Becuzz

8
@Ewan Якщо скорочення законопроекту заощадить 15 доларів на місяць, але це потребує $ 125 / годину підрядника, щоб виправити та перевірити його, я можу легко виправдати, що не варто витрачати час на бізнес (або принаймні не робити правильно тепер якщо час на ринок має вирішальне значення тощо). Завжди є компроміси. І те, що має сенс в одній обставині, може не в іншій.
Becuzz

3
ваші рахунки AWS дійсно дуже низькі
Еван

6
@Ewan AWS кругляться до стелі партіями в будь-якому випадку (стандарт 100 мс). Що означає, що така оптимізація дозволяє вам економити що завгодно, якщо вона постійно уникає підштовхування вас до наступного фрагменту.
Delioth

2

По-перше, кілька уточнень: Python - це мова. Існує кілька різних перекладачів, які можуть виконувати код, написаний мовою Python. Референтна реалізація (CPython) - це зазвичай те, на що посилається, коли хтось говорить про "Python", як ніби це реалізація, але важливо бути точним, коли говорити про характеристики продуктивності, оскільки вони можуть різко відрізнятися між реалізаціями.

Як і де ми можемо сприйняти SRP, не порушуючи ефективність роботи в Python, оскільки його притаманна реалізація безпосередньо впливає на нього?

Випадок 1.) Якщо у вас чистий код Python (<= мова Python версії 3.5, 3.6 має "підтримку бета-рівня"), який покладається лише на чисті модулі Python, ви можете охопити SRP скрізь і використовувати PyPy для його запуску. PyPy ( https://morepypy.blogspot.com/2019/03/pypy-v71-released-now-uses-utf-8.html ) - інтерпретатор Python, який має компілятор Just in Time (JIT) і може видалити функцію виклик накладних витрат до тих пір, поки у нього є достатній час для «прогрівання», відстежуючи виконаний код (кілька секунд IIRC). **

Якщо ви обмежені у використанні інтерпретатора CPython, ви можете витягнути повільні функції в розширення, написані на C, які будуть попередньо складені і не страждатимуть від будь-яких накладних інтерпретаторів. Ви все ще можете використовувати SRP скрізь, але ваш код буде розділений між Python та C. Чи краще це чи гірше для ремонту, ніж вибіркове відмовлення від SRP, але дотримання лише коду Python, залежить від вашої команди, але якщо у вас є критичні показники продуктивності вашої коду, це, безумовно, буде швидше, ніж навіть найбільш оптимізований чистий код Python, інтерпретований CPython. Багато хто з найшвидших математичних бібліотек Python використовують цей метод (numpy та scipy IIRC). Що є приємним описом у справі 2 ...

Випадок 2.) Якщо у вас є код Python, який використовує розширення C (або покладається на бібліотеки, які використовують розширення C), PyPy може бути або не корисним залежно від того, як вони написані. Докладні відомості див. На веб-сторінці http://doc.pypy.org/en/latest/extending.html , але підсумок полягає в тому, що CFFI має мінімальні накладні витрати, тоді як CType повільніші (використання його з PyPy може бути навіть повільніше, ніж CPython)

Cython ( https://cython.org/ ) - це ще один варіант, з яким у мене не так багато досвіду. Я згадую це заради повноти, щоб моя відповідь могла "стояти самостійно", але не вимагати жодної експертизи. З мого обмеженого використання здавалося, що мені довелося більше працювати, щоб отримати ті ж покращення швидкості, що я можу отримати "безкоштовно" з PyPy, і якщо мені потрібно щось краще, ніж PyPy, було так само просто написати своє власне розширення C ( що має перевагу, якщо я повторно використовую код в іншому місці або витягаю його частину в бібліотеку, весь мій код все ще може працювати під будь-яким інтерпретатором Python і його не потрібно запускати Cython).

Мені страшно бути "замкненим" у Cython, тоді як будь-який код, написаний для PyPy, може працювати і під CPython.

** Деякі додаткові замітки про PyPy у виробництві

Будьте дуже обережні, щоб робити будь-які рішення, які мають практичний ефект "заблокувати вас" в PyPy у великій кодовій базі. Оскільки деякі (дуже популярні та корисні) сторонні бібліотеки не грають добре з причин, зазначених раніше, це може спричинити дуже важкі рішення пізніше, якщо ви зрозумієте, що вам потрібна одна з цих бібліотек. Мій досвід полягає насамперед у використанні PyPy для прискорення деяких (але не всіх) мікросервісів, які чутливі до продуктивності в середовищі компанії, де це додає незначної складності нашому виробничому середовищу (у нас уже розроблено кілька мов, деякі з різними основними версіями, як 2.7 проти 3.5 працює в будь-якому випадку).

Я виявив, що використання PyPy та CPython регулярно змушують мене писати код, який покладається лише на гарантії, надані самою мовною специфікацією, а не на деталі реалізації, які можуть змінюватися в будь-який час. Ви можете думати про такі деталі як додатковий тягар, але я вважаю це цінним у своєму професійному розвитку, і я думаю, що це "здорово" для екосистеми Python в цілому.


Ага! Я розглядав фокус на розширеннях для цього випадку, замість того, щоб відмовлятися від принципу і писати дикий код, інші відповіді створювали мені враження, що це буде повільно незалежно, якщо я не помінявся з опорного перекладача. Щоб зрозуміти, OOP все одно бути розумним на ваш погляд?
lucasgcb

1
у випадку 1 (другий абзац), чи не ви отримуєте те ж саме над головою, яка викликає функції, навіть якщо самі функції виконуються?
Еван

CPython - єдиний перекладач, який, як правило, сприймається серйозно. PyPy цікавий , але, звичайно, не спостерігається будь-якого поширеного прийняття. Крім того, його поведінка відрізняється від CPython, і вона не працює з деякими важливими пакетами, наприклад, scipy. Небагато розумних розробників рекомендували б PyPy для виробництва. Таким чином, відмінність між мовою та реалізацією на практиці є несуттєвою.
jpmc26

Я думаю, ти вдарив цвях по голові. Немає причини, щоб у вас не було кращого перекладача чи компілятора. Для питона як мови це не властиво. Ви просто
Еван

@ jpmc26 Я використовував PyPy у виробництві, і рекомендую розглянути це іншим досвідченим розробникам. Він чудово підходить для мікросервісів, що використовують falconframework.org для легких API відпочинку (як один приклад). Поведінка відрізняється тим, що розробники покладаються на деталі впровадження, які НЕ є гарантією мови, не є причиною не використовувати PyPy. Це привід переписати свій код. Цей же код може все-таки зламатися, якщо CPython внесе зміни в його реалізацію (що це вільно робити до тих пір, поки він все ще відповідає специфікації мови).
Стівен Джексон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.