将装饰器附加到类中的所有函数

doc*_*ead 47 python oop class decorator class-method

我真的不需要这样做,但只是想知道,有没有办法将装饰器绑定到类中的所有函数,而不是明确地为每个函数声明它.

我认为它然后变成了一种方面,而不是装饰者,它确实感觉有点奇怪,但是想到时间或认证这样的东西它会非常整洁.

Dun*_*can 31

最简单的方法是对类定义进行其他修改,即定义元类.

或者,只需在类定义的末尾应用装饰器:

class Something:
   def foo(self): pass

for name, fn in inspect.getmembers(Something):
    if isinstance(fn, types.UnboundMethodType):
        setattr(Something, name, decorator(fn))
Run Code Online (Sandbox Code Playgroud)

对于Python 3,将types.UnboundMethodType替换为types.FunctionType.

在实践中,您当然希望更有选择地应用您的装饰器,并且只要您想要装饰除一种方法之外的所有方法,您将发现以传统方式使用装饰器语法更容易和更灵活.

  • 如果您不希望装饰未绑定的方法(即当您在类主体之外定义一个函数,然后执行类似“class Something: foo = function_you_define”之类的操作时),请使用“inspect.ismethod”而不是“inspect.isfunction” 。另请注意,即使使用“isfunction”,内置函数也不会被装饰,例如,如果您有“class Sommething:foo = len”,那么使用答案中的代码,“foo”将不会被装饰,您需要使用“inspect” .isroutine` 来处理这种情况。 (2认同)

Anu*_*yal 29

每次想到更改类定义时,都可以使用类装饰器或元类.例如,使用元类

import types

class DecoMeta(type):
   def __new__(cls, name, bases, attrs):

      for attr_name, attr_value in attrs.iteritems():
         if isinstance(attr_value, types.FunctionType):
            attrs[attr_name] = cls.deco(attr_value)

      return super(DecoMeta, cls).__new__(cls, name, bases, attrs)

   @classmethod
   def deco(cls, func):
      def wrapper(*args, **kwargs):
         print "before",func.func_name
         result = func(*args, **kwargs)
         print "after",func.func_name
         return result
      return wrapper

class MyKlass(object):
   __metaclass__ = DecoMeta

   def func1(self): 
      pass

MyKlass().func1()
Run Code Online (Sandbox Code Playgroud)

输出:

before func1
after func1
Run Code Online (Sandbox Code Playgroud)

注意:它不会修饰staticmethod和classmethod

  • 很棒的信息。我使用这种技术创建了测试方法 [Gold/approved file testing all methods in a test class against every file via metaclass metaprogramming in Python](https://gist.github.com/patkujawa-wf/1f569d245bbfc73f83a3) 。 (2认同)

Doh*_*mon 7

Python 3 的更新:

class DecoMeta(type):
    def __new__(cls, name, bases, attrs):

        for attr_name, attr_value in attrs.items():
            if isinstance(attr_value, types.FunctionType):
            attrs[attr_name] = cls.deco(attr_value)

        return super(DecoMeta, cls).__new__(cls, name, bases, attrs)

    @classmethod
    def deco(cls, func):
        def wrapper(*args, **kwargs):
            print("before",func.__name__)
            result = func(*args, **kwargs)
            print("after",func.__name__)
            return result
        return wrapper
Run Code Online (Sandbox Code Playgroud)

(并为此感谢邓肯)

  • 在“if isinstance”语句之后需要一个缩进块。感谢 3.X 的更新 (2认同)

pr-*_*pal 7

以下代码适用于 python2.x 和 3.x

import inspect

def decorator_for_func(orig_func):
    def decorator(*args, **kwargs):
         print("Decorating wrapper called for method %s" % orig_func.__name__)
         result = orig_func(*args, **kwargs)
         return result
    return decorator

def decorator_for_class(cls):
    for name, method in inspect.getmembers(cls):
        if (not inspect.ismethod(method) and not inspect.isfunction(method)) or inspect.isbuiltin(method):
            continue
        print("Decorating function %s" % name)
        setattr(cls, name, decorator_for_func(method))
    return cls

@decorator_for_class
class decorated_class:
     def method1(self, arg, **kwargs):
         print("Method 1 called with arg %s" % arg)
     def method2(self, arg):
         print("Method 2 called with arg %s" % arg)


d=decorated_class()
d.method1(1, a=10)
d.method2(2)
Run Code Online (Sandbox Code Playgroud)