Новини високих технологій
» » Python-декоратори - це що таке?

Python-декоратори - це що таке?

6-05-2018, 22:07
1 353
Декоратор - шаблон, призначений для підключення додаткового поведінки до об'єкта. Використовується в багатьох мовах ООП: Java, C#, PHP, JS. Python - не виняток. Завдання декораторів можна описати на наступному прикладі. Є об'єкт, який виконує деяку функцію, в ході розробки програми потрібно додати до нього деяку додаткову функціональність. Вона може виконуватися до або після, або навіть під час основних функцій. Для вирішення цього завдання використовуються декоратори. Вони розширюють функціональність, позбавляють від створення другого такого ж класу з додатковими методами. Декоратори Python - це своєрідна обгортка, яка змінює поведінку функції. Як змінюваного об'єкта використовується клас, функція або інший декоратор. Вони повинні застосовуватися дуже обережно з чітким розумінням того, чого саме потрібно домогтися. Занадто часте використання декораторів призводить до ускладнення розуміння коду.


Декоратор і функція

Декоратори в Python - це функція, яка використовує іншу функцію як аргумент. Являє собою блок коду, який повертає деяке значення.
Містить аргументи, вони будуть використовуватися в подальшому вплинуть на значення, що повертається. Результат, який виходить на виході, може бути будь-якого типу: список, кортеж, функція. У "Пайтоне" кожна функція - об'єкт, оголошується він за допомогою ключового слова def. Область значень задається не фігурними дужками, а позначається відступом tab. Після ключового слова вказується ім'я, аргументи задаються в дужках () після імені. Перед переходом на новий рядок ставиться символ ":". У "Пайтоне" тіло не може бути порожнім, повинно обов'язково містити список команд. Якщо потрібно залишити це місце ставиться порожній оператор pass.

def empty_func():

pass

Подібний синтаксис поширюється на всі функції, крім анонімною. Анонімна виглядає так:

  func = lambda x, y: x + y  

Виклик:

  func(1 2) #повертає 3 

Виклик (другий спосіб):

  (lambda x, y: x + y)(1 2) #повертає 3  

Декоратори викликаються так:

  @ім'я декоратора 
def изменяемая_функция
тіло изменяемой_функции

Схема роботи описується наступним кодом:

  def decorator(change_funct): 
def return_func1():
print "code before"
change_funct()
print "code after"
return return_func1

Відповідно, виклик відбувається наступним чином:

<script async="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js">
<!-- fb_336x280_2 -->

<script> (adsbygoogle = window.adsbygoogle ||[]).push({});
  @decorator 
def retrurn_func1():
print new_change

Аргументи функції

В аргументах декораторів Python передається будь-який тип даних.

Змінні одного типу перераховуються через кому. Є кілька способів того, як призначаються значення змінним, зазначені у параметрах.

  1. Звичайний.
  2. За допомогою ключових слів.
  3. Завдання статичних значень.
  4. Використання позиційних елементів.

При створенні вказується кілька аргументів в певному порядку. При виклику у параметрах вказуються всі значення у відповідному порядку.

  def bigger(a,b): 
if a > b:
print a
else:
print b

Правильний виклик:

  bigger(56)  

Неправильний виклик:

  bigger(3) 
bigger(1273)

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

  def person(name, age): 
print name, "is", "age", "years old"
person(age=23 name="John")

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


<script type="text/jаvascript">
var blockSettings2 = {blockId:"R-A-70350-39",renderTo:"yandex_rtb_R-A-70350-39",async:!0};
if(document.cookie.indexOf("abmatch=") >= 0) blockSettings2.statId = 70350;
!function(a,b,c,d,e){a[c]=a[c]||[],a[c].push(function(){Ya.Context.AdvManager.render(blockSettings2)}),e=b.getElementsByTagName("script")[0],d=b.createElement("script"),d.type="text/jаvascript",d.src="//an.yandex.ru/system/context.js",d.async=!0e.parentNode.insertBefore(d,e)}(this,this.document,"yandexContextAsyncCallbacks");
  def space(planet_name, center="Star"): 
print(planet_name, "is orbiting a", center)
space("Mars")

Коли кількість аргументів на етапі створення функції невідомо, використовуються аргументи. Вони можуть позначати кілька змінних одного типу або список:

  def func(*args): 
return args
func(123 'abc')
# (123 'abc')
func(1)
#(1)

Схожим способом передаються бібліотеки значень з ключами - з допомогою символу "**".

Змінні, зазначені в тілі, є локальними, використовуються безпосередньо самою функцією. Для створення глобальної змінної застосовується спецификатор global.

  def get_older(): 
global age
age += 1

Підтримується рекурсія.

  def fact(num): 
