Python 使用decorator管理函数内部的属性
这是我现在正在处理的一个较大代码的玩具示例。假设我有一个函数,我想用一种方式来装饰它,我可以记录有效的、遗留的或禁止的参数:Python 使用decorator管理函数内部的属性,python,metaprogramming,python-decorators,Python,Metaprogramming,Python Decorators,这是我现在正在处理的一个较大代码的玩具示例。假设我有一个函数,我想用一种方式来装饰它,我可以记录有效的、遗留的或禁止的参数: @选项(“1”) @选项(“2”,legacy=True) def do_材料(可选): 打印(f“您选择了{opt}!”) 做些什么(“1”) 做些什么(“2”) 做些什么(“3”) 这样做的结果是 You chose 1! 2 is deprecated! You chose 2! 3 is not allowed! 我现在尝试以不同的方式实现此行为,我最新的尝试
@选项(“1”)
@选项(“2”,legacy=True)
def do_材料(可选):
打印(f“您选择了{opt}!”)
做些什么(“1”)
做些什么(“2”)
做些什么(“3”)
这样做的结果是
You chose 1!
2 is deprecated!
You chose 2!
3 is not allowed!
我现在尝试以不同的方式实现此行为,我最新的尝试是:
从functools导入包装
def选项(opt,legacy=False):
def添加选项(f,选项):
如果不是hasattr(f,“选项”):
f、 opts=[]
f、 opts.append(选项)
def添加_遗留_选项(f,选项):
如果不是hasattr(f,“遗产”):
f、 遗产=[]
f、 legacy.append(选项)
def装饰(func):
@包装(func)
def包装器(选件):
如果hasattr(wrapper,“opts”)和选项不在wrapper.opts中:
打印(不允许使用f“{option}!”)
返回
如果hasattr(wrapper,“legacy”)和wrapper.legacy中的选项:
打印(f“{option}已弃用!”)
返回函数(选项)
添加选项(包装器,选项)
如果是遗产:
添加_legacy_opt(包装器,opt)
返回包装器
返修
它几乎起作用了。问题是它给了我双重的传统信息:
You chose 1!
2 is deprecated!
2 is deprecated!
You chose 2!
3 is not allowed!
如果有人有任何想法,请告诉我!谢谢:D
更新:以不同的顺序进行装饰:
@选项(“2”,legacy=True)
@选项(“1”)
def do_材料(可选):
打印(f“您选择了{opt}!”)
您已经在包装器周围创建了一个包装器。wrapps
decorator实际上使用“deeper-down”中的函数属性更新每个包装器,这在这里实际上很关键,但由于每个包装器都保留自己的.opts
和.legacy
(指向相同的列表对象),因此每一层包装器都将打印(f“{option}不推荐使用!”)
然后return func(option)
这只是调用嵌套包装器,它经过相同的检查
因此,请注意:
In [1]: from functools import wraps
...:
...:
...: def option(opt, legacy=False):
...: def add_opt(f, option):
...: if not hasattr(f, "opts"):
...: f.opts = []
...: f.opts.append(option)
...:
...: def add_legacy_opt(f, option):
...: if not hasattr(f, "legacy"):
...: f.legacy = []
...: f.legacy.append(option)
...:
...: def decorate(func):
...: @wraps(func)
...: def wrapper(option):
...: if hasattr(wrapper, "opts") and option not in wrapper.opts:
...: print(f"{option} is not allowed!")
...: return
...: if hasattr(wrapper, "legacy") and option in wrapper.legacy:
...: print(f"{option} is deprecated!")
...: return func(option)
...:
...: add_opt(wrapper, opt)
...: if legacy:
...: add_legacy_opt(wrapper, opt)
...:
...: return wrapper
...:
...: return decorate
...:
In [2]: @option("2", legacy=True)
...: @option("1")
...: def do_stuff(opt):
...: print(f"You chose {opt}!")
...:
In [3]: vars(do_stuff)
Out[3]:
{'__wrapped__': <function __main__.do_stuff(opt)>,
'opts': ['1', '2'],
'legacy': ['2']}
In [4]: vars(do_stuff.__wrapped__)
Out[4]: {'__wrapped__': <function __main__.do_stuff(opt)>, 'opts': ['1', '2']}
然后,我们可以执行以下操作:
def option(opt, legacy=False):
def decorate(func):
if hasattr(func, 'opt') or hasattr(func, 'legacy'): # has been decorated
wrapper = func # keep old wrapper
else: # wrap for the first time
@wraps(func)
def wrapper(option):
if hasattr(wrapper, "opts") and option not in wrapper.opts:
print(f"{option} is not allowed!")
return
if hasattr(wrapper, "legacy") and option in wrapper.legacy:
print(f"{option} is deprecated!")
return func(option)
_add_opt(wrapper, opt)
if legacy:
_add_legacy_opt(wrapper, opt)
return wrapper
return decorate
老实说,这种依赖函数属性的方法似乎非常脆弱。但也许你可以想出一个更稳健的方法,但这至少应该澄清问题并给你一个解决方案
def option(opt, legacy=False):
def decorate(func):
if hasattr(func, 'opt') or hasattr(func, 'legacy'): # has been decorated
wrapper = func # keep old wrapper
else: # wrap for the first time
@wraps(func)
def wrapper(option):
if hasattr(wrapper, "opts") and option not in wrapper.opts:
print(f"{option} is not allowed!")
return
if hasattr(wrapper, "legacy") and option in wrapper.legacy:
print(f"{option} is deprecated!")
return func(option)
_add_opt(wrapper, opt)
if legacy:
_add_legacy_opt(wrapper, opt)
return wrapper
return decorate