我有一个问题可以简化如下:我有一组特定的对象,我想以特定的方式修改。因此,我可以编写一个修改单个对象的函数,然后创建一个将该函数应用于集合中所有对象的装饰器。
所以,我想我有类似的东西:
def modify_all(f):
def fun(objs):
for o in objs:
f(o)
@modify_all
def modify(obj):
# Modify obj in some way.
modify(all_my_objs)
但是,有时我可能只想对一个对象进行操作。
有没有办法以编程方式“解开”modify
函数来重新获得原始(单个对象)函数? (我的意思是,不仅仅是删除装饰器。)或者是否有另一种方法可以更好地用于这种情况?
为了清晰和完整,实际 modify_all
装饰器中的内容比此处所示的要多得多。 modify
和modify_all
都有一定的工作要做,这就是为什么我认为装饰者可能会很好。此外,我还有modify
的其他变体可以直接从modify_all
中受益,这使它更有用。但我有时需要使用原始的modify
函数来做事情。我知道我总是可以传递一个单元素集来“欺骗”它按原样工作,但我想知道是否有更好的方法或更好的设计。
答案 0 :(得分:2)
手动将装饰器应用于该功能一次,并将结果保存为新名称。
def modify_one(obj):
...
modify_some = modify_all(modify_one)
modify_some([a, b, c])
modify_one(d)
答案 1 :(得分:2)
装饰器适用于以特定形式应用高阶函数(HOF)
def f ...
f = HOF(f)
在这种情况下,语法(具有相同的语义)
@HOF
def f ...
更简洁,并立即警告读者f
永远不会被“裸露”使用。
对于你的用例,你需要“裸f”和“装饰f”,它们必须有两个不同的名称,因此装饰器语法不能立即应用 - 但它们都不是必需的!一个很好的选择可能是将装饰名称用作装饰函数的属性,例如:
def modify(obj): ...
modify.all = modify_all(modify)
现在,您可以随时致电modify(justone)
和modify.all(allofthem)
并快速编码。
答案 2 :(得分:1)
def modify_all(f):
def fun(objs):
for o in objs:
f(o)
fun.unmodified = f
return fun
def undecorate(f):
return f.unmodified
@modify_all
def modify(obj):
# Modify obj in some way.
modify(all_my_objs)
undecorate(modify)(one_obj)
您只需致电undecorate
即可访问已修饰的功能。
另一种选择是像这样处理它:
def modify_one(modify, one):
return modify.unmodified(one)
答案 3 :(得分:0)
您可以使用装饰器来应用Alex的想法
>>> def forall(f):
... def fun(objs):
... for o in objs:
... f(o)
... f.forall=fun
... return f
...
>>> @forall
... def modify(obj):
... print "modified ", obj
...
>>> modify("foobar")
modified foobar
>>> modify.forall("foobar")
modified f
modified o
modified o
modified b
modified a
modified r
或许foreach
是一个更好的名字
答案 4 :(得分:-1)
在您的特定情况下,我会考虑使用参数解包。只需稍微修改现有代码即可完成此操作:
def broadcast(f):
def fun(*objs):
for o in objs:
f(o)
return fun
@broadcast
def modify(obj):
# Modify obj in some way.
modify(*all_my_objs)
modify(my_obj)
但是,参数解包的操作确实需要一些时间,所以这可能比刚刚通过普通旧列表的原始版本稍慢一些。如果您担心这会使您的应用程序放慢太多,请尝试一下,看看它有什么不同。
根据您使用的对象类型,另一个选项是让装饰器“智能地”决定它是否应该使用循环:
def opt_broadcast(f):
def fun(obj):
if isinstance(obj, list):
for o in objs:
f(o)
else:
f(o)
return fun
@opt_broadcast
def modify(obj):
# Modify obj in some way.
modify(all_my_objs)
modify(my_obj)
但是,当你这样做时,有一些混淆的可能性,如果你希望它作为一个对象在列表上运行时,如果你曾用列表调用modify
,那么它显然不起作用。 (换句话说,如果my_obj
可能是一个列表,请不要这样做)