if num == 0:
return 1
else:
return num * fact(num - 1)

Декорування методів

Функції і методи синтаксично схожі.

Різниця в тому, що функція викликається тільки по імені.

  func()  

А виклик методу здійснюється через оператора "." і вводиться ім'я методу, де першим параметром є батько.

  object.func()  

Таким чином, декоратори Python для методів створюються так само, як для функції.

Тут ми створили декоратор def method_friendly_decorator(method_to_decorate):

  def wrapper(self, lie): 
lie = lie - 3 #
return method_to_decorate(self, lie)
return wrapper

F тут створили клас з методами, які пізніше будуть модифіковані^

  class Lucy(object): 
def __init__(self):
self.age = 32

@method_friendly_decorator
def sayYourAge(self, lie):
print "Мені %s, а ти скільки дав?" % (self.age + lie)

Lucy.sayYourAge(-32)
#Мені 26 а ти скільки дав?

Тут використаний format(). Призначається для форматування рядків, використовується в такому вигляді:


<script type="text/jаvascript">
var blockSettings3 = {blockId:"R-A-70350-44",renderTo:"yandex_rtb_R-A-70350-44",async:!0};
if(document.cookie.indexOf("abmatch=") >= 0) blockSettings3.statId = 70350;
!function(a,b,c,d,e){a[c]=a[c]||[],a[c].push(function(){Ya.Context.AdvManager.render(blockSettings3)}),e=b.getElementsByTagName("script")[0],d=b.createElement("script"),d.type="text/jаvascript",d.src="//an.yandex.ru/system/context.js",d.async=!0e.parentNode.insertBefore(d,e)}(this,this.document,"yandexContextAsyncCallbacks");
  print("string {} {}").format(1 2) 
#string 1 2

В цьому прикладі в аргументах format() вказані дві цифри: 1 2. Вони заміщають символи {} в такому порядку, в якому розташовані. Форматування доступно виключно для рядкових елементів. Тобто, аргумент стає на перше місце фігурних дужок {}, а другий - друге, відповідно. У методі format передбачена можливість змінювати порядок вставки значень. Це робиться через індекси.

Якщо:

  print("string {} {}").format(1 2) 
#string 1 2

: R3r3r3701.

  print("string {1} {0}").format(1 2) 
#string 2 1

Допускається форматування рядків через ключові імена в форматі format(arg1 = value1 arg2 = value2).

  print("string {arg1} {arg2}").format(arg1 = 1 arg2 = 2) 
#string 1 2

Можна використовувати змішану систему - коли при двох аргументах тільки один з них має статичне значення. Для передачі значень указується індекс і ім'я змінної.

<script async="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js">

<script> (adsbygoogle = window.adsbygoogle ||[]).push({});
  print("string {arg1} {1}").format(arg1 = 1 2) 
#string 1 2

Декоратори з аргументами

Декораторам Python можна передати аргументи, які згодом модифікують оброблювану функцію.

  def decorator(function_to_decorate): 
def function(arg1 arg2):
print "Дивись, що я отримав:", arg1 arg2
return function

В даному випадку є @decorator, який модифікує function. Аргументи пробрасываются у другому рядку, передаються змінною function_to_decorate.

Виклик:

  @decorator 
def real_func(Петро Іванович)
print "Мене звати", arg1 arg2

На екрані з'явиться:

  Дивись, що я отримав: Петро Іванович 

Мене звати Петро Іванович

Вкладені декоратори

Коли не достатньо одного декоратора, реалізовується кілька рівнів обгортання. При створенні вкладеного декоратора кожен починається з нового рядка, кількість рядків визначає рівень складності. Виглядає так:

  @AAA 
@BBB
@CCC
def function():
pass:

Відповідно, AAA(), приймає в параметрах BBB(), а вона, обробляє CCC().

  def f(): 
pass:

f = (AAA BBB(CCC(function))):

Function передається трьома різними декоратором, присвоюється f(). Кожен з них повертає свій результат, який, у свою чергу, обробляє обгортку. Можна помітити, що останній декоратор списку є першим, він починає обробляти function().

В Python декоратори класу виглядають також.

  @firsdecorator 

@seconddecorator

class CDC:

pass:

C = firstdecorator(seconddecorator(CDC))

XX = C()

def fn1(arg): return lambda: 'XX' + arg()

def fn2(arg): return lambda: 'YY' + arg()

def fn3(arg): return lambda: 'ZZ' + arg()

@fn1

@fn2

@fn3

def myfunc(): # myfunc = fn1(fn2(fn3(myfunc)))

return 'Python'

print(myfunc()) # Виведе "XXYYZZPython"

В даному випадку реалізація обгортають логіки відбувається шляхом використання def lambda:


