Python: Функції як об'єкти
Що б зрозуміти що таке декоратори, для початку ви повинні зрозуміти, що функції в python - це об'єкти. Дане розуміння дуже важливо. Давайте розберемо це на простому прикладі:
Добре, тримайте цю інформацію в розумі, ми скоро повернемося до неї. Інша цікава властивість функцій в Python, це те, що вони можуть бути визначені ... всередині іншої функції!
Довідка за функціями
Добре, ви все ще тут? Тепер почнеться весела частина. Ви вже бачили, що функції це об'єкти і тому вони:
-Можуть бути присвоєні змінної;
-Можуть бути визначені всередині інших функцій.
У підсумку, це означає, що функції можуть повертати інші функції :-) [можливо тут подвійне значення 'return': 1) як оператор: 'можуть викликати оператор return для функцій', так і дію 'to return'. Сенс не змінюється абсолютно ніяк, мені просто здалося це забавним :-) прим. перекладача]
Давайте подивимося:
Але почекайте, це ще не все! Якщо ви можете повернути функцію, значить ви можете використовувати її як параметр:
Відмінно! Тепер ви знаєте все що потрібно, для того, щоб зрозуміти декоратори. Декоратори це обгортки, що означає,
що вони дозволяють виконувати код до і після виклику функції, яку вони обертають без необхідності змінювати саму функцію.
Декоратори своїми руками
Як написати декоратор самостійно:
Можливо тепер ви захочете, щоб кожен раз, коли ви викликаєте a_stand_alone_function, натомість неї викликалася функція a_stand_alone_function_decorated.
Це дуже легко! Просто переназначьте a_stand_alone_function функцією повертається my_shiny_new_decorator:
Розкриваємо таємницю декораторів!
Попередній приклад, використовуючи синтаксис декораторів:
Так, це дійсно настільки просо. decorator просто коротка форма запису цього:
Декоратори це просто Python варіант шаблону проектування decorator. Взагалі, для прості розробки в Python вбудовано кілька класичних шаблонів проектування, наприклад ітератори.
Звичайно ж ви можете поєднувати декоратори один з одним:
Теж саме, використовуючи синтаксис декораторів в Python:
Порядок в якому ви розставляєте декоратори МАЄ значення:
[Так як це переклад відповіді з stackoverflow, тут розташовувався відповідь на питання, він нічого нового не привносить і випадає з контексту, тому я вирішив його прибрати]
Тепер, ви можете просто піти щасливими або ще трохи напружити мізки і дізнатися як використовувати декоратори на більш високому рівні!
Передача аргументів на функцію-декоратор
Методи «декорування»
Що дійсно круто в Python, так це те, що методи і функції насправді одне і теж, з однією відмінністю,
що метод очікує перший параметр, який посилається на конкретний об'єкт (self).
Це означає, що ви можете зробити декоратор для методу тим же шляхом що й для функції, проcто тримайте в голові, що необхідно передати self:
Звичайно, якщо ви робите дуже універсальний декоратор і хочете що б він працював з будь-якою функцією або методом незалежно від їх аргументів, тоді просто використовуйте * args, ** kwargs:
Передаємо аргументи в декоратор
Чудово! Але що ви скажете щодо того, що б передати аргументи в сам декоратор? Ну звичайно нам доведеться трохи вивернутися, тому що декоратор повинен приймати функцію, як аргумент і з цієї причини, ви не зможете передати аргументи декоруємої функції безпосередньо в декоратор.
Перед тим як кинутися до вирішення завдання, давайте напишемо невелику напоминалку:
На ділі - це те ж саме. Просто викликається «my_decorator». Так що коли ви пишете @my_decorator, ви просто говорите Python викликати функцію певну змінної «my_decorator». Це дуже важливо, тому що мітка [найменування змінної] може безпосередньо вказувати на декоратор ... чи ні! Давайте зануримося в зло!
Тут ніяких сюрпризів. Давайте зробимо ТОЧНО таку ж штуку, але припустимо проміжні змінні:
Давайте зробимо це ЗНОВУ, ще коротше:
Гей, ви це бачили? Ми використовували виклик функції з "@" синтаксисом :-)
Повернемося назад до декораторам з аргументами. Якщо ми можемо використовувати функцію, щоб генерувати декоратор на льоту, ми можемо передати аргументи в цю функцію, вірно?
Ось він, декоратор з аргументами. Аргументи можуть бути призначені змінними:
Як ви бачите, ви можете передати аргументи декораторові як будь-який інший функції використовуючи цей трюк. Ви так само можете використовувати * args, ** kwargs, якщо захочете. Але пам'ятайте, декоратори викликаються тільки один раз. Тоді, коли Python імпортує скрипт. Ви не можете динамічно призначати аргументи після цього. Коли ви виконуєте «import x», функція вже декорована, так що ви нічого не зможете змінити.
Трохи практики: декоратор, для декорування декораторів.
І як бонус, я дам вам сніпет для створення будь-якого декоратора, що приймає будь-яку змінну. Зрештою, для того щоб приймати аргументи, ми створили декоратор використовує функцію. Ми обернули декоратор. Все що ми бачили останнім часом, просто загорнута функція? Ааа, декоратори! Давайте погуляємо і напишемо декоратор для декоратора:
Ми можемо використовувати цей код, як показано нижче:
Я здогадуюся, що останнім часом ви відчували теж почуття що і після фрази «Перед тим як зрозуміти рекурсію, потрібно зрозуміти рекурсію». Але з іншого боку, ви відчули задоволення від освоєння цього?
Кращі практики використання декораторів
- Їх додали в Python 2.4, так що переконаєтеся, що ваш код виконується.
- Декоратори уповільнюють виклик функції. Тримайте це в вмію
- Ви не зможете «раздекоріровать» функцію. Існують хакі, що б створити декоратори які можуть бути видалені, але ніхто не використовує їх. Так що як тільки функція задекорована, це назавжди. Протягом всього коду.
- Декоратори обертають функції, що може ускладнити їх дебаг.
Python 2.5 вирішує останню проблему, надаючи модуль functools, що включає functools.wraps який копіює ім'я, модуль і рядок документації будь-якої «оберненої» функції в її обгортку. Кумедний факт, functools.wraps - декоратор :-)
Як декоратори можуть бути корисні?
Нарешті головне питання: для чого я можу використовувати декоратори? Виглядає круто і потужно, але було б чудово побачити практичний приклад. Добре, існують тисячі можливостей. Класичне використання - розширення поведінки функції з зовнішньої бібліотеки (ви можете модифікувати її) або для цілей дебагінг (ви не захочете модифікувати її, тому що вона тимчасова). Ви можете використовувати декоратори для розширення декількох функцій з однаковим кодом НЕ переписуючи їх щоразу заново, заради DRY принципу. наприклад:
І, звичайно чудово, що ви можете використовувати декоратори для чого завгодно і коли завгодно без переписування. DRY в дії: