簡體   English   中英

不知道為什么我卡在 Python 卡住遞歸循環

[英]Not sure why I'm stuck in a Python stuck recursion loop

這里的 add 和 mul 定義是無意義的,因為它們依賴於返回 self,導致無限循環。 如果他們使用 lambdas 創建一個新的發行版,那么它可以正常工作,如下面我自己的回答所示。

我只是在玩類並試圖構建一個小型統計工具。 但是,當我運行此代碼時,我陷入了n1.pdf調用中的遞歸循環,該調用在__mul__調用中運行,我無法弄清楚原因。 I think it has something to do with Python lazily executing the __mul__ instead of doing what I kind of 'wanted' (let's say in the language of CS) which was to create a new pointer to the old function call for pdf that is owned by將新指針指向 pdf,然后將舊指針(main.pdf 指針)設置為新的 function。

我認為這措辭很糟糕,因此如果您理解我的要求,則非常歡迎進行編輯。

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,這不是問題。

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

pdf()調用自己,調用自己,調用自己......無限。

cdf()相同。

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

我假設您的意圖是創建一個新的 function ,它依賴於self.pdf的現有值。 不幸的是,它不是那樣工作的。 問題是 lambda 是后期綁定 當它執行時它會查找self.pdf ... 並找到它自己。

這里有一個單獨的問題,因為您正在編寫__mul____add__實現 - 即*+運算符,它們應該返回一個新 value ,而不是改變任何一個操作數。 (如果你寫a = 3b = 4然后c = 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)

請注意,實現這些方法還為您提供了增強的賦值運算符*=+= (盡管通過創建新的 object 並重新綁定名稱)。

讓我們測試一下:

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

感謝@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

那么這就解決了這個問題

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM