【问题标题】:Python decorators in classes类中的 Python 装饰器
【发布时间】:2019-09-20 21:35:09
【问题描述】:

可以这样写吗:

class Test(object):
    def _decorator(self, foo):
        foo()

    @self._decorator
    def bar(self):
        pass

这失败了:@self 中的 self 是未知的

我也试过了:

@Test._decorator(self)

同样失败:测试未知

我想临时更改一些实例变量 在装饰器中,然后运行装饰方法,之前 把它们改回来。

【问题讨论】:

    标签: python class decorator self


    【解决方案1】:

    你想做的事情是不可能的。以下面的代码是否有效为例:

    class Test(object):
    
        def _decorator(self, foo):
            foo()
    
        def bar(self):
            pass
        bar = self._decorator(bar)
    

    当然,它是无效的,因为此时尚未定义 selfTest 也是如此,因为在定义类本身(正在处理中)之前不会定义它。我给你看这段代码 sn-p 因为这是你的装饰器 sn-p 转换成的。

    因此,如您所见,在装饰器中访问实例实际上是不可能的,因为装饰器是在定义它们所附加的任何函数/方法期间应用的,而不是在实例化期间应用的。

    如果您需要类级访问权限,请尝试以下操作:

    class Test(object):
    
        @classmethod
        def _decorator(cls, foo):
            foo()
    
        def bar(self):
            pass
    Test.bar = Test._decorator(Test.bar)
    

    【讨论】:

    • 应该更新以参考下面更准确的答案
    • 不错。你的散文说不可能,但你的代码几乎展示了如何做到这一点。
    【解决方案2】:

    这样的东西能满足你的需要吗?

    class Test(object):
        def _decorator(foo):
            def magic( self ) :
                print "start magic"
                foo( self )
                print "end magic"
            return magic
    
        @_decorator
        def bar( self ) :
            print "normal call"
    
    test = Test()
    
    test.bar()
    

    这避免了调用 self 来访问装饰器,并将其作为常规方法隐藏在类命名空间中。

    >>> import stackoverflow
    >>> test = stackoverflow.Test()
    >>> test.bar()
    start magic
    normal call
    end magic
    >>> 
    

    编辑以在 cmets 中回答问题:

    如何在另一个类中使用隐藏的装饰器

    class Test(object):
        def _decorator(foo):
            def magic( self ) :
                print "start magic"
                foo( self )
                print "end magic"
            return magic
    
        @_decorator
        def bar( self ) :
            print "normal call"
    
        _decorator = staticmethod( _decorator )
    
    class TestB( Test ):
        @Test._decorator
        def bar( self ):
            print "override bar in"
            super( TestB, self ).bar()
            print "override bar out"
    
    print "Normal:"
    test = Test()
    test.bar()
    print
    
    print "Inherited:"
    b = TestB()
    b.bar()
    print
    

    输出:

    Normal:
    start magic
    normal call
    end magic
    
    Inherited:
    start magic
    override bar in
    start magic
    normal call
    end magic
    override bar out
    end magic
    

    【讨论】:

    • 装饰器还是被装饰的函数?请注意,当在实例上调用“bar”时,返回的包装 bar 的“魔术”函数在上面接收一个 self 变量,并且可以对它之前和之后(甚至是否)它调用“bar”的实例变量做任何事情.声明类时没有实例变量之类的东西。你想从装饰器中对班级做点什么吗?我不认为这是惯用的用法。
    • 谢谢迈克尔,现在才发现这就是我想要的。
    • 我发现这个解决方案比公认的答案好得多,因为它允许在定义点使用@装饰器语法。如果我必须在课程结束时调用装饰器,则不清楚这些函数是否正在被装饰。不能在装饰器本身上使用 @staticmethod 有点奇怪,但至少它可以工作。
    • 如果我创建一个Test的继承类我认为它不起作用。例如: class TestB(Test): @_decorator def foobar(self): print "adsf" 有没有解决方法?跨度>
    • @extraeee:检查我所做的编辑。您需要将给定的装饰器限定为静态方法,但只有在您完成使用它之后(或将静态方法版本分配给不同的名称)
    【解决方案3】:

    我在研究一个非常相似的问题时发现了这个问题。我的解决方案是将问题分成两部分。首先,您需要捕获要与类方法关联的数据。在这种情况下,handler_for 会将 Unix 命令与该命令输出的处理程序相关联。

    class OutputAnalysis(object):
        "analyze the output of diagnostic commands"
        def handler_for(name):
            "decorator to associate a function with a command"
            def wrapper(func):
                func.handler_for = name
                return func
            return wrapper
        # associate mount_p with 'mount_-p.txt'
        @handler_for('mount -p')
        def mount_p(self, slurped):
            pass
    

    现在我们已经为每个类方法关联了一些数据,我们需要收集这些数据并将其存储在类属性中。

    OutputAnalysis.cmd_handler = {}
    for value in OutputAnalysis.__dict__.itervalues():
        try:
            OutputAnalysis.cmd_handler[value.handler_for] = value
        except AttributeError:
            pass
    

    【讨论】:

      【解决方案4】:

      我在一些调试情况下使用这种类型的装饰器,它允许通过装饰覆盖类属性,而无需找到调用函数。

      class myclass(object):
          def __init__(self):
              self.property = "HELLO"
      
          @adecorator(property="GOODBYE")
          def method(self):
              print self.property
      

      这里是装饰器代码

      class adecorator (object):
          def __init__ (self, *args, **kwargs):
              # store arguments passed to the decorator
              self.args = args
              self.kwargs = kwargs
      
          def __call__(self, func):
              def newf(*args, **kwargs):
      
                  #the 'self' for a method function is passed as args[0]
                  slf = args[0]
      
                  # replace and store the attributes
                  saved = {}
                  for k,v in self.kwargs.items():
                      if hasattr(slf, k):
                          saved[k] = getattr(slf,k)
                          setattr(slf, k, v)
      
                  # call the method
                  ret = func(*args, **kwargs)
      
                  #put things back
                  for k,v in saved.items():
                      setattr(slf, k, v)
      
                  return ret
              newf.__doc__ = func.__doc__
              return newf 
      

      注意:因为我使用了类装饰器,所以你需要使用 @adecorator() 加上括号 来装饰函数,即使你没有将任何参数传递给装饰器类构造函数。

      【讨论】:

        【解决方案5】:

        装饰器似乎更适合修改整个对象(包括函数对象)的功能,而对象方法的功能通常取决于实例属性。例如:

        def mod_bar(cls):
            # returns modified class
        
            def decorate(fcn):
                # returns decorated function
        
                def new_fcn(self):
                    print self.start_str
                    print fcn(self)
                    print self.end_str
        
                return new_fcn
        
            cls.bar = decorate(cls.bar)
            return cls
        
        @mod_bar
        class Test(object):
            def __init__(self):
                self.start_str = "starting dec"
                self.end_str = "ending dec" 
        
            def bar(self):
                return "bar"
        

        输出是:

        >>> import Test
        >>> a = Test()
        >>> a.bar()
        starting dec
        bar
        ending dec
        

        【讨论】:

          【解决方案6】:

          你可以装饰装饰器:

          import decorator
          
          class Test(object):
              @decorator.decorator
              def _decorator(foo, self):
                  foo(self)
          
              @_decorator
              def bar(self):
                  pass
          

          【讨论】:

            【解决方案7】:

            这是从同一类中定义的decorator 内部访问(并已使用)self 的一种方式:

            class Thing(object):
                def __init__(self, name):
                    self.name = name
            
                def debug_name(function):
                    def debug_wrapper(*args):
                        self = args[0]
                        print 'self.name = ' + self.name
                        print 'running function {}()'.format(function.__name__)
                        function(*args)
                        print 'self.name = ' + self.name
                    return debug_wrapper
            
                @debug_name
                def set_name(self, new_name):
                    self.name = new_name
            

            输出(在Python 2.7.10 上测试):

            >>> a = Thing('A')
            >>> a.name
            'A'
            >>> a.set_name('B')
            self.name = A
            running function set_name()
            self.name = B
            >>> a.name
            'B'
            

            上面的例子很愚蠢,但它确实有效。

            【讨论】:

              【解决方案8】:
              import functools
              
              
              class Example:
              
                  def wrapper(func):
                      @functools.wraps(func)
                      def wrap(self, *args, **kwargs):
                          print("inside wrap")
                          return func(self, *args, **kwargs)
                      return wrap
              
                  @wrapper
                  def method(self):
                      print("METHOD")
              
                  wrapper = staticmethod(wrapper)
              
              
              e = Example()
              e.method()
              

              【讨论】:

              • TypeError: 'staticmethod' 对象不可调用
              • @wyx 不要调用装饰器。例如,应该是@foo,而不是@foo()
              • wrapper 的第一个参数不应该是self吗?
              • @docyoda 这不是问题。见stackoverflow.com/q/41921255。此示例中的可取之处在于 wrapper = staticmethod(wrapper) 低于 @wrapper。如果首先出现wrapper = staticmethod(wrapper)(或者使用更常见的@staticmethod 装饰器),它确实会给出TypeError。我实际上不确定使其成为静态方法的原因在这种情况下
              【解决方案9】:

              以下是对 Michael Speer 的回答的扩展,以更进一步:

              一个实例方法装饰器,它接受参数并作用于带有参数和返回值的函数。

              class Test(object):
                  "Prints if x == y. Throws an error otherwise."
                  def __init__(self, x):
                      self.x = x
              
                  def _outer_decorator(y):
                      def _decorator(foo):
                          def magic(self, *args, **kwargs) :
                              print("start magic")
                              if self.x == y:
                                  return foo(self, *args, **kwargs)
                              else:
                                  raise ValueError("x ({}) != y ({})".format(self.x, y))
                              print("end magic")
                          return magic
              
                      return _decorator
              
                  @_outer_decorator(y=3)
                  def bar(self, *args, **kwargs) :
                      print("normal call")
                      print("args: {}".format(args))
                      print("kwargs: {}".format(kwargs))
              
                      return 27
              

              然后

              In [2]:
              
                  test = Test(3)
                  test.bar(
                      13,
                      'Test',
                      q=9,
                      lollipop=[1,2,3]
                  )
                  ​
                  start magic
                  normal call
                  args: (13, 'Test')
                  kwargs: {'q': 9, 'lollipop': [1, 2, 3]}
              Out[2]:
                  27
              In [3]:
              
                  test = Test(4)
                  test.bar(
                      13,
                      'Test',
                      q=9,
                      lollipop=[1,2,3]
                  )
                  ​
                  start magic
                  ---------------------------------------------------------------------------
                  ValueError                                Traceback (most recent call last)
                  <ipython-input-3-576146b3d37e> in <module>()
                        4     'Test',
                        5     q=9,
                  ----> 6     lollipop=[1,2,3]
                        7 )
              
                  <ipython-input-1-428f22ac6c9b> in magic(self, *args, **kwargs)
                       11                     return foo(self, *args, **kwargs)
                       12                 else:
                  ---> 13                     raise ValueError("x ({}) != y ({})".format(self.x, y))
                       14                 print("end magic")
                       15             return magic
              
                  ValueError: x (4) != y (3)
              

              【讨论】:

                【解决方案10】:

                我有一个可能有帮助的装饰器实现

                    import functools
                    import datetime
                
                
                    class Decorator(object):
                
                        def __init__(self):
                            pass
                
                
                        def execution_time(func):
                
                            @functools.wraps(func)
                            def wrap(self, *args, **kwargs):
                
                                """ Wrapper Function """
                
                                start = datetime.datetime.now()
                                Tem = func(self, *args, **kwargs)
                                end = datetime.datetime.now()
                                print("Exection Time:{}".format(end-start))
                                return Tem
                
                            return wrap
                
                
                    class Test(Decorator):
                
                        def __init__(self):
                            self._MethodName = Test.funca.__name__
                
                        @Decorator.execution_time
                        def funca(self):
                            print("Running Function : {}".format(self._MethodName))
                            return True
                
                
                    if __name__ == "__main__":
                        obj = Test()
                        data = obj.funca()
                        print(data)
                

                【讨论】:

                  【解决方案11】:

                  在内部类中声明。 此解决方案非常可靠,值得推荐。

                  class Test(object):
                      class Decorators(object):
                      @staticmethod
                      def decorator(foo):
                          def magic(self, *args, **kwargs) :
                              print("start magic")
                              foo(self, *args, **kwargs)
                              print("end magic")
                          return magic
                  
                      @Decorators.decorator
                      def bar( self ) :
                          print("normal call")
                  
                  test = Test()
                  
                  test.bar()
                  

                  结果:

                  >>> test = Test()
                  >>> test.bar()
                  start magic
                  normal call
                  end magic
                  >>> 
                  

                  【讨论】:

                    【解决方案12】:

                    简单的方法。 您只需将装饰器方法放在类之外。 你仍然可以在里面使用它。

                    def my_decorator(func):
                        #this is the key line. There's the aditional self parameter
                        def wrap(self, *args, **kwargs):
                            # you can use self here as if you were inside the class
                            return func(self, *args, **kwargs)
                        return wrap
                    
                    class Test(object):
                        @my_decorator
                        def bar(self):
                            pass
                    

                    【讨论】:

                      猜你喜欢
                      • 2010-10-14
                      • 2011-04-28
                      • 2012-02-09
                      • 2013-10-30
                      • 1970-01-01
                      • 2014-02-16
                      • 2015-08-21
                      • 1970-01-01
                      相关资源
                      最近更新 更多