Сбой Python pickle при попытке вернуть значение по умолчанию в __getattr__ - PullRequest
6 голосов
/ 20 июня 2011

У меня есть словарь, как класс, который я использую для хранения некоторых значений в качестве атрибутов. Недавно я добавил логику (__getattr__), чтобы вернуть None, если атрибут не существует. Как только я сделал это, рассол рухнул, и я хотел понять почему?

Тестовый код:

import cPickle
class DictionaryLike(object):
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)

    def __iter__(self):
        return iter(self.__dict__)

    def __getitem__(self, key):
        if(self.__dict__.has_key(key)):
            return self.__dict__[key]
        else:
            return None

    ''' This is the culprit...'''    
    def __getattr__(self, key):
        print 'Retreiving Value ' , key
        return self.__getitem__(key)

class SomeClass(object):
    def __init__(self, kwargs={}):
       self.args = DictionaryLike(**kwargs)


someClass = SomeClass()
content = cPickle.dumps(someClass,-1)
print content

Результат:

Retreiving Value  __getnewargs__
Traceback (most recent call last):
  File <<file>> line 29, in <module>
    content = cPickle.dumps(someClass,-1)
TypeError: 'NoneType' object is not callable`

Я сделал что-то глупое? Я читал пост, что deepcopy () может потребовать, чтобы я выбрасывал исключение, если ключ не существует? Если это так, есть ли простой способ достичь того, что я хочу, не выбрасывая исключение?

Конечным результатом является то, что если несколько звонков

someClass.args.i_dont_exist

Я хочу, чтобы он вернул None.

Ответы [ 2 ]

13 голосов
/ 20 июня 2011

Реализация __getattr__ немного сложнее, поскольку она вызывается для каждого несуществующего атрибута.В вашем случае модуль pickle проверяет ваш класс на наличие специального метода __getnewargs__ и получает None, что явно не вызывается.

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

def __getattr__(self, key):
    if key.startswith('__') and key.endswith('__'):
        return super(DictionaryLike, self).__getattr__(key)
    return self.__getitem__(key)

Я обычно пропускаю все имена, начинающиеся с подчеркивания, чтобы обойти магию для внутренних символов.

5 голосов
/ 20 июня 2011

Вам нужно поднять AttributeError, когда атрибут отсутствует в вашем классе:

def __getattr__(self, key):
    i = self.__getitem__(key)
    if i == None:
        raise AttributeError
    return self.__getitem__(key)

Я собираюсь предположить, что это поведение требуется. Из документации по питону для getattr , «Вызывается, когда поиск атрибута не нашел атрибут в обычных местах (т. Е. Он не является атрибутом экземпляра и не найден в дереве классов для себя). имя атрибута. Этот метод должен возвращать (вычисляемое) значение атрибута или вызывать исключение AttributeError. "

Нет никакого способа сообщить pickle и т. Д., Что искомый атрибут не найден, если вы не вызовете исключение. Например, в вашем сообщении об ошибке Pickle ищет специальный вызываемый метод с именем __getnewargs__, Pickle ожидает, что, если исключение AttributeError не найдено, возвращаемое значение будет вызываться.

Полагаю, одна потенциальная работа вокруг вас могла бы попытаться определить все специальные методы, которые pickle ищет как фиктивные методы?

...