我用这种方法装饰所有的方法

import inspect

def decallmethods(decorator, prefix='test_'):
  def dectheclass(cls):
    for name, m in inspect.getmembers(cls, inspect.ismethod):
      if name.startswith(prefix):
        setattr(cls, name, decorator(m))
    return cls
  return dectheclass


@decallmethods(login_testuser)
class TestCase(object):
    def setUp(self):
        pass

    def test_1(self):
        print "test_1()"

    def test_2(self):
        print "test_2()"

这是工作,但它适用于顶部,如果我有其他装饰。
我是说
现在结果是
@login_testuser
@other
def test_2(self):
    print "test_2()"

但我想
@other
@login_testuser
def test_2(self):
    print "test_2()"

最佳答案

这当然是一个坏主意,但你想做的事情在某种程度上可以做到,这需要很多时间来解释。首先,与其把装饰者看作语法糖,不如把它们看作真正的东西:一个函数(即一个闭包),里面有一个函数。如果我们有一个函数,这就不可能了:

def operation(a, b):
    print('doing operation')
    return a + b

很简单它就能做到
>>> hi = operation('hello', 'world')
doing operation
>>> print(hi)
helloworld

现在定义一个装饰器,它在调用其内部函数之前和之后打印一些东西(相当于稍后要装饰的other装饰器):
def other(f):
    def other_inner(*a, **kw):
        print('other start')
        result = f(*a, **kw)
        print('other finish')
        return result
    return other_inner

然后,构建一个新的函数和decorator
@other
def o_operation(a, b):
    print('doing operation')
    return a + b

记住,这基本上相当于o_operation = other(operation)
运行此程序以确保其工作:
>>> r2 = o_operation('some', 'inner')
other start
doing operation
other finish
>>> print(r2)
someinner

最后,您希望在operation之前调用但不使用d_operation的最终装饰器,但是使用现有代码,结果如下:
def inject(f):
    def injected(*a, **kw):
        print('inject start')
        result = f(*a, **kw)
        print('inject finish')
        return result
    return injected

@inject
@other
def i_o_operation(a, b):
    print('doing operation')
    return a + b

运行上面的命令:
>>> i_o_operation('hello', 'foo')
inject start
other start
doing operation
other finish
inject finish
'hellofoo'

如前所述,decorator实际上是闭包,因此这就是为什么可以将有效实例化的项放在内部的原因。您可以通过__closure__属性访问它们:
>>> i_o_operation.__closure__
(<cell at 0x7fc0eabd1fd8: function object at 0x7fc0eabce7d0>,)
>>> i_o_operation.__closure__[0].cell_contents
<function other_inner at 0x7fc0eabce7d0>
>>> print(i_o_operation.__closure__[0].cell_contents('a', 'b'))
other start
doing operation
other finish
ab

看看这是如何有效地直接调用injected闭包内的函数的,就好像它被解包了一样。如果那个封盖可以换成注射用的呢?对于我们的所有保护,__closure__cell.cell_contents都是只读的。需要做的是,通过使用FunctionType函数构造函数(在AA>模块中找到)来构造具有预期闭包的完全新函数。
回到问题上来。因为我们现在拥有的是:
i_o_operation = inject(other(operation))

我们想要的是
o_i_operation = other(inject(operation))

