有没有办法编写装饰器,以便以下方法有效?
assert 'z' not in globals()
@my_decorator
def func(x, y):
print z
编辑:从anwser移动
回答hop的“为什么?”:语法糖/ DRY。
这不是关于缓存,而是基于x&的值计算z(和z1,z2,z3,...)。收率
我有很多相关的功能,我不想写
z1, z2, z3=calculate_from(x, y)
在每个单一功能的开头 - 我会在某处弄错。如果这是c我会用cpp做这个(如果这是lisp,我会用宏做这个...),但我想看看装饰者是否可以做同样的事情。
如果它有帮助,我几乎肯定会将装饰器称为“precalculate_z”,它肯定不会成为任何公共API的一部分。
我可能也可以通过使用类基础结构获得类似的效果,但我想看看它是否可以用于原始函数。
答案 0 :(得分:11)
回应Hop的回答
Python没有动态范围的变量,但您可以模拟它。这是一个通过创建全局绑定来模拟它的示例,但在退出时恢复以前的值:
def adds_dynamic_z_decorator(f):
def replacement(*arg,**karg):
# create a new 'z' binding in globals, saving previous
if 'z' in globals():
oldZ = (globals()['z'],)
else:
oldZ = None
try:
globals()['z'] = None
#invoke the original function
res = f(*arg, **karg)
finally:
#restore any old bindings
if oldZ:
globals()['z'] = oldZ[0]
else:
del(globals()['z'])
return res
return replacement
@adds_dynamic_z_decorator
def func(x,y):
print z
def other_recurse(x):
global z
print 'x=%s, z=%s' %(x,z)
recurse(x+1)
print 'x=%s, z=%s' %(x,z)
@adds_dynamic_z_decorator
def recurse(x=0):
global z
z = x
if x < 3:
other_recurse(x)
print 'calling func(1,2)'
func(1,2)
print 'calling recurse()'
recurse()
我对上述代码的实用性或完整性不作任何保证。实际上,我保证 是疯狂的,你应该避免使用它,除非你想从你的Python对手那里鞭打。
此代码类似于eduffy和John Montgomery的代码,但确保'z'被创建并正确恢复“就像”局部变量一样 - 例如,注意'other_recurse'如何能够看到绑定对于'递归'正文中指定的'z'。
答案 1 :(得分:8)
我不知道本地范围,但您可以暂时提供替代的全局名称空间。类似的东西:
import types
def my_decorator(fn):
def decorated(*args,**kw):
my_globals={}
my_globals.update(globals())
my_globals['z']='value of z'
call_fn=types.FunctionType(fn.func_code,my_globals)
return call_fn(*args,**kw)
return decorated
@my_decorator
def func(x, y):
print z
func(0,1)
哪个应该打印“z的值”
答案 2 :(得分:7)
a)不要这样做。
b)说真的,你为什么要这样做?
c)你可以在你的装饰器中声明z为全局,所以z在第一次调用装饰器之后才会出现在globals()中,所以断言不会吠叫。
d)为什么???
答案 3 :(得分:2)
我首先回应“请不要”,但这是你的选择。这是一个适合您的解决方案:
assert 'z' not in globals ()
class my_dec:
def __init__ (self, f):
self.f = f
def __call__ (self,x,y):
z = x+y
self.f(x,y,z)
@my_dec
def func (x,y,z):
print z
func (1,3)
在形式参数中确实需要z
,而不是实际参数。
答案 4 :(得分:1)
我也许可以通过使用类基础结构获得类似的效果,但我想看看它是否可以用于原始函数。
嗯,Python是一种面向对象的语言。在我看来,你应该在课堂上这样做。制作一个好的类接口肯定会简化你的问题。 不是是为什么装饰者制作的。
答案 5 :(得分:1)
明确比隐含更好。
这还不错吗?
def provide_value(f):
f.foo = "Bar"
return f
@provide_value
def g(x):
print g.foo
(如果你真的想要邪恶,分配给f.func_globals似乎很有趣。)
答案 6 :(得分:0)
其他人已经提供了一些制作工作装饰器的方法,许多人建议不要这样做,因为它在风格上与普通的python行为不同,它会让任何试图理解代码的人感到困惑。
如果你需要重新计算一些东西,将它们组合在一个对象中是否有意义?在构造函数中计算z1 ... zN,然后使用这些值的函数可以作为实例的一部分访问预先计算的答案。