代码之家  ›  专栏  ›  技术社区  ›  Michael Mior

在函数装饰器中调用Python实例方法

  •  3
  • Michael Mior  · 技术社区  · 14 年前

    有没有一种干净的方法让装饰器只在实例化类的实例时调用类的实例方法?

    class C:
        def instance_method(self):
          print('Method called')
    
        def decorator(f):
            print('Locals in decorator %s  ' % locals())
            def wrap(f):
                print('Locals in wrapper   %s' % locals())
                self.instance_method()
                return f
            return wrap
    
        @decorator
        def function(self):
          pass
    
    c = C()
    c.function()
    

    我知道这样不行因为 self 此时未定义 decorator

    class C:
        def instance_method(self):
          print('Method called')
    
        def decorator():
            print('Locals in decorator %s  ' % locals())
            def wrap(f):
                def wrapped_f(*args):
                    print('Locals in wrapper   %s' % locals())
                    args[0].instance_method()
                    return f
                return wrapped_f
            return wrap
    
        @decorator()
        def function(self):
          pass
    
    c = C()
    c.function()
    

    我知道任何实例方法的第一个参数 自己 . 这个包装器的定义方式的问题是每次执行函数时都会调用instance方法,这是我不希望看到的。然后我做了以下的小改动:

    class C:
        def instance_method(self):
          print('Method called')
    def decorator(called=[]):
        print('Locals in decorator %s  ' % locals())
        def wrap(f):
            def wrapped_f(*args):
                print('Locals in wrapper   %s' % locals())
                if f.__name__ not in called:
                    called.append(f.__name__)
                    args[0].instance_method()
                return f
            return wrapped_f
        return wrap
    
    @decorator()
    def function(self):
      pass
    
    c = C()
    c.function()
    c.function()
    

    现在这个函数只被调用一次,但是我不喜欢每次调用这个函数时都要进行这个检查。我猜没办法,但如果有人有什么建议,我很乐意听听!谢谢:)

    4 回复  |  直到 14 年前
        1
  •  1
  •   Michael Mior    14 年前

    我想出了一个可能的替代方案。我喜欢它,因为只有一个调用在定义函数时发生,一个调用在实例化类时发生。唯一的缺点是函数属性有一点点额外的内存消耗。

    from types import FunctionType
    
    class C:
        def __init__(self):
            for name,f in C.__dict__.iteritems():
                if type(f) == FunctionType and hasattr(f, 'setup'):
                      self.instance_method()
    
        def instance_method(self):
          print('Method called')
    
        def decorator(f):
            setattr(f, 'setup', True)
            return f
    
        @decorator
        def function(self):
          pass
    
    c = C()
    c.function()
    c.function()
    
        2
  •  0
  •   Katriel    14 年前

    我觉得你在问一些根本不可能的问题。decorator将与 ,但实例方法在实例存在之前不存在,这是稍后的。所以decorator不能处理特定于实例的功能。

    考虑这一点的另一种方法是,decorator是一个函子:它将函数转换为其他函数。但这并没有说明 论据 这些职能;它在一个更高的层次上工作。所以对的参数调用实例方法 function 不是一个装饰师应该做的事情;这是我们应该做的事 .

        3
  •  0
  •   Daniel Kluev    14 年前

    它可以通过使用可调用函数作为装饰器来实现。

    class ADecorator(object):
        func = None
        def __new__(cls, func):
            dec = object.__new__(cls)
            dec.__init__(func)
            def wrapper(*args, **kw):
                return dec(*args, **kw)
            return wrapper
    
        def __init__(self, func, *args, **kw):
            self.func = func
            self.act  = self.do_first
    
        def do_rest(self, *args, **kw):
            pass
    
        def do_first(self, *args, **kw):
            args[0].a()
            self.act = self.do_rest
    
        def __call__(self, *args, **kw):
            return self.act(*args, **kw)
    
    class A(object):
        def a(self):
            print "Original A.a()"
    
        @ADecorator
        def function(self):
            pass
    
    
    a = A()
    a.function()
    a.function()
    
        4
  •  0
  •   unutbu    14 年前

    该类的多个实例 C 表现?应该 instance_method function ? 或者每个实例都应该调用 一次?

    called=[] 默认参数使装饰器记住 功能 decorator 在两个不同的类上使用,这两个类都有一个名为

    c=C()
    d=D()
    c.function()
    d.function()
    

    只会打电话 c.instance_method 预防 d.instance_method

    下面,我用 self._instance_method_called 记录 self.instance_method C级 呼叫 最多一次。

    如果你愿意的话 实例\u方法 最多调用一次 电话 功能 ,然后简单地定义 _instance_method_called 作为类属性而不是实例属性。

    def decorator():
        print('Locals in decorator %s  ' % locals())
        def wrap(f):
            def wrapped(self,*args):
                print('Locals in wrapper   %s' % locals())            
                if not self._instance_method_called:
                    self.instance_method()
                    self._instance_method_called=True
                return f
            return wrapped
        return wrap
    
    class C:
        def __init__(self):
            self._instance_method_called=False
        def instance_method(self): print('Method called')
        @decorator()
        def function(self):
          pass
    
    c = C()
    # Locals in decorator {}  
    c.function()
    # Locals in wrapper   {'self': <__main__.C instance at 0xb76f1aec>, 'args': (), 'f': <function function at 0xb76eed14>}
    # Method called
    c.function()
    # Locals in wrapper   {'self': <__main__.C instance at 0xb76f1aec>, 'args': (), 'f': <function function at 0xb76eed14>}
    
    d = C()
    d.function()
    # Locals in wrapper   {'self': <__main__.C instance at 0xb76f1bcc>, 'args': (), 'f': <function function at 0xb76eed14>}
    # Method called
    d.function()
    # Locals in wrapper   {'self': <__main__.C instance at 0xb76f1bcc>, 'args': (), 'f': <function function at 0xb76eed14>}
    

    为了摆脱 if 声明:

    def decorator():
        print('Locals in decorator %s  ' % locals())
        def wrap(f):
            def rest(self,*args):
                print('Locals in wrapper   %s' % locals())
                return f
            def first(self,*args):
                print('Locals in wrapper   %s' % locals())            
                self.instance_method()
                setattr(self.__class__,f.func_name,rest)
                return f
            return first
        return wrap
    
    class C:
        def instance_method(self): print('Method called')
        @decorator()
        def function(self):
          pass