2013-11-14 3 views
0

Я пишу класс, подобный этому (упрощенному), чтобы представлять распределения вероятностей. Я хочу инициализировать объект Распространение только с его типом и параметрами и что у него есть некоторые функции, назначенные в соответствии с его типом.TypeError в определении класса

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) 

Теперь, когда я бегу тип (dist.pdf) Я понимаю, что это лямбда-функция, но когда я запускаю функцию, скажем dist.pdf (4.0) в TypeError: «список» объект не является отозваны.

На стороне примечания, как стиль/сложность кода?

ответ

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 словарь как у вас есть. Например, именованные параметры не нужны для функций pdf и cdf, но могут быть полезны для интроспекции. Кроме того, можете ли вы предположить, что любой созданный дистрибутив имеет ровно два метода: pdf и cdf, или у экземпляра есть методы добавления?

+0

спасибо, я проверю эти методы, которые вы упомянули. Я просто хотел иметь класс, функции которого зависят от набора параметров, вещь стала более сложной, потому что параметры, которые мне нужны, зависят от «dist_type», поэтому я использовал словарь. – chuse

-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' 

Стиль код выглядит разумным, хотя я стесняюсь немного, чтобы SetAttr так много в себе. Кроме того, ваши имена переменных могут быть немного более наглядными, но, возможно, это потому, что вы делаете математику, и часто математика использует неправильные имена переменных :)

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" элемента вашего functions Словаря, что случается быть list. Поскольку 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']

Причина трюк со значением аргумента по умолчанию работает так, что она оценивается в момент определения функции, не когда он называется.

Смежные вопросы