我们必须以某种方式从types中删除对other的调用,并以某种方式用i_o_operation将其包装起来以生成inject。(休息后有龙跟着)
首先,构造一个函数,通过将闭包关闭为深度(从而使其包含原始的o_i_operation,但将其与inject(operation)生成的代码混合在一起:
i_operation = FunctionType(
    i_o_operation.__code__,
    globals=globals(),
    closure=i_o_operation.__closure__[0].cell_contents.__closure__,
)

因为foperation的结果,所以我们可以使用该代码生成一个新函数。inject(f)是一个必需的形式,最后得到嵌套级别的闭包,然后生成函数的第一部分。确认未调用i_o_operation
>>> i_operation('test', 'strip')
inject start
doing operation
inject finish
'teststrip'

整洁。然而,我们仍然希望inject(f)被包装在这个之外,最终生成globals。我们确实需要以某种方式将我们生成的这个新函数放在一个闭包中,方法是创建一个产生一个
def closure(f):
    def surrogate(*a, **kw):
        return f(*a, **kw)
    return surrogate

简单地使用它来构造和提取我们的闭包
o_i_operation = FunctionType(
    i_o_operation.__closure__[0].cell_contents.__code__,
    globals=globals(),
    closure=closure(i_operation).__closure__,
)

叫这个:
>>> o_i_operation('job', 'complete')
other start
inject start
doing operation
inject finish
other finish
'jobcomplete'

看来我们终于得到了我们需要的。虽然这并不能准确地回答你的问题,但这是正确的,但已经相当严重了。
现在针对实际问题:一个函数,它将确保装饰函数是在给定的原件、未装饰的函数(即对于给定的otherother)之前最内层(最终)可调用的函数,我们要模拟给出o_i_operation的结果。这是代码:
from types import FunctionType

def strip_decorators(f):
    """
    Strip all decorators from f.  Assumes each are functions with a
    closure with a first cell being the target function.
    """

    # list of not the actual decorator, but the returned functions
    decorators = []
    while f.__closure__:
        # Assume first item is the target method
        decorators.append(f)
        f = f.__closure__[0].cell_contents
    return decorators, f

def inject_decorator(decorator, f):
    """
    Inject a decorator to the most inner function within the stack of
    closures in `f`.
    """

    def closure(f):
        def surrogate(*a, **kw):
            return f(*a, **kw)
        return surrogate

    decorators, target_f = strip_decorators(f)
    result = decorator(target_f)

    while decorators:
        # pop out the last one in
        decorator = decorators.pop()
        result = FunctionType(
            decorator.__code__,
            globals=globals(),
            closure=closure(result).__closure__,
        )

    return result

为了测试这一点,我们使用一个典型的用例-html标记。
def italics(f):
    def i(s):
        return '<i>' + f(s) + '</i>'
    return i

def bold(f):
    def b(s):
        return '<b>' + f(s) + '</b>'
    return b

def underline(f):
    def u(s):
        return '<u>' + f(s) + '</u>'
    return u

@italics
@bold
def hi(s):
    return s

正在运行测试。
>>> hi('hello')
'<i><b>hello</b></i>'

我们的目标是将targetdecorator(特别是f(g(...(callable))可调用的)注入到最内部的闭包中。可以这样做,使用我们在上面定义的函数:
>>> hi_u = inject_decorator(underline, hi)
>>> hi_u('hello')
'<i><b><u>hello</u></b></i>'

使用未修饰的函数:
>>> def pp(s):
...     return s
...
>>> pp_b = inject_decorator(bold, pp)
>>> pp_b('hello')
'<b>hello</b>'

对于重写器的第一个剪切版本,有一个主要的假设,即链中的所有装饰器只有一个闭包长度,即一个元素是要装饰的函数。以这个装饰器为例:
def prefix(p):
    def decorator(f):
        def inner(*args, **kwargs):
            new_args = [p + a for a in args]
            return f(*new_args, **kwargs)
        return inner
    return decorator

示例用法:
>>> @prefix('++')
... def prefix_hi(s):
...     return s
...
>>> prefix_hi('test')
'++test'

现在尝试注入一个f(g(...(target(callable))))decorator,如下所示:
>>> prefix_hi_bold = inject_decorator(bold, prefix_hi)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 18, in inject_decorator
ValueError: inner requires closure of length 2, not 1

这仅仅是因为underline内的u(hi)形成的闭包有两个元素,一个是前缀字符串bold,另一个是实际函数,decorator嵌套在其中,期望这两个元素都出现在闭包内。解决这个问题需要更多的代码来分析和重构细节。
无论如何,这个解释花费了相当多的时间和话语,所以我希望你能理解这一点,也许你能从实际的正确轨道开始。
如果你想把prefix变成装饰师,或者把它融入你的班级装饰家,那就太好了,大部分的辛苦工作已经完成了。

关于python - 如何在方法的所有装饰器的基础上应用类装饰器,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/33249515/

10-12 00:48
查看更多