Неожиданное назначение объекта - PullRequest
0 голосов
/ 09 февраля 2010
class TrafficData(object):
    def __init__(self):
        self.__data = {}
    def __getitem__(self, epoch):
        if not isinstance(epoch, int):
            raise TypeError()
        return self.__data.setdefault(epoch, ProcessTraffic())
    def __iadd__(self, other):
        for epoch, traffic in other.iteritems():

            # these work
            #existing = self[epoch]
            #existing += traffic

            # this does not
            self[epoch] += traffic # here the exception is thrown

        return self

В приведенном выше урезанном коде я не ожидаю присвоения элемента, но, очевидно, он происходит в отмеченной строке и выдает следующее исключение:

  File "nethogs2.py", line 130, in __iadd__
    self[epoch] += traffic
TypeError: 'TrafficData' object does not support item assignment

Однако, если я вместо этого использую предыдущие 2 закомментированные строки, исключение не выдается.

На мой взгляд, 2 должны вести себя одинаково. self[epoch] возвращает ссылку на объект, и он изменяется на месте через эти объекты __iadd__. Что я тут недопонимаю? Я часто сталкиваюсь с этой проблемой при использовании словарей.

Update0

Вероятно, стоит указать, что для значений в self.__data определено __iadd__, но не __add__, и я бы предпочел изменить это значение, если это возможно. Я также хотел бы избежать создания метода __setitem__.

Update1

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

class Value(object):
    def __init__(self, initial=0):
        self.a = initial
    def __iadd__(self, other):
        self.a += other
        return self
    def __str__(self):
        return str(self.a)

class Blah(object):
    def __init__(self):
        self.__data = {}
    def __getitem__(self, key):
        return self.__data.setdefault(key, Value())

a = Blah()
b = a[1]
b += 1
print a[1]
a[1] += 2
print a[1]

Ответы [ 3 ]

6 голосов
/ 09 февраля 2010

Что вы точно делаете в:

self[epoch] += traffic

есть:

self[epoch] = self[epoch] + traffic

Но вы не определили __setitem__ метод, так что вы можете сделать это самостоятельно.

Вам также необходимо:

def __setitem__(self, epoch, value):
        self.__data[epoch] = value

или что-то подобное.

1 голос
/ 09 февраля 2010

Вероятно, стоит отметить, что значения в self.__data имеют __iadd__ определено, но не __add__, и я бы предпочел изменить значение в место, если это возможно.

Чтобы добавить некоторую точность к предыдущим ответам, при описанных вами обстоятельствах self[epoch] += traffic точно соответствует:

self[epoch] = self[epoch].__iadd__(traffic)

Так что, если вам нужны только побочные эффекты __iadd__, без части назначения предмета, ваш выбор ограничен обходным решением, которое вы уже определили в комментариях в коде, который вы опубликовали, или по телефону __iadd__ самостоятельно - возможно, через модуль operator, хотя я считаю, что operator.__iadd__(self[epoch], traffic) не имеет добавленной стоимости по сравнению с более простым self[epoch].__iadd__(traffic) (когда self[epoch] имеет __iadd__ метод).

0 голосов
/ 09 февраля 2010

код:

self[epoch] += traffic

Является синтаксическим сахаром для:

self[epoch] = self[epoch] + traffic

Так что назначение не является неожиданным, это назначение в +=. Поэтому вам также необходимо переопределить метод __setitem__().

...