Уведомления

Группа в Telegram: @pythonsu

#1 Окт. 28, 2010 17:18:54

shupg
От:
Зарегистрирован: 2009-08-07
Сообщения: 25
Репутация: +  0  -
Профиль   Отправить e-mail  

Как применить декоратор ко всем пользовательским методам

Есть стремление не приписывать декоратор к каждому методу класса, а просто сказать, что все функции этого класса должны перед вызовом сделать то-то и то-то. Например, напечатать, “сейчас работает функция f(a=1,b=2,c=3)”

Представляю себе код примерно так:

import types

class ClassWithLoggedFunctions(type):
def __call__(self, *args, **kargs):
self.instance = super(ClassWithLoggedFunctions, self).__call__(*args,**kargs)
def __call__(self, *args, **kargs):
print 'execution %s(%s, %s)' % (func, args, kargs)
func.__call__(self, *args, **kargs)
for i in dir(self.instance):
func = getattr(self.instance, i)
if isinstance(func, types.MethodType):
pass # magic here!
return self.instance

class C():
__metaclass__ = ClassWithLoggedFunctions
def f(self, a, b, c):
return (a,b,c)

C().f(1,2,3)
-> Starts f(a=1,b=2,c=3) executing…

Есть идеи?



Офлайн

#2 Окт. 28, 2010 18:28:07

poltergeist
От:
Зарегистрирован: 2007-02-28
Сообщения: 522
Репутация: +  0  -
Профиль   Отправить e-mail  

Как применить декоратор ко всем пользовательским методам

Может лучше воспользоваться правильным логированием и писать сообщения для дебага приложения в самих методах класса?

import logging

logger = logging.getLogger(__name__)

class C(object):

def f(self, a, b, c):
logger.debug('f called with %r, %r, %r' % (a, b, c))
# ... process
logger.debug('f done')
Во время разработки эти сообщения будут видны, а на продакшене они в лог не попадут (это всё потом можно будет сконфигурировать).



Офлайн

#3 Окт. 28, 2010 18:29:47

ZZZ
От: Москва
Зарегистрирован: 2008-04-03
Сообщения: 2161
Репутация: +  26  -
Профиль   Адрес электронной почты  

Как применить декоратор ко всем пользовательским методам

Я бы подошёл так:

class MetaProperty(type):
def __init__(cls, name, base, cls_dict):
for attr, value in cls_dict.items():
if callable(value):
setattr(cls, attr, property(value))

class StrangeObject(object):
__metaclass__ = MetaProperty

def a(self):
return 'a'

so = StarangeObject()
assert so.a == 'a'
Не проверял, но моя мысль, думаю, ясна.



Офлайн

#4 Окт. 28, 2010 21:28:11

Ed
От:
Зарегистрирован: 2008-12-13
Сообщения: 1032
Репутация: +  13  -
Профиль   Отправить e-mail  

Как применить декоратор ко всем пользовательским методам

А просто в __getattribute__ заврапить то, что является callable не пойдет?



Офлайн

#5 Окт. 28, 2010 23:15:50

shupg
От:
Зарегистрирован: 2009-08-07
Сообщения: 25
Репутация: +  0  -
Профиль   Отправить e-mail  

Как применить декоратор ко всем пользовательским методам

Перемудрил я! Спасибо ZZZ!

Сделал так:

import types

def decorator(f):
def func(*args, **kargs):
print 'executing %s...' % f.__name__
return f(*args, **kargs)
return func


class LoggedClass(type):
def __call__(cls, *args, **kargs):
def func(f):
print f.__name__
return f

for attr in dir(cls):
value = getattr(cls, attr)
if isinstance(value, types.MethodType):
setattr(cls, attr, decorator(value))
return type.__call__(cls, *args, **kargs)

class CustomClass(object):
__metaclass__ = LoggedClass

def some_function(self):
return 'a'

c = CustomClass()
print c.some_function()



Офлайн

#6 Апрель 18, 2011 07:27:31

kl
От:
Зарегистрирован: 2011-04-18
Сообщения: 6
Репутация: +  0  -
Профиль   Отправить e-mail  

Как применить декоратор ко всем пользовательским методам

