Найти - Пользователи
Полная версия: Как применить декоратор ко всем пользовательским методам
Начало » Python для экспертов » Как применить декоратор ко всем пользовательским методам
1 2
shupg
Есть стремление не приписывать декоратор к каждому методу класса, а просто сказать, что все функции этого класса должны перед вызовом сделать то-то и то-то. Например, напечатать, “сейчас работает функция 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…

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

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')
Во время разработки эти сообщения будут видны, а на продакшене они в лог не попадут (это всё потом можно будет сконфигурировать).
ZZZ
Я бы подошёл так:
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'
Не проверял, но моя мысль, думаю, ясна.
Ed
А просто в __getattribute__ заврапить то, что является callable не пойдет?
shupg
Перемудрил я! Спасибо 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()
kl
спасибо за классный рабочий пример!
только вот функция func, которая внутри __call__ похоже никогда не вызывается.
как и для чего подразумевается ее использовать?

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

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

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

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

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

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

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

class CustomClass(object):
__metaclass__ = LoggedClass
_throw_exc_on_False = True
еще интересный момент в том, что как и с методами, хотелось бы применять к классу сразу несколько декораторов.
но для этого, я так понимаю, их прийдется объединять во что-то типа иерархии наследования,
с обеспечением вызова всех базовых классов.
Можно, только осторожно) У меня это сделать нормально не получилось. В результате, нарисовал иерархию метаклассов, там все и обрабатывал.
Андрей Светлов
Ребята, 2011 год на дворе! Начиная с Python 2.6 можно писать декораторы для классов.
Даже не взирая на то, что логировать вызовы всех методов без исключения — бессмысленно.
Логи ведь нужно не только писать, а еще и читать ;)
kl
Андрей Светлов
Ребята, 2011 год на дворе! Начиная с Python 2.6 можно писать декораторы для классов.
ну и как это сделать лучше учитывая, что год 2011 и python >= 2.6?

Андрей Светлов
Даже не взирая на то, что логировать вызовы всех методов без исключения — бессмысленно.
Логи ведь нужно не только писать, а еще и читать ;)
наличие смысла определяется разработчиками, а читабельность системой логирования.
у нас она логирует нечто похожее на дерево вызовов.
читается все отлично.
kl
мда… срочно надо почитать про мета-классы.
задам, наверное, ламерский вопрос.
каким образом, флаг _throw_exc_on_False или любой другой можно передать
из CustomClass в LoggedClass или прочитать внутри него,
тем самым, дополнительно настроив поведение декоратора?
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