如何在不为每种方法反复输入的情况下装饰类的所有函数?

rap*_*ura 52 python decorator wrapper

让我说我的类有很多方法,我想在每一个方法上应用我的装饰,后来当我添加新方法时,我想要应用相同的装饰器,但我不想在方法声明之上写@mydecorator所有时间?

如果我调查的__call__是正确的方法吗?

重要提示:以下示例似乎解决了与原始问题不同的问题.

编辑:我想以这种方式展示,这是我的问题的类似解决方案,对于任何人后来发现这个问题,使用评论中提到的mixin.

class WrapinMixin(object):
    def __call__(self, hey, you, *args):
        print 'entering', hey, you, repr(args)
        try:
            ret = getattr(self, hey)(you, *args)
            return ret
        except:
            ret = str(e)
            raise
        finally:
            print 'leaving', hey, repr(ret)
Run Code Online (Sandbox Code Playgroud)

然后你可以在另一个

class Wrapmymethodsaround(WrapinMixin): 
    def __call__:
         return super(Wrapmymethodsaround, self).__call__(hey, you, *args)
Run Code Online (Sandbox Code Playgroud)

小智 57

使用遍历类属性并装饰callables的函数来装饰类.如果你的类变量可能碰巧是可调用的,那么这可能是错误的做法,并且还会修饰嵌套类(Sven Marnach指出这一点),但通常这是一个相当简洁的解决方案.示例实现(请注意,这不会排除__init__可能需要或可能不需要的特殊方法(等)):

def for_all_methods(decorator):
    def decorate(cls):
        for attr in cls.__dict__: # there's propably a better way to do this
            if callable(getattr(cls, attr)):
                setattr(cls, attr, decorator(getattr(cls, attr)))
        return cls
    return decorate
Run Code Online (Sandbox Code Playgroud)

使用这样:

@for_all_methods(mydecorator)
class C(object):
    def m1(self): pass
    def m2(self, x): pass
    ...
Run Code Online (Sandbox Code Playgroud)

在Python 3.0和3.1中,callable不存在.它永远存在于Python 2.x中,并且作为包装器返回到Python 3.2中isinstance(x, collections.Callable),因此您可以callable在这些版本中使用它(或使用此定义您自己的替换).

  • 为什么不使用`inspect.getmembers(cls,inspect.ismethod)`而不是`__dict__`和`callable()`?当然,静态方法在这种情况下是不可能的. (9认同)
  • 在Python 3中,`inspect.getmembers(cls,inspect.ismethod)`将不起作用,因为`inspect.ismethod`为未绑定的方法返回`False`.在Python 2中,`inspect.ismethod`为未绑定的方法返回`True`,但`inspect.isfunction`返回`False`.也许最好写一下`inspect.getmembers(cls,inspect.isroutine)`,因为它适用于两者. (8认同)
  • 请注意,这也将装饰嵌套类.(我的实现有同样的问题.) (4认同)
  • 使用 `cls.__dict__` 不会修饰继承的方法,但 `inspect` 方法会。 (2认同)

Sin*_*ion 26

虽然我不喜欢在显式方法中使用神奇方法,但您可以使用元类来实现此目的.

def myDecorator(fn):
    fn.foo = 'bar'
    return fn

class myMetaClass(type):
    def __new__(cls, name, bases, local):
        for attr in local:
            value = local[attr]
            if callable(value):
                local[attr] = myDecorator(value)
        return type.__new__(cls, name, bases, local)

class myClass(object):
    __metaclass__ = myMetaClass
    def baz(self):
        print self.baz.foo
Run Code Online (Sandbox Code Playgroud)

并且它的工作方式就好像每个可调用的myClass都被装饰了一样myDecorator

>>> quux = myClass()
>>> quux.baz()
bar
Run Code Online (Sandbox Code Playgroud)

  • 我也在研究这种方法,它很棒。这是一般讨论元类的一个很好的参考:https://blog.ionelmc.ro/2015/02/09/understanding-python-metaclasses/ 该参考文献中提到的一个好点是,这种方法还可以处理子类,其中,我相信类装饰器不会。这对我来说是很重要的一点! (2认同)
  • 我不认为这有什么神奇之处,而是元类有一个糟糕的公关部门。只要合适就应该使用它们,就像这里的情况一样。好的解决方案! (2认同)

nic*_*ame 8

不要从死里复活,但我真的很喜欢德尔南的答案,但发现它很缺乏.

def for_all_methods(exclude, decorator):
    def decorate(cls):
        for attr in cls.__dict__:
            if callable(getattr(cls, attr)) and attr not in exclude:
                setattr(cls, attr, decorator(getattr(cls, attr)))
        return cls
    return decorate
Run Code Online (Sandbox Code Playgroud)

编辑:修复缩进

所以你可以指定方法//属性//你不想要装饰的东西

  • 总是欢迎改进,有人可能会发现它有用 (4认同)

osa*_*osa 7

以上答案都不适合我,因为我还想装饰继承的方法,这不是通过 using 完成的__dict__,而且我不想用元类使事情变得过于复杂。最后,我对 Python 2 的解决方案感到满意,因为我只需要立即添加一些分析代码来测量类的所有函数使用的时间。

import inspect
def for_all_methods(decorator):
    def decorate(cls):
        for name, fn in inspect.getmembers(cls, inspect.ismethod):
            setattr(cls, name, decorator(fn))
        return cls
    return decorate
Run Code Online (Sandbox Code Playgroud)

来源(略有不同的解决方案):https : //stackoverflow.com/a/3467879/1243926 在那里您还可以看到如何为 Python 3 更改它。

正如对其他答案的评论所暗示的那样,请考虑使用inspect.getmembers(cls, inspect.isroutine)。如果您找到了适用于 Python 2 和 Python 3 并修饰继承方法的合适解决方案,并且仍然可以在 7 行中完成,请编辑。


小智 5

您可以生成一个元类。这不会修饰继承的方法。

def decorating_meta(decorator):
    class DecoratingMetaclass(type):
        def __new__(self, class_name, bases, namespace):
            for key, value in list(namespace.items()):
                if callable(value):
                    namespace[key] = decorator(value)

            return type.__new__(self, class_name, bases, namespace)

    return DecoratingMetaclass
Run Code Online (Sandbox Code Playgroud)

这将生成一个元类,用指定的函数装饰所有方法。您可以通过执行以下操作在 Python 2 或 3 中使用它

def doubling_decorator(f):
    def decorated(*a, **kw):
        return f(*a, **kw) * 2
    return decorated

class Foo(dict):
    __metaclass__ = decorating_meta(doubling_decorator)

    def lookup(self, key):
        return self[key]

d = Foo()
d["bar"] = 5
print(d.lookup("bar")) # prints 10
Run Code Online (Sandbox Code Playgroud)