<script type="text/jаvascript">
var blockSettings = {blockId:"R-A-70350-45",renderTo:"yandex_rtb_R-A-70350-45",async:!0};
if(document.cookie.indexOf("abmatch=") >= 0) blockSettings.statId = 70350;
!function(a,b,c,d,e){a[c]=a[c]||[],a[c].push(function(){Ya.Context.AdvManager.render(blockSettings)}),e=b.getElementsByTagName("script")[0],d=b.createElement("script"),d.type="text/jаvascript",d.src="//an.yandex.ru/system/context.js",d.async=!0e.parentNode.insertBefore(d,e)}(this,this.document,"yandexContextAsyncCallbacks");
  lambda: 'XX' + arg()  

Fn3() обертає myfunc, повертає ZZPython замість попереднього рядка Python. Потім починає працювати реверсивна fn2(), яка в підсумку повертає результат YYZZPython. В кінці fn1() обробляє myfunc() і повертає кінцевий результат - рядок XXYYZZPython.

Вбудовані декоратори

Існують вбудовані декоратори функцій Python. Вони поставляються в комплекті з інтерпретатором, для їх використання потрібно імпортувати додаткові модулі.

Staticmethod обробляє функцію-аргумент так, що вона стає статичної та приймає спецификатор static.

  class C: 

@staticmethod

def f(arg1 arg2 ): #static

pass

Classmethod робить з оброблюваної функції клас.

  class MyClass: 

@classmethod

def method(cls, arg):

print('%s classmethod. %d' % (cls.__name__, arg))

@classmethod

def call_original_method(cls):

cls.method(5)

def call_class_method(self):

self.method(10)

class MySubclass(MyClass):

@classmethod

def call_original_method(cls):

cls.method(6)


MyClass.method(0) # MyClass classmethod. 0

MyClass.call_original_method() # MyClass classmethod. 5

MySubclass.method(0) # MySubclass classmethod. 0

MySubclass.call_original_method() # MySubclass classmethod. 6

# Викликаємо методи класу через об'єкт.

my_obj = MyClass()

my_obj.method(1)

my_obj.call_class_method()

Будь-яка функція може використовуватися як декоратор.

Заміна сеттерів і геттеров

З допомогою класу property призначаються геттери, сетери, делеттеры.

  class property([fget[, fset[, fdel[, doc]]]])  

Геттери і сетери в TypeScript представлені в такому вигляді:

Здійснюється передача параметрів в клас Python. Декоратор property має методи:

  • fget - отримує значення атрибута;
  • fset визначає значення атрибута;
  • fdel видаляє;
  • doc створює опис атрибута. Якщо doc не призначений, повертається копія опис fget(), якщо є.
  class C(object): 

def __init__(self):

self._x = None

def getx(self):

return self._x

def setx(self, value):

self._x = value

def delx(self):

del self._x

x = property(getx, setx, delx, "i'm the 'x' property.")

Використання функції як декоратор Pythonproperty:

  class C(object): 

def __init__(self):

self._x = None

@property

def x(self):

"""I'm the 'x' property."""

return self._x

@x.setter

def x(self, value):

self._x = value

@x.deleter

def x(self):

del self._x

Property створив функції x декоратор. Оскільки у всіх декораторів є вбудовані методи setter, getter, deletter, можна викликати один з них.

Особливості

При роботі з декоратором необхідно враховувати деякі особливості:

  1. Використання декораторів трохи уповільнює виклик функції.
  2. Один раз задекорована функція не може бути раздекорированной. Існують способи обходу цього правила. Можна створити такий декоратор, який згодом можна буде від'єднати від функції. Але це не дуже вдала практика.
  3. З-за того, що декоратор обертає функцію, може ускладнитися налагодження. Проблема вирішується за допомогою модуля functools.

Модуль functools - збірник методів, завдяки яким забезпечується взаємодія з іншими функціями, він також є декоратором Python.

Корисний метод cmp_to_key(func) перетворює cmp() key(). Обидва методи призначені для сортування списку, але перший вилучений у "Пайтон 3.0", а другий доданий у версії 2. Lru_cache зберігає останні дзвінки в кеш. Якщо maxsixe вказаний як none, розмір кешу зростає нескінченно. Для зберігання часто використовуваних запитів використовується словник. Якщо аргумент typed=true, то аргументи різних типів кешується окремо. Відповідно, при typed=true, зберігаються в єдиний список.

Total_ordering декорують клас, який містить методи порівняння, і додає всі інші.

Partial(func, *args, **keywords) повертає функцію, яка викликається по першому аргументу з області параметрів методу, передає позиційний *args, який задається другим, і іменований kwargs.

Reduce працює так:

  reduce(lambda x, y: x+y,[1, 2, 3, 4, 5])  

