Найти - Пользователи
Полная версия: Введение в декораторы для новичков
Начало » Python для новичков » Введение в декораторы для новичков
1
Vadim
Я не знаю какие на этом форуме правила и устои, но обилие тем типа “А как мне записать в файл а потом прочитать в другой кодировке” очень угнетает
Так как это раздел для новичков я решил выложить немножко интересных и даже захватывающих материалов, полезных в основном для людей мало знакомых с программированием но желающих научиться. Я пишу статью по программированию в первый раз и не претендую на точность изложения, однако некоторым моим знакомым эта статья помогла понять зачем им вообще использовать декораторы.

Декоратор - простой способ изменения поведения функции или класса. Это один из самых непонятных элементов языка Python, который на самом деле не отличается особой сложностью.

1 - Зачем нужны декораторы?
Декоратор это очень простой способ изменить поведение вашей функции или класса, с помощью них можно конструировать потоки, измерять время выполнений функции. Декоратор может стать оказаться инструментом при оптимизации программы, для синхронизации потоков, для проверки условий.

2 - Синтаксис

@*Function_Name(arguments)*
def *Other_Function_name*(*Some_arguments*) : *code*
это эквивалентно следующему коду

def *Other_Function_name*(*Some_arguments*) : *code*
*Function_Name* = *Other_Function_name*(*Function_Name*)
Простой пример, неожиданно возвращающий None

Причем выполнив код один раз, функции навсегда(Ну, по крайней мере, на время выполнения программы или переопределения функции) будет присвоено такое значение.

def empty(f):
return None

@empty
def function(x):
return x
Пример написания этого кода без декораторов
def empty(f):
return None

def function(x):
return x
function = empty(function)
print function
Этот код возвращает значение None.
И теперь каждый раз вызывая функцию function результатом будет None, если вы не переопределяете значение, например как в коде, указанном ниже.

def sqr(f): return 10

def function(func):

@func
def summ(x): return x-0.1*x

print summ
3 - Пример полезного использования декораторов
Типичный пример использования декораторов - функция засекающая время выполнения другой функции.

Эта функция будет получать декорируемую ею функцию и список её аргументов, после чего засекать время выполнения, очень похоже на использование тегов, то есть сначала записывается текущее время, затем выполняется функция а затем записывается разность между текущим временем и ранее записанным

import time
def Timer(f): # Функция таймер, в которую будет "обернута" будущая функция
def func(*args, **kwargs): # Функция которая будет выполнять переданную таймеру функцию и засекать время, требуется для того
# чтоб выполнялась функция с произвольными начальными данными
currtime = time.time() # Запомнить текущее время
result = f(*args'''Кортеж аргументов''', **kwargs'''Словарь аргументов''') #Выполнить введенную в таймер функцию
print "Time to function : %f" % (time.time()-currtime) # Вывести результат
return result

return func # Вернуть функцию

@Timer
def Euclid(x, y):
while x != y:
if x > y:
x = x - y
else:
y = y - x
return x

print Euclid(216,6)

# напечатает 'Время выполнения: *время выполнения вашей функции*'
Идея в том что функция Timer получает в качестве аргумента функцию f, кортеж и словарь её аргументов, после чего передает словарь и кортеж в функцию func, которая сначала замеряет текущее время, потом выполняет введенную функцию(сама функция в данном случае для функции func находится в глобальном пространстве имен) с исходными данными указанными в кортеже и словаре. То есть она создает новую функцию и эту функцию возвращает.

Но на выполнение функции Timer также требуется время, как гласит экспериментальная физика “Каждый наблюдаемый опыт не точен, вследствии влияния наблюдателя”

Можно написать код по-другому, учтя неточность.

import time

def Timer(f): # таймер выполнения
def func(*args, **kwargs): #Функция, позволяющая принимать произвольные аргументы
currtime = time.time() #Запись текущего времени
result = f(*args, **kwargs) #Выполнение функции заданной на вход
s = time.time()-currtime #Разность текущего времени и ранее записанного
return s #Вернуть время выполнения

return func

@Timer
def Euclid( x =12, y =144):
while x != y:
if x > y:
x = x - y
else:
y = y - x
return x
@Timer
def empty():return None

print '%1.15f' % (Euclid(5673, 100) - empty()) #Разность между временем выполнения пустой функции и функции Euclid
Как это можно было реализовать без декораторов

import time

def Timer(f):
def func(*args, **kwargs):
currtime = time.time()
result = f(*args, **kwargs)
s = time.time()-currtime
return s

return func
def Euclid( x =12, y =144):
while x != y:
if x > y:
x = x - y
else:
y = y - x
return x
def empty():return None
Euclid = Timer(Euclid)
empty = Timer(empty)
print '%1.15f' % (Euclid(5673, 100) - empty())
Если декораторы являются вложенными то они образуют очередь FIFO(First-Is First-Out) начиная с первого, то есть выполняюстя сверху вниз начиная с первого, идущего в тексте программы то есть

