Ara*_*Fey 3 python class decorator python-decorators
虽然有很多关于使用类作为装饰器的资源,但我还没有找到任何处理装饰方法问题的资源。这个问题的目标是解决这个问题。我将发布我自己的解决方案,但当然也邀请其他所有人发布他们的解决方案。
标准装饰器类实现的问题在于,python 不会创建被装饰函数的绑定方法:
class Deco:
def __init__(self, func):
self.func= func
def __call__(self, *args):
self.func(*args)
class Class:
@Deco
def hello(self):
print('hello world')
Class().hello() # throws TypeError: hello() missing 1 required positional argument: 'self'
Run Code Online (Sandbox Code Playgroud)
方法装饰器需要克服这个障碍。
从前面的示例中获取类,预计以下事情会起作用:
>>> i= Class()
>>> i.hello()
hello world
>>> i.hello
<__main__.Deco object at 0x7f4ae8b518d0>
>>> Class.hello is Class().hello
False
>>> Class().hello is Class().hello
False
>>> i.hello is i.hello
True
Run Code Online (Sandbox Code Playgroud)
理想情况下,函数__doc__
和签名以及类似的属性也被保留。
通常当一个方法被访问为 时some_instance.some_method()
,python 的描述符协议启动并调用some_method.__get__()
,它返回一个绑定的方法。但是,因为该方法已被Deco
类的实例替换,所以不会发生这种情况——因为Deco
它不是一个描述符。为了Deco
按预期工作,它必须实现一个__get__
返回自身绑定副本的方法。
这是基本的“什么都不做”装饰器类:
import inspect
import functools
from copy import copy
class Deco(object):
def __init__(self, func):
self.__self__ = None # "__self__" is also used by bound methods
self.__wrapped__ = func
functools.update_wrapper(self, func)
def __call__(self, *args, **kwargs):
# if bound to an object, pass it as the first argument
if self.__self__ is not None:
args = (self.__self__,) + args
#== change the following line to make the decorator do something ==
return self.__wrapped__(*args, **kwargs)
def __get__(self, instance, owner):
if instance is None:
return self
# create a bound copy
bound = copy(self)
bound.__self__ = instance
# update __doc__ and similar attributes
functools.update_wrapper(bound, self.__wrapped__)
# add the bound instance to the object's dict so that
# __get__ won't be called a 2nd time
setattr(instance, self.__wrapped__.__name__, bound)
return bound
Run Code Online (Sandbox Code Playgroud)
为了让装饰器做一些事情,在__call__
方法中添加你的代码。
这是一个带参数的:
class DecoWithArgs(object):
#== change the constructor's parameters to fit your needs ==
def __init__(self, *args):
self.args = args
self.__wrapped__ = None
self.__self__ = None
def __call__(self, *args, **kwargs):
if self.__wrapped__ is None:
return self.__wrap(*args, **kwargs)
else:
return self.__call_wrapped_function(*args, **kwargs)
def __wrap(self, func):
# update __doc__ and similar attributes
functools.update_wrapper(self, func)
return self
def __call_wrapped_function(self, *args, **kwargs):
# if bound to an object, pass it as the first argument
if self.__self__ is not None:
args = (self.__self__,) + args
#== change the following line to make the decorator do something ==
return self.__wrapped__(*args, **kwargs)
def __get__(self, instance, owner):
if instance is None:
return self
# create a bound copy of this object
bound = copy(self)
bound.__self__ = instance
bound.__wrap(self.__wrapped__)
# add the bound decorator to the object's dict so that
# __get__ won't be called a 2nd time
setattr(instance, self.__wrapped__.__name__, bound)
return bound
Run Code Online (Sandbox Code Playgroud)
像这样的实现让我们可以在方法和函数上使用装饰器,所以我认为它应该被认为是一种很好的做法。
归档时间: |
|
查看次数: |
1811 次 |
最近记录: |