Доступ к методу с помощью getattr

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

class DataProject(object):    
    def __init__(self, name=none,input_file=None,datamode=None,comments=None,readnow=True):
        ..............
        # here's where I'm trying to call the method in the other class
        if type(input_file) == str:
            self.input_file_format = self.input_file.split(".")[-1]
            if readnow:
                getattr(Analysis(),'read_'+self.input_file_format)(self,input_file)

class Analysis(object):
    def __init__(self):
        pass # nothing happens here atm

    def read_xlsx(self,parent,input_file):
        """Method to parse xlsx files and dump them into a DataFrame"""
        xl = pd.ExcelFile(input_file)
        for s in sheet_names:
            parent.data[s]=xl.parse(s)

Я получаю NameError: global name 'read_xlsx' is not defined когда я запустил это с afile.xlxs в качестве входных данных, что заставило меня подумать, что я только что обнаружил огромную дыру в моих знаниях Python (не то, чтобы их было немного, но их трудно увидеть, вроде больших лесов...)).

Я бы подумал что getattr(Analysis(), ... ) получит доступ к глобальному пространству имен, в котором он найдет класс Analysis и его методы. И на самом деле print(globals().keys()) показывает, что анализ является частью этого:

['plt', 'mlab', '__builtins__', '__file__', 'pylab', 'DataProject', 'matplotlib', '__package__', 'W32', 'Helpers', 'time', 'pd', 'pyplot', 'np', '__name__', 'dt', 'Analysis', '__doc__']

Что мне здесь не хватает?

РЕДАКТИРОВАТЬ:

Полный возвратный путь:

Traceback (most recent call last):
  File "C:\MPython\dataAnalysis\dataAnalysis.py", line 101, in <module>
    a=DataProject(input_file='C:\\MPython\\dataAnalysis\\EnergyAnalysis\\afile.xlxs',readnow=True)
  File "C:\MPython\dataAnalysis\dataAnalysis.py", line 73, in __init__
    getattr(Analysis(),'read_'+self.input_file_format)(self,input_file)
  File "C:\MPython\dataAnalysis\dataAnalysis.py", line 90, in read_xls
    read_xlsx(input_file)
NameError: global name 'read_xlsx' is not defined

Мой главный звонок:

if __name__=="__main__":
    a=DataProject(input_file='C:\\MPython\\dataAnalysis\\EnergyAnalysis\\afile.xlx',readnow=True)

2 ответа

Решение

Из полной трассировки кажется, что ваш DataProject класс вызывает (успешно) Analysys.read_xls метод, который в свою очередь пытается вызвать read_xlsx, Однако он вызывает его как глобальную функцию, а не как метод.

Вероятно, вам просто нужно заменить код в строке 90, повернув read_xlsx(input_file) в self.read_xlsx(input_file)хотя вам может потребоваться передать дополнительный параметр для родителя DataProject экземпляр тоже.

getattr() работает так, как вы описали это в Python2.x и Python3.x. Ошибка должна быть где-то еще.

Эта модификация вашего кода (ни одна из основных логик не изменена) работает нормально, например:

class DataProject(object):    
    def __init__(self, name="myname",input_file="xlsx",datamode=None,comments=None,readnow=True):
        if type(input_file) == str:
            self.input_file_format = input_file.split(".")[-1]
            if readnow:
                getattr(Analysis(),'read_'+self.input_file_format)(self,input_file)

class Analysis(object):
    def __init__(self):
        pass # nothing happens here atm

    def read_xlsx(self,parent,input_file):
        """Method to parse xlsx files and dumpt them into a DataFrame"""
        print("hello")

a=DataProject()

Выход:

$ python3 testfn.py
hello

Зачем использовать getattr() таким образом, обычно плохая идея

Как вы используете getattr вызывает соглашение об именах для ваших методов (read_someformat). Наименование ваших методов не должно быть основной частью логики вашей программы. - Вы всегда должны иметь возможность изменять имя функции при каждом вызове и определении этой функции и оставлять поведение программы без изменений.

Если формат файла должен обрабатываться определенным методом, эту логику следует делегировать некоторому модулю (например, функции), ответственному за это. Один из способов (есть другие) сделать это - иметь функцию, которая принимает входные данные и решает, какая функция должна их обрабатывать:

def read_file(self,file,format):
    if format == `xls`:
        self.read_xls(file)
    if format == `csv`:
        self.read_csv(file)

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

Другие вопросы по тегам