Using the same decorator (with arguments) with functions and methods
我一直在尝试创建一个可以同时用于Python中的函数和方法的修饰器。这本身并不难,但当创建一个接受参数的装饰器时,似乎是这样。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class methods(object): def __init__(self, *_methods): self.methods = _methods def __call__(self, func): def inner(request, *args, **kwargs): print request return func(request, *args, **kwargs) return inner def __get__(self, obj, type=None): if obj is None: return self new_func = self.func.__get__(obj, type) return self.__class__(new_func) |
上面的代码正确地包装了函数/方法,但是在方法的情况下,
有没有一种方法来判断是否将修饰符应用于函数而不是方法,并进行相应的处理?
扩展
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class _MethodDecoratorAdaptor(object): def __init__(self, decorator, func): self.decorator = decorator self.func = func def __call__(self, *args, **kwargs): return self.decorator(self.func)(*args, **kwargs) def __get__(self, instance, owner): return self.decorator(self.func.__get__(instance, owner)) def auto_adapt_to_methods(decorator): """Allows you to use the same decorator on methods and functions, hiding the self argument from the decorator.""" def adapt(func): return _MethodDecoratorAdaptor(decorator, func) return adapt |
。
这样,您就可以使您的装饰器自动适应使用它的条件。
1 2 3 4 5 6 7 8 9 | def allowed(*allowed_methods): @auto_adapt_to_methods def wrapper(func): def wrapped(request): if request not in allowed_methods: raise ValueError("Invalid method %s" % request) return func(request) return wrapped return wrapper |
请注意,包装器函数是在所有函数调用上调用的,所以不要在那里做任何昂贵的事情。
装饰器的使用:
1 2 3 4 5 6 7 8 9 10 11 | class Foo(object): @allowed('GET', 'POST') def do(self, request): print"Request %s on %s" % (request, self) @allowed('GET') def do(request): print"Plain request %s" % request Foo().do('GET') # Works Foo().do('POST') # Raises |
。
decorator总是应用于一个函数对象——让decorator
即,在代码中:
1 2 3 4 | class X(object): @deco def f(self): pass |
也许你能更好地解释一下你想为你的装饰师使用的一种玩具,这样我们可以提供更多的帮助…?
编辑:这也适用于带有参数的装饰器,即
1 2 3 4 | class X(object): @deco(23) def f(self): pass |
号
那么在类体中调用的是
因为您已经定义了一个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | class methods(object): def __init__(self, *_methods, called_on_method=False): self.methods = _methods self.called_on_method def __call__(self, func): if self.called_on_method: def inner(self, request, *args, **kwargs): print request return func(request, *args, **kwargs) else: def inner(request, *args, **kwargs): print request return func(request, *args, **kwargs) return inner def __get__(self, obj, type=None): if obj is None: return self new_func = self.func.__get__(obj, type) return self.__class__(new_func, called_on_method=True) |
我提出的部分(特定)解决方案依赖于异常处理。我正试图创建一个装饰器来只允许某些httpRequest方法,但要使它能够同时使用视图和视图方法这两种函数。
所以,这门课会做我想做的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class methods(object): def __init__(self, *_methods): self.methods = _methods def __call__(self, func): @wraps(func) def inner(*args, **kwargs): try: if args[0].method in self.methods: return func(*args, **kwargs) except AttributeError: if args[1].method in self.methods: return func(*args, **kwargs) return HttpResponseMethodNotAllowed(self.methods) return inner |
。
下面是两个用例:修饰函数:
1 2 3 | @methods("GET") def view_func(request, *args, **kwargs): pass |
以及类的修饰方法:
1 2 3 4 5 6 7 | class ViewContainer(object): # ... @methods("GET","PUT") def object(self, request, pk, *args, **kwargs): # stuff that needs a reference to self... pass |
。
是否有比使用异常处理更好的解决方案?
下面是我发现的一种检测修饰可调用是否是函数或方法的一般方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | import functools class decorator(object): def __init__(self, func): self._func = func self._obj = None self._wrapped = None def __call__(self, *args, **kwargs): if not self._wrapped: if self._obj: self._wrapped = self._wrap_method(self._func) self._wrapped = functools.partial(self._wrapped, self._obj) else: self._wrapped = self._wrap_function(self._func) return self._wrapped(*args, **kwargs) def __get__(self, obj, type=None): self._obj = obj return self def _wrap_method(self, method): @functools.wraps(method) def inner(self, *args, **kwargs): print('Method called on {}:'.format(type(self).__name__)) return method(self, *args, **kwargs) return inner def _wrap_function(self, function): @functools.wraps(function) def inner(*args, **kwargs): print('Function called:') return function(*args, **kwargs) return inner |
示例用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class Foo(object): @decorator def foo(self, foo, bar): print(foo, bar) @decorator def foo(foo, bar): print(foo, bar) foo(12, bar=42) # Function called: 12 42 foo(12, 42) # Function called: 12 42 obj = Foo() obj.foo(12, bar=42) # Method called on Foo: 12 42 obj.foo(12, 42) # Method called on Foo: 12 42 |
。