【问题标题】:Unexpected object assignment意外的对象分配
【发布时间】:2023-03-11 02:58:02
【问题描述】:
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__ 对其进行了适当的修改。我在这里有什么误解?我在使用字典时经常遇到这个问题。

更新0

可能值得指出的是self.__data 中的值定义了__iadd__,但没有定义__add__,如果可能的话,我更愿意修改该值。我还想避免创建__setitem__ 方法。

更新1

下面是一个演示问题的测试用例,我将上面的代码留作现有答案。

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]

【问题讨论】:

    标签: python variable-assignment dictionary


    【解决方案1】:

    你到底在做什么:

    self[epoch] += traffic
    

    是:

    self[epoch] = self[epoch] + traffic
    

    但是你还没有定义__setitem__ 方法,所以你可以自己做。

    你还需要:

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

    或类似的东西。

    【讨论】:

    【解决方案2】:

    可能值得指出的是 self.__data 中的值有 __iadd__ 已定义,但未定义 __add__,我更愿意修改 如果可能的话。

    为了增加之前的答案的准确性,在您描述的情况下,self[epoch] += traffic 完全转换为:

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

    因此,如果您想要的只是 __iadd__ 的副作用,而没有项目分配部分,那么您的选择将仅限于您已经在您发布的代码中的 cmets 中确定的解决方法,或者调用__iadd__你自己——可能通过operator模块,虽然我相信operator.__iadd__(self[epoch], traffic)与更简单的self[epoch].__iadd__(traffic)相比没有附加价值(当self[epoch]确实有一个__iadd__方法)。

    【讨论】:

    • 你知道为什么self[epoch] += traffic没有被翻译成self[epoch].__iadd__(traffic)吗?除了使用我提供的“注释代码”之外,我能找到的保持语法的最佳方法是提供一个执行 assert self[key] is value... 的 __setitem__...原地修改)
    • @Matt,+=定义 是对左侧操作数(__add____iadd__ 的结果)执行赋值) -- 与是否有分配相比,这种方式更常规(这很重要,例如,在shelve 实例中!)。是的,如果你确实想写__setitem__,它可以是一个noop或只是一个检查(但你确实提到你宁愿写它;- )。
    【解决方案3】:

    代码:

    self[epoch] += traffic
    

    是语法糖:

    self[epoch] = self[epoch] + traffic
    

    所以分配并不意外,它是+=中的分配。所以你还需要重写__setitem__()方法。

    【讨论】:

      猜你喜欢
      • 2021-09-29
      • 1970-01-01
      • 1970-01-01
      • 2021-03-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多