在现有属性递增时调用__setattr__?

时间:2017-04-29 02:22:49

标签: python overloading setattr

当现有属性递增时,是否调用__setattr__?

我有一个名为 C 的课程,我试图重载__setattr__。这是类 C

中的一段代码
class C:

    def bump(self):
        self.a += 1
        self.b += 1
        self.c += 1

    def __setattr__(self,name,value):
        calling = inspect.stack()[1]
        if 'private_' in name:
            raise NameError("\'private_\' is in the variable name.")
        elif '__init__' in calling.function:
            self.__dict__['private_' + name] = value
        elif name.replace('private_', '') in self.__dict__:
            if self.in_C(calling):
                if name.replace('private_', '') in self.__dict__.keys():
                    old_value = self.__dict__[name.replace('private_', '')]
                    new_value = old_value + value
                    self.__dict__[name.replace('private_', '')] = new_value
                else:
                    self.__dict__[name.replace('private_','')] = value
            else:
                raise NameError()
        else:
            self.__dict__[name] = value

__ setattr__,根据Python docs

  

object .__ setattr __(self,name,value):尝试进行属性赋值时调用。这被称为代替正常机制(即将值存储在实例字典中)。 name是属性名称,value是要分配给它的值。

我知道您可以为变量指定一个值(例如:C.__dict__[name] = value),但是现有属性何时递增,例如self.a += 1中的bump()

假设已经定义了属性 a b c ,我调用bump(),然后调用{ {1}}。但是,我收到了这个错误:

__setattr__

现有属性递增时是否调用 setattr ?如果是这样,如何增加setattr中的现有属性?

注意:假设在定义a,b和c之后调用Error: o.bump() raised exception TypeError: unsupported operand type(s) for +=: 'NoneType' and 'int' 。此外,bump()是一个函数,用于检查in_C(calling)是否从__setattr__,C中的某个方法或C之外的方法调用。

告诉我是否需要进一步澄清。

2 个答案:

答案 0 :(得分:5)

  

Python:当现有属性递增时调用__setattr__?

答案是肯定的。使用简化版代码很容易看到这一点:

class C(object):

    def __init__(self, a):
        object.__setattr__(self, 'a', a)

    def __setattr__(self, name, value):
        print('Setting {} to {}'.format(name, value))
        object.__setattr__(self, name, value)


c = C(10)
c.a += 1

运行该代码段会产生:

Setting a to 11

您发布的代码的问题是+=在调用__getattribute__之前先调用__setattr__。如果属性不存在,那就是失败。

解决方案是确保在调用 bump()之前初始化属性:

class C(object):

    def __init__(self, a, b, c):
        object.__setattr__(self, 'a', a)
        object.__setattr__(self, 'b', a)
        object.__setattr__(self, 'c', a)

除了该修复程序之外,还有其他错误(例如 inspect ),但这应该可以帮助您入门。

答案 1 :(得分:0)

虽然看起来似乎

a += 1

相当于a.__iadd__(1)(如果a__iadd__),它实际上相当于:

a = a.__iadd__(1)    # (but `a` is only evaluated once.)

对于可变类型,__iadd__返回相同的对象,因此您没有看到差异。

因此,如果目标是c.a,则会调用c的{​​{1}}。 同样,如果您执行类似__setattr__的操作,则会调用__setitem__

这样做是因为Python具有不可变类型,否则增强赋值将无效。

grammar reference entry for augmented assignments(强调我的)中记录了这一点:

  

增强分配评估目标(与正常情况不同)   赋值语句,不能解包)和表达式   list,执行特定于赋值类型的二进制操作   在两个操作数上,并将结果分配给原始目标。   目标仅评估一次。

举例说明:

c['a']+=1