将装饰器应用于类中所有函数
有没有办法可以把一个装饰器应用到一个类里的所有函数上,而不是每个函数都要单独写一次?
我觉得这样做就有点像是一个方面,而不是传统意义上的装饰器,听起来有点奇怪。不过我在想,如果是用来做一些像计时或者身份验证的功能,那这样做会很方便。
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))
不过在实际操作中,你肯定会想更有选择性地使用你的装饰器。当你想给除了一个方法以外的所有方法都加上装饰器时,你会发现用传统的装饰器语法会更简单、更灵活。