Еквівалентно:

  ((((1+2)+3)+4)+5)  

Reduce застосовує зазначену функцію послідовно пар елементів, зазначених у списку **keywords, або до всіх елементів *args. Таким чином, у наведеному вище прикладі з допомогою lambda function обробляються перші два елементи:

  1+2  

Далі результат підсумовується з третім, отриманий з цього результат додається до наступними елементами і т. д.

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

  assigned=WRAPPER_ASSIGNMENTS  

Кортеж WRAPPER_ASSIGNMENTS містить за замовчуванням значення __name__, __module__, __annotations__ і __doc__.

  updated=WRAPPER_UPDATES  

У WRAPPER_UPDATES зазначаються атрибути, які оновлюються, за замовчуванням це __dict__.

Wraps викликає partial в якості декоратора.

Декоратор обробки помилки

Можливості декораторів дозволяють створювати таку функцію, яка при виникненні помилки видає один результат, якщо помилки немає - на іншу.

Реалізація:

  import functools def retry(func): @functools.wraps(func) def wrapper(*args, **kwargs): while True: try: return func(*args, **kwargs) except Exception: pass return wrapper  

Видно, що в разі винятків функція запускається заново.

  @retry def do_something_unreliable(): if random.randint(010) > 1: raise IOError("Broken sauce, everything is hosed!!!111one") else: return "Awesome sauce!" print(do_something_unreliable())  

Цей код означає, що 9 з 11 випадків виникне помилка.

  def my_decorator(fn): def wrapped(): try: return fn() except as Exception e: print("Error:", e) return wrapped @my_decorator def my_func(): import random while True: if random.randint(0 4) == 0: raise Exception('Random!') print('Ok') my_func()  

Тут в консоль виведеться:

  Ok Ok Ok Error: Random!  

Створення свого декоратора

Декоратор являє собою функцію, всередині якої знаходиться інша функція з відповідними методами.

Приклад декоратора в Python:

  def my_shiny_new_decorator(function_to_decorate): 

def the_wrapper_around_the_original_function():

print("Я - код, який відпрацює до виклику функції")

function_to_decorate()

print("А я - код, що спрацьовує після")

return the_wrapper_around_the_original_function

У цьому коді декоратором є my_shiny_new_decorator(). Надалі декорує function_to_decorate() з області параметрів. the_wrapper_around_the_original_function - це приклад того, як буде оброблена декорируемая функція. В даному випадку додається:

  print("Я - код, який відпрацює до виклику функції") 

print("А я - код, що спрацьовує після")

My_shiny_new_decorator() повертає декоруєму the_wrapper_around_the_original_function().

Щоб відпрацювати яку-небудь функцію, вона обертається декоратором.

  stand_alone_function = my_shiny_new_decorator(stand_alone_function)  

В даному випадку декорована функція - stand_alone_function, декоратор - це my_shiny_new_decorator. Значення присвоюється змінній stand_alone_function.

  def stand_alone_function(): 

print("Я проста самотня функція, адже ти не посмеешь мене змінювати?")

stand_alone_function()
Я - код, який відпрацює до виклику функції

Я проста самотня функція, адже ти не посмеешь мене змінювати?

А я - код, що спрацьовує після


Таким чином, видно, що stand_alone_function, яка виводила на екран одне речення, тепер виводить три пропозиції. Це здійснюється з допомогою декоратора.
Цікаво по темі
Оператори Python і їх пріоритет виконання
Оператори Python і їх пріоритет виконання
Оператори описують дія над змінними - операндами. Визначають порядок виконання операцій. Операції з високим пріоритетом виконуються першими. Крім
Метод appendTo в jQuery: вставка елементів
Метод appendTo в jQuery: вставка елементів
Синтаксис і особливості використання методу appendTo в бібліотеці jQuery. Контекст виклику, вхідні параметри і значення, що повертається. Специфіка
Функція Random C++
Функція Random C++
Починаючи з версії C++11 у стандарті мови були значно розширені засоби для роботи з випадковими числами (далі, СЧ). СЧ використовуються в багатьох
Перевантаження операторів в С++: основи, приклади
Перевантаження операторів в С++: основи, приклади
У будь-якій науці є стандартні позначення, які полегшують розуміння ідей. Наприклад, в математиці це множення, ділення, додавання і інші символьні
Що таке динамічні масиви C++?
Що таке динамічні масиви C++?
Визначимося для початку з тим, що таке динамічний масив. З часів Сі існують масиви, але їх особливістю був фіксований розмір, який зазначався при
Як здійснити запис у файл Java
Як здійснити запис у файл Java
При написанні програм на мові Java рано чи пізно постане необхідність читання і запису інформації в файл. Для цього в мові передбачені наступні