类定义中的TypeError

时间:2013-11-14 16:33:35

标签: python

我正在编写一个这样的类(简化)来表示概率分布。 我想要的是仅使用其类型和参数初始化对象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'对象不是可调用的。

另一方面,代码的样式/复杂程度如何?

3 个答案:

答案 0 :(得分: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元素,恰好是list。由于lambda会捕获val,因此稍后当您调用它时,它会尝试调用['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']

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

答案 1 :(得分:1)

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

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

val只是在lambda最终调用时查找的名称,而不是在定义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字典的原因。例如,pdfcdf函数不需要命名参数,但可能对内省有用。另外,您是否可以假设创建的任何分发都有两种方法pdfcdf,或者实例也可以使用其他方法?

答案 2 :(得分:-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'

代码风格看起来很合理,虽然我犹豫了一下,但仍然犹豫不决。此外,您的变量名称可能更具描述性,但可能是因为您正在进行数学运算,并且数学通常使用较差的变量名称:)