不确定为什么我会陷入 Python 卡住的递归循环
Not sure why I'm stuck in a Python stuck recursion loop
这里的add和mul定义是无意义的,因为它们依赖于返回self,导致死循环。如果他们使用 lambda 表达式创建一个新的发行版,那么它就可以正常工作,正如我在下面的回答中所述。
我只是在玩弄 类 并试图构建一个小型统计工具。但是,当我 运行 这段代码时,我陷入了 __mul__
调用中的递归循环中,该循环在 n1.pdf
调用中被 运行 调用,我无法弄清楚原因。我认为这与 Python 懒惰地执行 __mul__
而不是做我想做的 'wanted' (让我们用 CS 的语言来说)是创建一个新指针有关旧函数调用由指向 pdf 的新指针拥有的 pdf,然后将旧指针(主 .pdf 指针)设置为新函数。
我认为这句话的措辞很糟糕,所以如果您理解我的问题,非常欢迎进行编辑。
import math
import random
class Distribution:
def __init__(self, pdf, cdf):
self.pdf = pdf
self.cdf = cdf
def pdf(self, x):
return self.pdf(x)
def cdf(self, x):
return self.cdf(x)
def __mul__(self, other):
if isinstance(other, float) or isinstance(other, int):
newpdf = lambda x : self.pdf(x) * other
self.pdf = newpdf
newcdf = lambda x : self.cdf(x) * other
self.cdf = newcdf
return self
else:
return NotImplemented
def __add__(self, other):
self.pdf = lambda x : self.pdf(x) + other.pdf(x)
self.cdf = lambda x : self.cdf(x) + other.cdf(x)
return Distribution(self.pdf, self.cdf)
class Normal(Distribution):
def __init__(self, mean, stdev):
self.mean = mean
self.stdev = stdev
def pdf(self, x):
return (1.0 / math.sqrt(2 * math.pi * self.stdev ** 2)) * math.exp(-0.5 * (x - self.mean) ** 2 / self.stdev ** 2)
def cdf(self, x):
return (1 + math.erf((x - self.mean) / math.sqrt(2) / self.stdev)) / 2
def sample(self):
return self.mean + self.stdev * math.sqrt(2) * math.cos(2 * math.pi * random.random())
if __name__ == "__main__":
n1 = Normal(1,2)
n1half = n1 * 0.5
x = n1.pdf(1)
print(x)
p.s。我知道它乘以0.5后不再是pdf,这不是问题。
class Distribution:
...
def pdf(self, x):
return self.pdf(x)
pdf()
调用自己,调用自己,调用自己...无限。
与cdf()
相同。
感谢@John 和@Tom 以及@bbbbbb 的帮助...问题是尝试return self 而不是创建新的发行版。如果我将 mul 的 def'n 更改为
def __mul__(self, other):
if isinstance(other, float) or isinstance(other, int):
def newpdf(x):
return self.pdf(x) * other
def newcdf(x):
return self.cdf(x) * other
return Distribution(newpdf, newcdf)
else:
return NotImplemented
那么这个问题就解决了
def pdf(self, x):
return self.pdf(x)
def cdf(self, x):
return self.cdf(x)
我假设您的意图是委托给属性。因为它们总是被赋值,所以它们会被发现(假设你在一个实例上进行查找)而不是 class 方法(如果没有这些属性,这将是无限递归);但这又意味着这些 class 方法毫无用处。 x.cdf(y)
,其中 cdf
是一个可调用的实例属性,可以正常工作;也不需要提供方法。
newpdf = lambda x : self.pdf(x) * other
self.pdf = newpdf
我假设您的意图是创建一个依赖于 self.pdf
的现有值的新函数。不幸的是,它不是那样工作的。问题是 lambda 是 late binding。当它执行时,那个就是它会查找self.pdf
...并找到自己的时间。
这里有一个单独的问题,因为您正在编写 __mul__
和 __add__
实现 - 即 *
和 +
运算符,它们应该是return 一个新值 ,并且 not 改变任一操作数。 (如果你写 a = 3
和 b = 4
然后 c = a * b
,如果 a
或 b
的值发生变化,你会非常惊讶,是吗?)
我们可以一次解决这两个问题,只需使用计算的 pdf
和 cdf
创建一个新实例(无论如何我们都需要):
def __mul__(self, other):
if isinstance(other, float) or isinstance(other, int):
newpdf = lambda x : self.pdf(x) * other
newcdf = lambda x : self.cdf(x) * other
return Distribution(newpdf, newcdf)
else:
return NotImplemented
同样,__add__
应该使用局部变量,而不是修改self
:
def __add__(self, other):
newpdf = lambda x : self.pdf(x) + other.pdf(x)
newcdf = lambda x : self.cdf(x) + other.cdf(x)
return Distribution(newpdf, newcdf)
请注意,实现这些方法还可以为您提供增强的赋值运算符 *=
和 +=
(尽管通过创建新对象并重新绑定名称)。
我们来测试一下:
if __name__ == "__main__":
n1 = Normal(1,2)
n1half = n1 * 0.5
print(n1.pdf(1))
print(n1half.pdf(1))
n1 += n1
print(n1.pdf(1))
我得到:
>py test.py
0.19947114020071635
0.09973557010035818
0.3989422804014327
这里的add和mul定义是无意义的,因为它们依赖于返回self,导致死循环。如果他们使用 lambda 表达式创建一个新的发行版,那么它就可以正常工作,正如我在下面的回答中所述。
我只是在玩弄 类 并试图构建一个小型统计工具。但是,当我 运行 这段代码时,我陷入了 __mul__
调用中的递归循环中,该循环在 n1.pdf
调用中被 运行 调用,我无法弄清楚原因。我认为这与 Python 懒惰地执行 __mul__
而不是做我想做的 'wanted' (让我们用 CS 的语言来说)是创建一个新指针有关旧函数调用由指向 pdf 的新指针拥有的 pdf,然后将旧指针(主 .pdf 指针)设置为新函数。
我认为这句话的措辞很糟糕,所以如果您理解我的问题,非常欢迎进行编辑。
import math
import random
class Distribution:
def __init__(self, pdf, cdf):
self.pdf = pdf
self.cdf = cdf
def pdf(self, x):
return self.pdf(x)
def cdf(self, x):
return self.cdf(x)
def __mul__(self, other):
if isinstance(other, float) or isinstance(other, int):
newpdf = lambda x : self.pdf(x) * other
self.pdf = newpdf
newcdf = lambda x : self.cdf(x) * other
self.cdf = newcdf
return self
else:
return NotImplemented
def __add__(self, other):
self.pdf = lambda x : self.pdf(x) + other.pdf(x)
self.cdf = lambda x : self.cdf(x) + other.cdf(x)
return Distribution(self.pdf, self.cdf)
class Normal(Distribution):
def __init__(self, mean, stdev):
self.mean = mean
self.stdev = stdev
def pdf(self, x):
return (1.0 / math.sqrt(2 * math.pi * self.stdev ** 2)) * math.exp(-0.5 * (x - self.mean) ** 2 / self.stdev ** 2)
def cdf(self, x):
return (1 + math.erf((x - self.mean) / math.sqrt(2) / self.stdev)) / 2
def sample(self):
return self.mean + self.stdev * math.sqrt(2) * math.cos(2 * math.pi * random.random())
if __name__ == "__main__":
n1 = Normal(1,2)
n1half = n1 * 0.5
x = n1.pdf(1)
print(x)
p.s。我知道它乘以0.5后不再是pdf,这不是问题。
class Distribution:
...
def pdf(self, x):
return self.pdf(x)
pdf()
调用自己,调用自己,调用自己...无限。
与cdf()
相同。
感谢@John 和@Tom 以及@bbbbbb 的帮助...问题是尝试return self 而不是创建新的发行版。如果我将 mul 的 def'n 更改为
def __mul__(self, other):
if isinstance(other, float) or isinstance(other, int):
def newpdf(x):
return self.pdf(x) * other
def newcdf(x):
return self.cdf(x) * other
return Distribution(newpdf, newcdf)
else:
return NotImplemented
那么这个问题就解决了
def pdf(self, x):
return self.pdf(x)
def cdf(self, x):
return self.cdf(x)
我假设您的意图是委托给属性。因为它们总是被赋值,所以它们会被发现(假设你在一个实例上进行查找)而不是 class 方法(如果没有这些属性,这将是无限递归);但这又意味着这些 class 方法毫无用处。 x.cdf(y)
,其中 cdf
是一个可调用的实例属性,可以正常工作;也不需要提供方法。
newpdf = lambda x : self.pdf(x) * other
self.pdf = newpdf
我假设您的意图是创建一个依赖于 self.pdf
的现有值的新函数。不幸的是,它不是那样工作的。问题是 lambda 是 late binding。当它执行时,那个就是它会查找self.pdf
...并找到自己的时间。
这里有一个单独的问题,因为您正在编写 __mul__
和 __add__
实现 - 即 *
和 +
运算符,它们应该是return 一个新值 ,并且 not 改变任一操作数。 (如果你写 a = 3
和 b = 4
然后 c = a * b
,如果 a
或 b
的值发生变化,你会非常惊讶,是吗?)
我们可以一次解决这两个问题,只需使用计算的 pdf
和 cdf
创建一个新实例(无论如何我们都需要):
def __mul__(self, other):
if isinstance(other, float) or isinstance(other, int):
newpdf = lambda x : self.pdf(x) * other
newcdf = lambda x : self.cdf(x) * other
return Distribution(newpdf, newcdf)
else:
return NotImplemented
同样,__add__
应该使用局部变量,而不是修改self
:
def __add__(self, other):
newpdf = lambda x : self.pdf(x) + other.pdf(x)
newcdf = lambda x : self.cdf(x) + other.cdf(x)
return Distribution(newpdf, newcdf)
请注意,实现这些方法还可以为您提供增强的赋值运算符 *=
和 +=
(尽管通过创建新对象并重新绑定名称)。
我们来测试一下:
if __name__ == "__main__":
n1 = Normal(1,2)
n1half = n1 * 0.5
print(n1.pdf(1))
print(n1half.pdf(1))
n1 += n1
print(n1.pdf(1))
我得到:
>py test.py
0.19947114020071635
0.09973557010035818
0.3989422804014327