【问题标题】:Decorator for both class methods and functions类方法和函数的装饰器
【发布时间】:2016-07-04 05:22:45
【问题描述】:

我有一个装饰器

def deco(func):
    def inner(params):
        #< DO STUFF WITH func >
    return inner

还有一个基类

class GenericClass:
    def __init__(self,value):
        self.value = value
    def method(self,params):
        print 'NOT IMPLEMENTED YET'
    def other_method(self):
        print 'GOOD TO GO'

我希望能够在 GenericClass 的子类上装饰“方法”方法,例如检查输入/输出或处理异常(方法“方法”将被覆盖)

我想做的是……

class ChildClass(GenericClass):
    @deco
    def method(self,params):
        #< NEW METHOD >

我不是专家级的 Python 开发人员,该级别的所有文档都非常混乱(即元类、装饰器中的微妙之处、__call__ 方法等),我没有在 SO 上找到解决方案。

【问题讨论】:

  • 不确定这里有什么问题,如果你的装饰器是一个普通的函数,那么用它装饰任何东西都不应该有问题。
  • 当我使用装饰器时,它也会将“self”作为内部函数的参数。我想让我的装饰器足够通用,以便能够在未嵌入类的函数中使用它。
  • 我已经更新了下面的答案。我猜你不要在deco 中使用params,否则,你必须使用args[index] 访问它们,其中索引可以移动,即函数的第一个实际参数的索引为0,方法的索引为1。为简单起见,您可以使用负索引,但前提是参数的数量是恒定的

标签: python decorator


【解决方案1】:

知道了。您基本上是在问如何编写一个可以应用于函数和方法的装饰器。有可能:

def deco(func):
    def inner(*args):
        print('DECORATED: args={}'.format(args))
        func(*args)
    return inner

class Class:
    @deco
    def method(self, param): print('PARAM is {}'.format(param))

@deco
def func(a, b, c): print('{} {} {}'.format(a, b, c))

Class().method('X')

func(1, 2, 3)

输出:

DECORATED: args=(<__main__.Class instance at 0x7f740c6297a0>, 'X')
PARAM is X
DECORATED: args=(1, 2, 3)
1 2 3

附言

一年后,我在这里找到了一篇有用的帖子(8 年前有人问过):Using the same decorator (with arguments) with functions and methods。如果您关心修饰函数的实际参数,那么此处描述的方法将很有用。

【讨论】:

  • 只是一个荣耀的问题...有一种方法可以在父类级别包含装饰器操作吗?即在父级别编写检查代码,即使在子级别调用该方法时也会激活该检查
  • 不确定我是否理解正确。你的意思是你想装饰基方法,以便子类的覆盖方法也将被装饰?不,这是不可能的。来自 Python 之禅:Explicit is better than implicit.。你必须明确地装饰每个方法/函数
【解决方案2】:

我想通了。诀窍是模块级函数(除了闭包,我猜,你可能不想装饰它)有一个简单的名称,而方法至少有两个部分的限定名称。 忘掉inspect.ismethod——由于某种原因,它在这种情况下不起作用,尽管它应该是显而易见的选择,可能是一个错误。

def can(*fargs):
    def wrapper(func):
        if len(func.__qualname__.split('.')) > 1:
            def calling(self, *args, **kwargs):
                self, thing = args[0], args[1]
                do_stuff(thing)
                func(*args, **kwargs)
        else:
            def calling(*args, **kwargs):
                thing = args[0]
                do_stuff(thing)
                func(*args, **kwargs)
        return calling
    return wrapper

class C:
    @can(2, 3)
    def test(self, x):
        print(7, ismethod(self.test), x)

@can()
def test(x):
    print(8, ismethod(test), x)

c = C()
c.test(12)

test(8)

【讨论】:

    猜你喜欢
    • 2011-06-06
    • 2012-09-11
    • 1970-01-01
    • 2014-01-14
    • 2020-01-11
    • 1970-01-01
    • 1970-01-01
    • 2015-12-08
    • 2014-12-07
    相关资源
    最近更新 更多