python类方法中如何避免无限递归
这是我的后续问题 我想要一个类来处理一个可以自我更新的函数。这是一个简化的示例,但我仍然以无限递归结束:python类方法中如何避免无限递归,python,class,recursion,Python,Class,Recursion,这是我的后续问题 我想要一个类来处理一个可以自我更新的函数。这是一个简化的示例,但我仍然以无限递归结束: def f(x): return x class func: def __init__(self, f): self.f = f def translate(self, c): def f_(x): return self.f(x + c) self.f = f_ 它只能工作一次: >
def f(x):
return x
class func:
def __init__(self, f):
self.f = f
def translate(self, c):
def f_(x):
return self.f(x + c)
self.f = f_
它只能工作一次:
>>> myfunc = func(f)
>>> myfunc.f(1)
1
>>> myfunc.translate(5)
>>> myfunc(1)
...
RecursionError: maximum recursion depth exceeded
问题是self.f
调用self.f
,如果translate
是在类之外定义的,则不会发生这种情况:
def translate(f, c):
def f_(x):
return f(x+c)
return f_
这项工作:
>>> f = translate(f, 5)
>>> f(1)
6
>>> f = translate(f,-5)
>>>f(1)
1
如何使其在类内工作?只需使用闭包,就像在没有类的情况下一样,在更新
self.f
属性之前获取对原始函数对象的引用:
In [1]: def f(x):
...: return x
...:
...: class func:
...:
...: def __init__(self, f):
...: self.f = f
...:
...: def translate(self, c):
...: f = self.f
...: def f_(x):
...: return f(x + c)
...: self.f = f_
...:
In [2]: myfunc = func(f)
In [3]: myfunc.f(1)
Out[3]: 1
In [4]: myfunc.translate(5)
In [5]: myfunc.f(1)
Out[5]: 6
在更新
self.f
属性之前,只需使用闭包,就像在没有类的情况下一样,获取对原始函数对象的引用:
In [1]: def f(x):
...: return x
...:
...: class func:
...:
...: def __init__(self, f):
...: self.f = f
...:
...: def translate(self, c):
...: f = self.f
...: def f_(x):
...: return f(x + c)
...: self.f = f_
...:
In [2]: myfunc = func(f)
In [3]: myfunc.f(1)
Out[3]: 1
In [4]: myfunc.translate(5)
In [5]: myfunc.f(1)
Out[5]: 6
您遇到了Python名称解析的怪癖。试着这样做:
def translate(self, c):
def f_(x, f=self.f):
return f(x + c)
self.f = f_
我希望我能很好地理解这个问题,能给出一个简洁的解释。粗略的说法是,self.f
总是指向“self的f方法”。当您替换self的f方法时,它指向新函数而不是旧函数。这就是它无限循环的原因
kwargs技巧通过在特殊范围内创建新变量来解决这个问题。
f=self.f
中的f
值包含在函数中,并与此特定函数定义保持一致。定义函数时,将其设置为当前值self.f
。因此,它不会更改为指向函数的循环版本。您遇到了Python名称解析的怪癖。试着这样做:
def translate(self, c):
def f_(x, f=self.f):
return f(x + c)
self.f = f_
我希望我能很好地理解这个问题,能给出一个简洁的解释。粗略的说法是,self.f
总是指向“self的f方法”。当您替换self的f方法时,它指向新函数而不是旧函数。这就是它无限循环的原因
kwargs技巧通过在特殊范围内创建新变量来解决这个问题。
f=self.f
中的f
值包含在函数中,并与此特定函数定义保持一致。定义函数时,将其设置为当前值self.f
。因此,它不会更改为指向函数的循环版本。如果您试图编写与第一个translate
更接近的外部a级translate
:
def f(x):
return x
def translate(c):
global f
def f_(x):
return f(x+c)
f = f_
translate(5)
f(1)
你也会得到一个递归错误。您的Extern-a-classtranslate
之所以有效,是因为它的f_
在不会被覆盖的局部变量中查找f
,而不是在您将要重新绑定到新f_
的属性或全局变量中
让您的translate
方法也查找局部变量:
def translate(self, c):
f = self.f
def f_(self, x):
return f(x+c)
self.f = f_
(顺便说一句,调用此方法足够多次,您将堆积如此多的包装层,无论如何都会达到递归限制。无限期地堆积包装层是一个坏主意。)如果您试图编写一个外部类
translate
更接近您第一次编写translate
的方式:
def f(x):
return x
def translate(c):
global f
def f_(x):
return f(x+c)
f = f_
translate(5)
f(1)
你也会得到一个递归错误。您的Extern-a-classtranslate
之所以有效,是因为它的f_
在不会被覆盖的局部变量中查找f
,而不是在您将要重新绑定到新f_
的属性或全局变量中
让您的translate
方法也查找局部变量:
def translate(self, c):
f = self.f
def f_(self, x):
return f(x+c)
self.f = f_
(顺便说一句,如果调用这个方法的次数足够多,那么你会堆积太多的包装层,以至于达到递归限制。无限期地堆积包装层是个坏主意。)这是一个很有见地的观点。堆叠包装器是可以避免的吗?@Ziofil:在这种情况下,您可以跟踪实例上的偏移量。对于更一般的转换,否取决于您的需求。如果要按顺序应用包装器,可以保留并循环一系列方法。如果需要同时影响“向下”和“向上”的值,则需要更复杂的解决方案(例如
beforeNested
和afterNested
方法)。这是一个很有见地的观点。堆叠包装器是可以避免的吗?@Ziofil:在这种情况下,您可以跟踪实例上的偏移量。对于更一般的转换,否取决于您的需求。如果要按顺序应用包装器,可以保留并循环一系列方法。如果需要同时影响“向下”和“向上”的值,则需要更复杂的解决方案(例如beforeNested
和afterNested
方法)。