@n-th

@third
@second
@first

4 - Применение декораторов

Декораторам можно передавать параметры.
То есть декоратором будет являться результат выполнения введенной функции-декоратора.

@function(*value*)
def otherFunction : *code*

это тоже самое что выражение вида

def otherFunction : *code*
otherFunction = function(*value*)(otherFunction)
Дл примера приведу функцию возводящую число n в степень n

 
def state(t):
def Start(f):
def imfunc(*args, **kwargs):
result = 1
for i in xrange(t):
result = result*f(*args, **kwargs)
return result
return imfunc
return Start
def start():
while 1:
try:
m = int(raw_input('Input number :'))
break
except ValueError:
print 'This is not value'
@state(m)
def func(x):
return x

print func(m)
start()
Вызывая функцию state создается динамическая функция, которая вообщем-то и является самим декоратором

5 - О классах

имеется 2 метода - staticmethod и classicmethod
О них замечательно рассказано на хабре, я просто процитирую.
class TestClass(object):
@classmethod
def f1(cls):
print cls.__name__

@staticmethod
def f2():
pass

class TestClass2(TestClass):
pass

TestClass.f1() # печатает TestClass
TestClass2.f1() # печатает TestClass2

a = TestClass2()
a.f1() # печатает TestClass2
Статический метод (обёрнутый декоратором staticmethod) в принципе соответствует статическим методам в C++ или Java. А вот метод класса — это нечто более интересное. Первым аргументом такой метод получает класс (не экземпляр!), это происходит примерно так же, как с обычными методами, которые первым аргументом получают референс на экземпляр класса. В случае, когда метод класса вызывается на инстансе, первым параметром передаётся актуальный класс инстанса, это видно на примере выше: для порождённого класса передаётся именно порождённый класс.
Надеюсь это хоть кому-нибудь поможет понять как использовать декораторы.
Андрей Светлов
Если декораторы являются вложенными то они образуют очередь FIFO(First-Is First-Out) начиная с первого, то есть выполняюстя сверху вниз начиная с первого, идущего в тексте программы то есть

@first
@sedond
@third

@n'th
Наоборот. Первым будет применен последний декоратор
regall
Vadim, не надо такое постить на форум - пишите в вики, раз уж хотите сделать доброе дело )
Андрей Светлов, там в предложении противоречие - сначала говорится о FIFO, что верно, а потом наоборот ))
Андрей Светлов
Тогда уж на форуме стоит выложить ссылку. А то я вики не читаю и за обновлениями не слежу
tmt
Отличное, если не самое лучшее, пособие для новичков про декораторы (правдна на буржуйском =) ) - это книга http://www.mindviewinc.com/Books/Python3Patterns/Index.php
Там есть целая глава по ним. Очень хорошо и доступно описано.
regall
Андрей Светлов, а ты что не разбираешся в декораторах? =) Нам свыше даны поисковые системы. Кто ищет, тот найдет, форум для того и есть, чтоб спрашивать. А насчет ссылки - это хорошо, но только скорее во флейм, чтоб не гадить по разделам, и кто захочет - просмотрит и поправит, как в этом случае, а на вики любой сможет исправить неточность …
Андрей Светлов
Я-то много в чем разбираюсь довольно неплохо. Но, тем не менее, взял за правило почитывать - кто что новенькое пишет. Иногда бывает полезно.
Zubchick
Я тоже всегда читаю кто-что пишет по питону, повторение мать учения, да и чего-нить новенькое встретить можно. А по ссылкам хожу редко почему-то…
regall
Андрей Светлов
Я-то много в чем разбираюсь довольно неплохо. Но, тем не менее, взял за правило почитывать - кто что новенькое пишет. Иногда бывает полезно.
Zubchick
Я тоже всегда читаю кто-что пишет по питону, повторение мать учения, да и чего-нить новенькое встретить можно. А по ссылкам хожу редко почему-то…
Я ни в коем случае не возражаю, я возражаю против того, чтобы постить все подряд в форум. Статья в вики + ссылочка в Флейм с описанием. Кому интересно - заглянет.
Vadim
Спасибо, исправил.

По поводу википедии.
Хорошо, я не буду выкладывать на форум.
Но просто лично сначала проще прочитать очень упрощенное описание а затем уже читать что-то что должно дать мне реальное представление об этом, вот я и решил выложить упрощенное описание. Вдруг кто-то не зная что такое декораторы увидит это и заинтересуется.

Если вы о http://wiki.python.su/ то я незнаю как туда что-либо выложить.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB