Почему __getattribute__ не работает с: TypeError: объект «NoneType» не может быть вызван

Это мой первый вопрос здесь, а также мой первый проект на Python.

Я пытаюсь сохранить экземпляры класса с именем Ip500Device:

class Ip500Device(object):

    list = []
    def __init__(self, shortMac, mac, status, deviceType):
        self.__shortMac =shortMac
        self.__mac=mac
        self.__status=status
        self.__deviceType=deviceType
        self.__nbOfObjects=0
        Ip500Device.list.append(self)    

    def __getattribute__(self, att):
        if att=='hello':
            return 0

Этот первый тест просто «привет», но после этого я хочу получить все атрибуты.

Из другого класса я создаю объект устройств и добавляю их в список:

self.__ip500DevicesLst.append(Ip500Device.Ip500Device(lst[0],lst[1],lst[2],lst[3]))
for abcd in self.__ip500DevicesLst:
       print abcd.__getattribute__('hello')

Но когда я пытаюсь распечатать, программа возвращает это сообщение:

TypeError: 'NoneType' object is not callable

Я не очень хорошо понимаю, как хранить экземпляры классов в Python.


person matcha-tea    schedule 29.12.2016    source источник
comment
Нам нужно будет угадать, что такое __ip500DevicesLst.   -  person Peter Wood    schedule 29.12.2016
comment
В ОП совершенно четко указано, что __ip500DevicesLst - это список. Однако это не относится к вопросу о том, почему вызов __getattribute__ вызывает ошибку. ОП предоставил достаточно информации для ответа на этот вопрос, поэтому я думаю, что вопрос следует открыть заново.   -  person ekhumoro    schedule 01.01.2017
comment
Похоже, что один из элементов в списке — None. Не уверен, что это происходит из-за вызова метода, который вы показали для добавления в список, или он уже содержит None. В любом случае попробуйте проверить, что содержимое списка соответствует ожиданиям.   -  person Basic    schedule 01.01.2017
comment
@Базовый. Нет: __getattribute__ вызывается безоговорочно, поэтому очень важно вызывать метод базового класса для необработанных атрибутов. Код OP этого не делает, поэтому он просто возвращает None при попытке доступа к самому атрибуту __getattribute__ — отсюда и ошибка.   -  person ekhumoro    schedule 01.01.2017
comment
@ekhumoro Да, ты прав. Хороший улов. Вызываемый __getatribute__   -  person Basic    schedule 01.01.2017
comment
Примечание: вы почти никогда не захотите переопределять __getattribute__. Если вам просто нужен обработчик неопределенных имен, определите __getattr__ (который вызывается, только если имя не найдено), а не __getattribute__ (который вызывается безоговорочно). В любом случае вы хотите получить доступ как obj.hello, а не явно вызывать специальный метод (что противоречит цели определения специального метода, который должен иметь специальный обработчик для поиска стандартного атрибута). Оба специальных метода должны поднимать AttributeError, когда нет возвращаемого значения, а не молча, неявно возвращая None.   -  person ShadowRanger    schedule 01.01.2017


Ответы (2)


Ошибка возникает из-за того, что __getattribute__ вызывается для всех атрибутов, а вы определили, что он возвращает None для всего, кроме "hello". Поскольку __getattribute__ сам по себе является атрибутом, при попытке вызвать его вы получите TypeError.

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

>>> class Ip500Device(object):
...     def __getattribute__(self, att):
...         print('getattribute: %r' % att)
...         if att == 'hello':
...             return 0
...         return super(Ip500Device, self).__getattribute__(att)
...
>>> abcd = Ip500Device()
>>> abcd.__getattribute__('hello')
getattribute: '__getattribute__'
getattribute: 'hello'
0

Однако лучше определить __getattr__, так как это вызывается только для атрибутов, которые еще не существуют:

>>> class Ip500Device(object):
...     def __getattr__(self, att):
...         print('getattr: %r' % att)
...         if att == 'hello':
...             return 0
...         raise AttributeError(att)
...
>>> abcd = Ip500Device()
>>> abcd.hello
getattr: 'hello'
0
>>> abcd.foo = 10
>>> abcd.foo
10

Наконец, обратите внимание, что если все, что вы хотите сделать, это получить доступ к атрибутам по имени, вы можете использовать встроенный getattr:

>>> class Ip500Device(object): pass
...
>>> abcd = Ip500Device()
>>> abcd.foo = 10
>>> getattr(abcd, 'foo')
10
person ekhumoro    schedule 01.01.2017

print abcd.__getattribute__('hello')

abcd.__getattribute__ не является методом __getattribute__. Когда вы пытаетесь оценить abcd.__getattribute__, вы на самом деле звоните

type(abcd).__getattribute__(abcd, '__getattribute__')

который возвращает None, который вы затем пытаетесь вызвать, как если бы это был метод.

person user2357112 supports Monica    schedule 01.01.2017