2013-06-06 1 views
5

Я хочу регистрировать каждый вызов метода в некоторых классах. Я мог бы сделатьPython: протоколирование всех методов класса без оформления каждого из них

class Class1(object): 
    @log 
    def method1(self, *args): 
     ... 
    @log 
    def method2(self, *args): 
     ... 

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

class LoggedMeta(type): 
    def __new__(cls, name, bases, attrs): 
     def __getattribute__(self, name_): 
      attr = super().__getattribute__(name_) 
      if isinstance(attr, (types.MethodType, types.FunctionType)) and not name_.startswith("__"): 
       return makeLogged(attr) #This returns a method that first logs the method call, and then calls the original method. 
      return attr 
     attrs["__getattribute__"] = __getattribute__ 
    return type.__new__(cls, name, bases, attrs) 

class Class1(object): 
    __metaclass__ = LoggedMeta 
    def method1(self, *args): 
     ... 

Однако, я на Python 2.X, а синтаксис super() не работает. В то время, когда я называю супер, у меня нет класса __getattribute__ (но у меня есть его имя класса), поэтому я не могу использовать старый синтаксис super(Class, Inst).

Я попытался использовать метаклассы раньше, но переопределить все методы вместо __getattribute__, но я хочу также регистрировать статические вызовы методов, и они дали мне некоторые проблемы.

Я искал этот вопрос, но не нашел никого, кто пытался изменить класс таким образом.

Любые идеи или помощь будут очень благодарны.

EDIT: Мое решение было это (в основном взяты из this нити):

import inspect, types 

CLASS = 0 
NORMAL = 1 
STATIC = 2 

class DecoratedMethod(object): 

    def __init__(self, func, type_): 
     self.func = func 
     self.type = type_ 

    def __get__(self, obj, cls=None): 
     def wrapper(*args, **kwargs): 
      print "before" 
      if self.type == CLASS: 
       #classmethods (unlike normal methods) reach this stage as bound methods, but args still contains the class 
       #as a first argument, so we omit it. 
       ret = self.func(*(args[1:]), **kwargs) 
      else: 
       ret = self.func(*args, **kwargs) 
      print "after" 
      return ret 
     for attr in "__module__", "__name__", "__doc__": 
      setattr(wrapper, attr, getattr(self.func, attr)) 
     if self.type == CLASS: 
      return types.MethodType(wrapper, cls, type) 
     elif self.type == NORMAL: 
      return types.MethodType(wrapper, obj, cls) 
     else: 
      return wrapper 

def decorate_class(cls): 
    for name, meth in inspect.getmembers(cls): 
     if inspect.ismethod(meth): 
      if inspect.isclass(meth.im_self): 
       # meth is a classmethod 
       setattr(cls, name, DecoratedMethod(meth, CLASS)) 
      else: 
       # meth is a regular method 
       setattr(cls, name, DecoratedMethod(meth, NORMAL)) 
     elif inspect.isfunction(meth): 
      # meth is a staticmethod 
      setattr(cls, name, DecoratedMethod(meth, STATIC)) 
    return cls 


@decorate_class 
class MyClass(object): 

    def __init__(self): 
     self.a = 10 
     print "__init__" 

    def foo(self): 
     print self.a 

    @staticmethod 
    def baz(): 
     print "baz" 

    @classmethod 
    def bar(cls): 
     print "bar" 

позже я очистил его немного, но это суть раствора. Мне нужна эта разница между классными, статическими и обычными методами, потому что я хочу иметь

inst = MyClass() 
assert type(inst.baz) == types.FunctionType 
assert type(inst.foo) == types.MethodType 
assert type(inst.bar) == types.MethodType 
+0

Возможный дубликат [Как я могу украсить все функции класса, не вводя его снова и снова для каждого добавленного метода? Python] (http://stackoverflow.com/questions/6307761/how-can-i-decorate-all-functions-of-a-class-without-typing-it-over-and-over-for) – Flynsee

ответ

9

Почему бы вам не изменить объект класса?

Вы можете пройти через методы в классе с dir(MyClass) и заменить их обернутой версии ... что-то вроде:

def logify(klass): 
    for member in dir(klass): 
     if not callable(getattr(klass, method)) 
      continue # skip attributes 
     setattr(klass, method, log(method)) 

возиться вокруг с чем-то вроде этого ... должно работать ...

+1

Если вы был этот «возвратный класс», вы могли бы использовать его в качестве декоратора класса. – mgilson

+0

хороший, @mgilson. я вроде никогда не попадал в официальный материал декоратора, потому что я просто не использую его так часто ... –

2

Здесь может помочь декоратор класса. Украсьте весь класс и добавьте функциональность ведения журнала ко всем атрибутам, вызываемым классом.

0

Я предлагаю принимать for_all_methods декоратора из this SO post, то ваш код будет

@for_all_methods(log) 
class Class1(): 
    def method1(self): pass 
    ...