0

我正在写一个像这样(简化)的类来表示概率分布。我想要的是仅使用其类型和参数初始化对象 Distribution,并且它具有根据其类型分配的一些功能。

functions = {'exp':{
                 'parameters': ['l'],
                 'pdf': lambda x,p: exp(x/p[0])*p[0],
                 'cdf': lambda x,p: 1-exp(x/p[0]) },
             'uniform':{
                 'parameters': ['x1','x2'],
                 'pdf': lambda x,p: 1/(p[1]-p[0]),
                 'cdf': lambda x,p: (x-p[0])/(p[1]-p[0]) }
            }
class Distribution:
    def __init__(self,dist_type,**parameters):
        self.dist_type = dist_type
        self.parameters = parameters
        self.p = [ self.parameters[z] for z in functions[dist_type]['parameters'] ]

        for key,val in functions[dist_type].items():
            if key == 'parameters':
                pass
            else
                setattr(self, key, lambda x:val(x,self.p))


dist = Distribution('exp',l=3.5)

现在,当我运行 type(dist.pdf) 时,我得到它是一个 lambda 函数,但是当我运行该函数时,说 dist.pdf(4.0) 一个 TypeError: 'list' object is not a callable。

在旁注中,代码的样式/复杂性如何?

4

3 回答 3

2

你是关闭的受害者。这是一个可能的修复:

    for key,val in functions[dist_type].items():
        if key == 'parameters':
            pass
        else:
            def f(x, dist=val):
                return dist(x, self.p)
            setattr(self, key, f)

偶然地,val在此迭代期间的最后一个值是"parameters"您的 dict 的元素functions,它恰好是 a list。因为val被 lambda 捕获,所以稍后当您调用它时,它会尝试调用['l'](4.5, 3.5)-['l']作为"parameters"项目的值。

这可以用这个片段来证明:

class Distribution:
    def __init__(self,dist_type,**parameters):
        self.dist_type = dist_type
        self.parameters = parameters
        self.p = [self.parameters[z] for z in functions[dist_type]['parameters']]

        for key,val in functions[dist_type].items():
            if key == 'parameters':
                pass
            else:
                setattr(self, key, lambda x: val)


dist = Distribution('exp',l=3.5)

print dist.pdf(4.0)

输出:

['l']

使用参数默认值的技巧起作用的原因是它在函数定义时被评估,而不是在被调用时。

于 2013-11-14T16:54:58.627 回答
1

您被 Python 中的范围界定问题所困扰。在这一行:

setattr(self, key, lambda x:val(x,self.p))

vallambda只是在最终调用时查找的名称,而不是在lambda定义时引用的对象。一种解决方法是添加第二个参数,该参数具有在定义时绑定的默认值:

for key,val in functions[dist_type].items():
    if key == 'parameters':
        pass
    else:
        setattr(self, key, lambda x,val=val: val(x,self.p))

我不知道我会尝试像您尝试的那样将子类定义嵌入字典中。有几种可用的技术(类工厂、元类)可以生成一个适当的类,例如

exp_dist = make_distribution('exp')
d = exp_dist(l=3.5)
d.pdf(4.0)

这种解决方案的复杂性取决于您的用例,以及您选择functions按照您的方式构建字典的原因。例如,命名参数对于pdfandcdf函数不是必需的,但可能对自省有用。此外,您是否可以假设创建的任何发行版都具有两个方法,pdf并且cdf,或者该实例也可以具有附加方法?

于 2013-11-14T16:43:50.673 回答
-3

当我尝试运行 dist.pdf() 时,我得到:

Traceback (most recent call last):
  File "./dist-pdf", line 30, in <module>
    dist.pdf()
TypeError: <lambda>() missing 1 required positional argument: 'x'

代码风格看起来很合理,尽管我有点犹豫是否将这么多设置为 self. 此外,您的变量名可能更具描述性,但也许那是因为您正在做数学,而且数学经常使用糟糕的变量名:)

于 2013-11-14T16:48:49.487 回答