спасибо за классный рабочий пример!
только вот функция func, которая внутри __call__ похоже никогда не вызывается.
как и для чего подразумевается ее использовать?

собственно у меня похожая задача, только я хочу использовать декораторы не только для логирования,
но и для обработки ошибок, а так же возможно, для разных уровней логирования.
для двух последних задач, кмк, нужны декораторы, меняющие свое поведение,
в зависимости от внешних условий (например параметры программы).

задача и ее решение на словах:
- есть много классов и их методов, возвращающих True/False.
- хочется не меняя их (или меняя минимально ~1-2 строки на класс) писать новый клиентский код,
который не будет проверять True/False после каждого вызова
- пишем декоратор для обработки ошибок, в котором, в зависимости от какого-то класса конфигурации,
решаем, что на каждый False надо еще бросить Exception (хочется завершать программу по любой ошибке),
тогда клиентский код становится компактнее

можно сделать лучше?

код пока еще не написан…
далее буду эксперементировать с тем сохраняется ли при таком подходе более или адекватный стэк,
что важно для анализа ошибок “пользователем” программы.

еще интересный момент в том, что как и с методами, хотелось бы применять к классу сразу несколько декораторов.
но для этого, я так понимаю, их прийдется объединять во что-то типа иерархии наследования,
с обеспечением вызова всех базовых классов.

проще нельзя?



Отредактировано (Апрель 18, 2011 07:45:42)

Офлайн

#7 Апрель 18, 2011 09:50:01

shupg
От:
Зарегистрирован: 2009-08-07
Сообщения: 25
Репутация: +  0  -
Профиль   Отправить e-mail  

Как применить декоратор ко всем пользовательским методам

функция func, которая внутри __call__ похоже никогда не вызывается.
Осталось от дебага, видимо

можно сделать лучше?
Пользовался дополнительными флагами при объявлении класса. И обрабатывал их в метаклассе.

class CustomClass(object):
__metaclass__ = LoggedClass
_throw_exc_on_False = True
еще интересный момент в том, что как и с методами, хотелось бы применять к классу сразу несколько декораторов.
но для этого, я так понимаю, их прийдется объединять во что-то типа иерархии наследования,
с обеспечением вызова всех базовых классов.
Можно, только осторожно) У меня это сделать нормально не получилось. В результате, нарисовал иерархию метаклассов, там все и обрабатывал.



Офлайн

#8 Апрель 18, 2011 09:57:45

Андрей Светлов
От:
Зарегистрирован: 2007-05-15
Сообщения: 3137
Репутация: +  14  -
Профиль   Адрес электронной почты  

Как применить декоратор ко всем пользовательским методам

Ребята, 2011 год на дворе! Начиная с Python 2.6 можно писать декораторы для классов.
Даже не взирая на то, что логировать вызовы всех методов без исключения — бессмысленно.
Логи ведь нужно не только писать, а еще и читать ;)



Офлайн

#9 Апрель 18, 2011 12:53:35

kl
От:
Зарегистрирован: 2011-04-18
Сообщения: 6
Репутация: +  0  -
Профиль   Отправить e-mail  

Как применить декоратор ко всем пользовательским методам

Андрей Светлов
Ребята, 2011 год на дворе! Начиная с Python 2.6 можно писать декораторы для классов.
ну и как это сделать лучше учитывая, что год 2011 и python >= 2.6?

Андрей Светлов
Даже не взирая на то, что логировать вызовы всех методов без исключения — бессмысленно.
Логи ведь нужно не только писать, а еще и читать ;)
наличие смысла определяется разработчиками, а читабельность системой логирования.
у нас она логирует нечто похожее на дерево вызовов.
читается все отлично.



Офлайн

#10 Апрель 18, 2011 12:57:14

kl
От:
Зарегистрирован: 2011-04-18
Сообщения: 6
Репутация: +  0  -
Профиль   Отправить e-mail  

Как применить декоратор ко всем пользовательским методам

мда… срочно надо почитать про мета-классы.
задам, наверное, ламерский вопрос.
каким образом, флаг _throw_exc_on_False или любой другой можно передать
из CustomClass в LoggedClass или прочитать внутри него,
тем самым, дополнительно настроив поведение декоратора?



Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version