类中的Python装饰器

hcv*_*vst 123 python class decorator self

可以写一些像:

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

    @self._decorator
    def bar(self):
        pass
Run Code Online (Sandbox Code Playgroud)

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

我也尝试过:

@Test._decorator(self)
Run Code Online (Sandbox Code Playgroud)

也失败了:测试未知

我想暂时更改装饰器中的一些实例变量,然后在更改它们之前运行装饰方法.

Mic*_*eer 238

这样的事情会做你需要的吗?

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()
Run Code Online (Sandbox Code Playgroud)

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

>>> import stackoverflow
>>> test = stackoverflow.Test()
>>> test.bar()
start magic
normal call
end magic
>>> 
Run Code Online (Sandbox Code Playgroud)

编辑回答评论中的问题:

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

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
Run Code Online (Sandbox Code Playgroud)

输出:

Normal:
start magic
normal call
end magic

Inherited:
start magic
override bar in
start magic
normal call
end magic
override bar out
end magic
Run Code Online (Sandbox Code Playgroud)

  • 装饰者还是装饰功能?注意返回的"魔术"函数包装bar在上面的实例上调用"bar"时会接收一个自变量,并且可以对它之前和之后想要的实例变量做任何事情(或者甚至是否称为"bar") .在声明类时,没有实例变量这样的东西.你想从装饰师那里对这个班做些什么吗?我认为这不是惯用法. (7认同)
  • 谢谢迈克尔,现在才看到这就是我想要的。 (2认同)
  • 我发现此解决方案比接受的答案好得多,因为它允许在定义时使用@ decorator语法。如果必须将装饰器调用放在类的末尾,则不清楚是否正在装饰函数。您不能在装饰器本身上使用@staticmethod有点奇怪,但至少可以使用。 (2认同)
  • @AndreiToroplean我想你要么必须将你的装饰器移到类之外才能遵守你的工具,要么尝试在你的工具上添加一个PR,如果该函数在类中用作装饰器,则允许缺少 self 。祝你好运。 (2认同)

Eva*_*ark 54

你不想做什么.例如,以下代码是否有效:

class Test(object):

    def _decorator(self, foo):
        foo()

    def bar(self):
        pass
    bar = self._decorator(bar)
Run Code Online (Sandbox Code Playgroud)

当然,它无效,因为此时self没有定义.同样适用,Test因为在定义类本身(它在其过程中)之前不会定义它.我正在向您展示此代码段,因为这是您的装饰器片段转换为的内容.

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

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

class Test(object):

    @classmethod
    def _decorator(cls, foo):
        foo()

    def bar(self):
        pass
Test.bar = Test._decorator(Test.bar)
Run Code Online (Sandbox Code Playgroud)

  • "不可能"事实上是不正确的. (18认同)
  • 应该更新,以参考下面更准确的答案 (4认同)
  • 好的。你的散文说不可能,但你的代码几乎展示了如何做到这一点。 (4认同)

mad*_*rdi 20

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()
Run Code Online (Sandbox Code Playgroud)

  • 类型错误:“静态方法”对象不可调用 (5认同)
  • “wrapper”的第一个参数不应该是“self”吗? (2认同)
  • @docyoda 这不是问题。请参阅 /sf/ask/2934487881/ 。此示例中的可取之处是“wrapper = staticmethod(wrapper)”低于“@wrapper”。如果首先发生“wrapper = staticmethod(wrapper)”(或者使用更常见的“@staticmethod”装饰器),它确实会给出“TypeError”。我实际上不确定*在这种情况下*将其设为静态方法可以实现什么效果。 (2认同)

小智 7

这是我知道(并且已经使用过)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
Run Code Online (Sandbox Code Playgroud)

输出(在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'
Run Code Online (Sandbox Code Playgroud)

上面的例子很愚蠢,但表明它有效.


men*_*kgs 7

简单的方法来做到这一点。您所需要做的就是将装饰器方法放在类之外。您仍然可以在里面使用它。

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
Run Code Online (Sandbox Code Playgroud)

  • 将装饰器放在类之外并不能回答如何将装饰器放在类内部的问题。您的方法不起作用的一个例子是装饰器依赖于类属性 (4认同)

sam*_*yse 6

我在研究一个非常相似的问题时发现了这个问题。我的解决方案是将问题分成两部分。首先,您需要捕获要与类方法关联的数据。在这种情况下,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
Run Code Online (Sandbox Code Playgroud)

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

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


dig*_*orn 6

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

class myclass(object):
    def __init__(self):
        self.property = "HELLO"

    @adecorator(property="GOODBYE")
    def method(self):
        print self.property
Run Code Online (Sandbox Code Playgroud)

这是装饰器代码

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 
Run Code Online (Sandbox Code Playgroud)

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


Yar*_*huk 6

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

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()
Run Code Online (Sandbox Code Playgroud)

结果:

>>> test = Test()
>>> test.bar()
start magic
normal call
end magic
>>> 
Run Code Online (Sandbox Code Playgroud)


Oli*_*ans 5

这是迈克尔·斯佩尔 (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
Run Code Online (Sandbox Code Playgroud)

进而

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)
Run Code Online (Sandbox Code Playgroud)