Ошибка типа: объект 'classname' не может быть вызван - PullRequest
0 голосов
/ 05 апреля 2019

Я работаю над классом для работы с наборами данных "x, y". Данные обычно поступают из текстовых файлов, где первый столбец данных хранится в «x», а второй столбец хранится в «y».

Я добавляю некоторую функциональность «подгонка кривой» к классу. И я получаю ошибку в заголовке этого поста.

Вот класс:

class XY(object):
    import matplotlib.pyplot as plt
    from scipy.optimize import curve_fit
    import numpy as np
    def __init__(self,f=None):
        self.file(f)
        self.read()
        return
    def file(self,f=None):
        self.filename=self.get_filename(f)
        return
    def filename(self):
        return self.filename
    def get_filename(self,f):
        if(type(f)==str):
            filename=f
        elif(type(f)==file):
            filename=f.name
        else:
            filename=None
        return filename
    def read(self,f=None):
        if(f is None):
            if(self.filename is None):
                return
            else:   # Use internal filename
                filename=self.filename
        else:   # Change/set internal filename
            self.filename=self.get_filename(f)
            filename=self.filename
        data=[]
        try:
            with open(filename,'r') as F:
                for line in F:
                    data.append(line.split())
        except IOError as e:
            print("%s"%e)
            return
        F.close()
        for r in range(0,len(data)):
            for c in range(0,len(data[r])):
                data[r][c]=float(data[r][c])
        self.data=data
        self.x=[self.data[i][0] for i in range(0,len(self.data))]
        self.y=[self.data[i][1] for i in range(0,len(self.data))]
        return self.data
    def f00(self,x,a,b,c,d):
        return a*x**b*self.np.exp(-c*x)+d
    def cf00(self):
        popt,pcov=self.curve_fit(self.f00,self.x,self.y)
        self.y=self.f00(self.x,*popt)
        return self.y

Я вставляю класс в python в интерактивном режиме. А затем попробуйте следующее:

$ python
Python 2.7.14 (default, Oct 31 2017, 21:12:13)
[GCC 6.4.0] on cygwin
Type "help", "copyright", "credits" or "license" for more information.
>>>

Я вставляю в класс, показанный выше, а затем попробуйте следующее:

>>> xy=XY("rot-03-05.dat")
>>> len(xy.x)
220
>>> len(xy.y)
220
>>> xy.cf00()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 56, in cf00
  File "/usr/lib/python2.7/site-packages/scipy/optimize/minpack.py", line 751, in curve_fit
    res = leastsq(func, p0, Dfun=jac, full_output=1, **kwargs)
  File "/usr/lib/python2.7/site-packages/scipy/optimize/minpack.py", line 383, in leastsq
    shape, dtype = _check_func('leastsq', 'func', func, x0, args, n)
  File "/usr/lib/python2.7/site-packages/scipy/optimize/minpack.py", line 27, in _check_func
    res = atleast_1d(thefunc(*((x0[:numinputs],) + args)))
  File "/usr/lib/python2.7/site-packages/scipy/optimize/minpack.py", line 463, in func_wrapped
    return func(xdata, *params) - ydata
TypeError: 'XY' object is not callable
>>> xy.cf00
<bound method XY.cf00 of <__main__.XY object at 0x6ff5ea25fd0>>

Я пытался взять self из f00() и cf00(). Не сработало.

Я попытался позвонить f00() из моего экземпляра, и это работает:

>>> xy=XY()
>>> xy.f00(1,1,1,1,1)
1.3678794411714423

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

Вот отдельные функции, которые работают, когда они не являются частью класса:

def f00(x,a,b,c,d): return a*x**b*np.exp(-c*x)+d
def cf00(x,y):
    popt,pcov=curve_fit(f00,x,y,maxfev=1200000)
    return f00(x,*popt)

y1=cf00(x,y)

Нет проблем.

Ответы [ 2 ]

2 голосов
/ 05 апреля 2019

Таким образом, вы импортируете from scipy.optimize import curve_fit в определение класса, и python связывает это имя с пространством имен класса, поэтому, когда вы вызываете self.curve_fit, оно работает, но имейте в виду, что функция curve_fit определена внутри scipy .

Когда вы звоните self.method(), на самом деле происходит следующее: type(self).method(self), поэтому при вызове self.curve_fit он передает класс XY в качестве первого параметра и ожидает функцию.

Чтобы решить эту проблему, я рекомендую поместить импорт в самый верх файла и просто вызвать curve_fit без self

0 голосов
/ 05 апреля 2019

Это большой блок кода, но я обрисовал, что все должно быть исправлено в вашем коде, включая комментарии:

import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
import numpy as np
from io import TextIOWrapper # to check file-handle type
# Import modules at the global level, makes life easier

class XY(object):

    def __init__(self,f=None):
        self.filename = self.get_filename(f)
        self.data, self.x, self.y = self.read()
        # Don't return from __init__
        # return

    # This function is unnecessary
    # def file(self,f=None):
    #     self.filename=self.get_filename(f)
    #     return

    # This doesn't do anything, just access the attribute like instance.filename
    # def filename(self):
    #     return self.filename

    def get_filename(self,f):
        if isinstance(f, str): # use isinstance for type checking
            filename=f
        elif isinstance(f, TextIOWrapper):
            filename=f.name
        else:
            filename=None # I'd probably raise an exception here
        return filename


    def read(self): # the f param wasn't used otherwise
        # The whole block here was just a repeat of self.get_filename, no
        # need for it
        data=[]
        try:
            with open(filename,'r') as F:
                for line in F:
                    data.append(line.split())
        except IOError as e:
            # I would just let the exception happen and raise it
            print("%s"%e)
            raise
            # return # Don't just return None, you'll get weird behavior
        # F is already closed, that's what with does
        # F.close()
        for r in range(0,len(data)):
            for c in range(0,len(data[r])):
                data[r][c]=float(data[r][c])

        x = [self.data[i][0] for i in range(0,len(self.data))]
        y = [self.data[i][1] for i in range(0,len(self.data))]
        return data, x, y # It's generally bad form to assign self.attr outside of __init__

    def f00(self,x,a,b,c,d):
        return a*x**b*np.exp(-c*x)+d

    def cf00(self):
        popt, pcov = curve_fit(self.f00, self.x, self.y) # you don't need self.curve fit since it's now globally imported
        self.y=self.f00(self.x, *popt)
        return self.y # Not sure you need to return this, since you can just access it after the function is called

Для дальнейшего использования лучше всего включить только то, что необходимо для воспроизведения проблемы, с которой вы столкнулись, и ничего более. Это значительно облегчает выявление проблемы. Минимальный рабочий пример может выглядеть следующим образом:

import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
import numpy as np

def XY(object):
    def __init__(self):
        self.x = [float(a) for a in range(100)]
        self.y = [float(a) for a in range(100)]
        self.data = list(zip(self.x, self.y))

    def f00(self,x,a,b,c,d):
        return a*x**b*np.exp(-c*x)+d

    def cf00(self):
        popt, pcov = curve_fit(self.f00, self.x, self.y)
        return self.f00(self.x, *popt)

Гораздо проще определить, что происходит

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...