将装饰器应用于类中所有函数

79 投票
11 回答
28443 浏览
提问于 2025-04-16 02:41

有没有办法可以把一个装饰器应用到一个类里的所有函数上,而不是每个函数都要单独写一次?

我觉得这样做就有点像是一个方面,而不是传统意义上的装饰器,听起来有点奇怪。不过我在想,如果是用来做一些像计时或者身份验证的功能,那这样做会很方便。

11 个回答

15

以下代码在Python 2.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)
36

每当你想要修改类的定义时,你可以选择使用类装饰器或者元类。比如,使用元类的方式:

import types

class DecoMeta(type):
   def __new__(cls, name: str, bases: tuple, attrs: dict):
      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.func_name
         result = func(*args, **kwargs)
         print "after",func.func_name
         return result
      return wrapper
   
class MyKlass(metaclass=DecoMeta):
   def func1(self): 
      pass
  
MyKlass().func1()

输出结果:

before func1
after func1

注意:这不会对静态方法和类方法进行装饰。

46

最简单的方法来实现这个,或者对一个类的定义进行其他修改,就是定义一个元类。

另外,你也可以在类定义的最后使用一个装饰器,具体可以用inspect来实现:

import inspect

class Something:
    def foo(self): 
        pass

for name, fn in inspect.getmembers(Something, inspect.isfunction):
    setattr(Something, name, decorator(fn))

不过在实际操作中,你肯定会想更有选择性地使用你的装饰器。当你想给除了一个方法以外的所有方法都加上装饰器时,你会发现用传统的装饰器语法会更简单、更灵活。

撰写回答