Python,如何添加另一个装饰器来过滤现有多装饰器的输出与python中的属性?
Posted
技术标签:
【中文标题】Python,如何添加另一个装饰器来过滤现有多装饰器的输出与python中的属性?【英文标题】:Python, How to add another decorator to filter the output of the existing multi decorators with property in python? 【发布时间】:2019-09-20 22:32:31 【问题描述】:我有 2 个现有的装饰器可以在 python 中运行,@property 和 @safe_property。这些装饰器无法更改,它们是我无权访问的代码的一部分。
def safe_property(original_property):
def wrap(self):
try:
return original_property(self)
except AttributeError as e:
pass
return wrap
class MyClass(object):
def __init__(self):
pass
@property
@safe_property
def do_func(self):
print("inside do_func!")
return [2,3,4]
通过调用函数:
a = MyClass()
print(a.do_func)
输出对我有好处!:
inside do_func!
[2, 3, 4]
现在,另一个特性来了,我试图根据 (可选) 附加参数过滤掉 do_func 的一些返回值。这意味着一些用户可以像往常一样继续工作并调用:
print(a.do_func)
而其他人可以使用过滤器调用:
print(a.do_func(True))
为了尝试这个,我创建了另一个名为 my_decorator 的装饰器,例如:
def my_decorator(*args, **kwargs):
print(args)
print(kwargs)
def wrapper(*args):
print(args)
if args[1] == True:
return
# how do I return filter?
else:
return #without the filter?
return wrapper
class MyClass(object):
def __init__(self):
pass
@my_decorator
@property
@safe_property
def do_func(self):
print("inside do_func!")
return [2,3,4]
这个功能的当前输出是:
(<property object at 0x02AF0090>,)
(<__main__.MyClass object at 0x00BCDBB0>, True)
None
我怎样才能只过滤奇数**例如,**的返回列表来自: do_func?
谢谢
【问题讨论】:
问题在于如何嵌套装饰器。@property
应该是最顶层的装饰器,如果你的其他装饰器处理函数。 property()
将函数转换为其他东西(属性;这是一个描述符),因此任何 above 的装饰器都需要处理属性而不是函数。
【参考方案1】:
您正在将您的装饰器应用于@property
装饰器的输出。该装饰器生成property()
object,而不是函数。这是因为装饰器是从函数定义中向外应用的;见我的answer on decorator execution order;所以首先应用@safe_property
,然后是@property
,然后是@my_decorator
。
如果你想装饰 getter 函数,请将你的装饰器放在 def
语句的正上方,它将首先执行,无论你的装饰器返回什么都将传递给 safe_property()
装饰器(它添加了自己的包装器功能):
@property
@safe_property
@my_decorator
def do_func(self):
print("inside do_func!")
return [2,3,4]
或者,看到@safe_property
也产生一个适合作为getter 函数的包装函数,您可以将装饰器放在@safe_property
和@property
行之间以包装返回前者的包装函数:
@property
@my_decorator
@safe_property
def do_func(self):
print("inside do_func!")
return [2,3,4]
无论哪种方式,您的装饰器包装器都会传递可调用来装饰,并且应该返回一个替换。属性获取器只使用self
,您的替换也将使用self
调用,并且没有其他参数:
def my_decorator(func):
def wrapper(self): # a replacement getter function, so only self is passed in
result = func(self) # call the original getter
if self.some_flag: # you can access the instance in the wrapper
# return only odd values from the getter
return [i for i in result if i % 2]
else:
# otherwise return the values unchanged
return result
return wrapper
将@my_decorator
放在顶部是装饰property()
对象,而不是函数,因此您需要专门处理被传递的此类对象(您可以查看@property
装饰器在@ 中的工作方式987654323@)。
例如您可以从property().fget
属性中提取 getter,然后返回适当的替换(这将是另一个property()
对象):
def my_decorator(prop):
getter = prop.fget
def wrapper(self): # a replacement getter function, so only self is passed in
result = getter(self) # call the original getter, taken from the property
if self.some_flag: # you can access the instance in the wrapper
# return only odd values from the getter
return [i for i in result if i % 2]
else:
# otherwise return the values unchanged
return result
# return a new property object, with the wrapper as the getter function
# and copying across all other property attributes
return property(wrapper, prop.fset, prop.fdel, prop.doc)
请注意,属性 getter
函数只会被传递 self
,没有其他可能的属性获取器参数。
但是,直接处理 property
对象并没有比将装饰器降低一行有任何优势,它只会使事情复杂化,因为必须添加 prop.fget
引用和 property(...)
返回值。
【讨论】:
谢谢,我正在尝试了解您的解决方案,我正在执行您的代码,当我尝试发送 时,您的代码中的 some_flag 是什么print(a.do_func(True)) @raptor0102 这是实例上的示例属性。它不存在,我只是想表明您可以访问实例本身。 谢谢,你帮我解决了我的问题,现在代码可以正常工作了。感谢您的努力以上是关于Python,如何添加另一个装饰器来过滤现有多装饰器的输出与python中的属性?的主要内容,如果未能解决你的问题,请参考以下文章
你如何使用 Flask-Login 和自定义 Python 装饰器来分配用户权限?