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-class
translate
之所以有效,是因为它的
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-class
translate
之所以有效,是因为它的
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
方法)。