0

这里的 add 和 mul 定义是无意义的,因为它们依赖于返回 self,导致无限循环。如果他们使用 lambdas 创建一个新的发行版,那么它可以正常工作,如下面我自己的回答所示。

我只是在玩类并试图构建一个小型统计工具。但是,当我运行此代码时,我陷入了__mul__调用中正在运行的n1.pdf调用内部的递归循环中,我无法弄清楚原因。我认为这与 Python 懒惰地执行__mul__而不是做我“想要”的事情(让我们用 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)

ps我知道乘以0.5后不再是pdf,这不是问题。

4

3 回答 3

2
class Distribution:
    ...
    def pdf(self, x):
        return self.pdf(x)

pdf()呼唤自己,呼唤自己,呼唤自己……无限。

与 相同cdf()

于 2021-12-27T01:08:46.727 回答
0
def pdf(self, x):
    return self.pdf(x)
    
def cdf(self, x):
    return self.cdf(x)

我假设您的意图是委托给属性。由于它们总是被分配的,它们将被找到(假设您在一个实例上进行查找)而不是类方法(这将直接是没有这些属性的无限递归);但这反过来又意味着这些类方法毫无用处。x.cdf(y),哪里cdf是可调用的实例属性,才有效;也不需要提供方法。

newpdf = lambda x : self.pdf(x) * other
self.pdf = newpdf

我假设您的意图是创建一个依赖于self.pdf. 不幸的是,它不是那样工作的。问题是 lambda 是后期绑定。当它执行时它会查找self.pdf......并找到它自己。

这里有一个单独的问题,因为您正在编写__mul____add__实现 - 即*and+运算符,它们应该返回一个新 value,而不是改变任何一个操作数。(如果你写了a = 3and b = 4then ,如果orc = a * b的值改变了,你会非常惊讶,是吗?)ab

我们可以一次解决这两个问题,只需使用计算pdfcdf创建一个新实例(无论如何我们都需要):

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
于 2021-12-27T01:32:44.770 回答
0

感谢@John 和@Tom 和@bbbbbb 的帮助......问题是试图返回自我而不是创建一个新的发行版。如果我将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

那么这就解决了这个问题

于 2021-12-27T01:15:01.590 回答