将一个装饰器附加到一个类中的所有函数中
我真的不需要这样做,但只是想知道,有没有办法将一个装饰器绑定到一个类中的所有函数,而不是明确地为每个函数声明它。
我想它会成为一种方面,而不是一个装饰者,它确实感觉有点奇怪,但是正在考虑像时间或authentication这样的东西,它会很整齐。
要做到这一点或者对类定义进行其他修改的最简洁的方法是定义一个元类。
或者,只需在类定义的末尾应用装饰器:
class Something: def foo(self): pass for name, fn in inspect.getmembers(Something): if isinstance(fn, types.UnboundMethodType): setattr(Something, name, decorator(fn))
对于Python 3,用types.FunctionTypereplacetypes.UnboundMethodType。
在实践当然,你会希望更有select性地应用你的装饰器,只要你想装饰除了一个方法以外的所有方法,你会发现,只是以传统的方式使用装饰器语法更简单,更灵活。
每当你想到改变类的定义,你可以使用类装饰器或元类。 例如使用元类
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()
输出:
before func1 after func1
注意:它不会装饰staticmethod和classmethod
当然,如果要修改python创build对象的方式,metaclasses是最Python的方法。 这可以通过覆盖你的类的__new__
方法来完成。 但是这个问题有一些问题(特别是python 3.X),我想提一下:
-
types.FunctionType
不保护特殊方法不被装饰,因为它们是函数types。 作为一个更一般的方法,你可以用双下划线(__
)来装饰它们的名字不会被启动的对象。 这种方法的另一个好处是它也覆盖了存在于命名空间中的那些以__
开始的对象,但是不像__qualname__
__
,__
__module__
等 -
__new__
头部中的namespace
参数不包含__init__
类属性。 原因是__new__
在__init__
之前执行(初始化)。 -
使用
classmethod
作为装饰器是没有必要的,因为在大多数情况下从另一个模块导入装饰器的时候。 - 如果你的类包含一个全局的项目(在
__init__
外面)拒绝被装饰,同时检查名称是不是以__
开头的,你可以检查types的typestypes.FunctionType
确保你不装饰非function对象。
这里是一个你可以使用的样本metacalss:
class TheMeta(type): def __new__(cls, name, bases, namespace, **kwds): # if your decorator is a class method of the metaclass use # `my_decorator = cls.my_decorator` in order to invoke the decorator. namespace = {k: v if k.startswith('__') else my_decorator(v) for k, v in namespace.items()} return type.__new__(cls, name, bases, namespace)
演示:
def my_decorator(func): def wrapper(self, arg): # You can also use *args instead of (self, arg) and pass the *args # to the function in following call. return "the value {} gets modified!!".format(func(self, arg)) return wrapper class TheMeta(type): def __new__(cls, name, bases, namespace, **kwds): # my_decorator = cls.my_decorator (if the decorator is a classmethod) namespace = {k: v if k.startswith('__') else my_decorator(v) for k, v in namespace.items()} return type.__new__(cls, name, bases, namespace) class MyClass(metaclass=TheMeta): # a = 10 def __init__(self, *args, **kwargs): self.item = args[0] self.value = kwargs['value'] def __getattr__(self, attr): return "This class hasn't provide the attribute {}.".format(attr) def myfunction_1(self, arg): return arg ** 2 def myfunction_2(self, arg): return arg ** 3 myinstance = MyClass(1, 2, value=100) print(myinstance.myfunction_1(5)) print(myinstance.myfunction_2(2)) print(myinstance.item) print(myinstance.p)
输出:
the value 25 gets modified!! the value 8 gets modified!! 1 This class hasn't provide the attribute p. # special method is not decorated.
为了检查上述注释中的第三项,可以取消注释a = 10
的行,然后执行print(myinstance.a)
并查看结果,然后在__new__
更改字典理解,如下所示,然后再次查看结果:
namespace = {k: v if k.startswith('__') and not isinstance(v, types.FunctionType)\ else my_decorator(v) for k, v in namespace.items()}
你可以覆盖__getattr__
方法。 它实际上并没有附加一个装饰器,但它可以让你返回一个装饰的方法。 你可能想要做这样的事情:
class Eggs(object): def __getattr__(self, attr): return decorate(getattr(self, `_` + attr))
有一些丑陋的recursion隐藏在那里,你会想要防范,但这是一个开始。