使Python中的@property支持+=、-=等操作

7 投票
3 回答
1338 浏览
提问于 2025-04-18 04:12

Python文档中,我看到你可以设置一些方法来处理属性,这样做是很方便的:

class C(object):
    def __init__(self):
        self._x = None

    def getx(self):
        return self._x

    def setx(self, value):
        self._x = value

    def delx(self):
        del self._x

    x = property(getx, setx, delx, "I'm the 'x' property.")

现在假设 C._x 是一个列表。在初始化的时候,它被简单地设置为 []。所以如果我这样做:

c = C()
c.x = [1,2,3]

c.x 会被设置为 [1,2,3]。我现在想要做的是

#...
c.x += 4

这样 c.x 现在变成了 [1,2,3,4]。这个例子很简单,但显然,我希望 setxgetx 方法能包含一些处理和检查。否则,使用这种方法就没什么意义了。

编辑: 可能只用 __add__ 方法在 C 上来强制执行这种行为就足够了,但我在想是否可以把这种行为放在属性上,而不是整个类上。

3 个回答

0

这是一种可能的解决方案。你可以调整一下 __iadd__ 方法的行为。

class C(object):
    def __init__(self):
        # instanciate an empty list of your custom list-type
        self._x = MyList([])

    def getx(self):
        return self._x

    def setx(self, value):
        self._x = value

    def delx(self):
        del self._x

    x = property(getx, setx, delx, "I'm the 'x' property.")

class MyList(list):

    def __iadd__(self,x):
        # if the item is an iterable, extend the list (standard __iadd__ behaviour)
        if hasattr(x,"__iter__"):
            return super(MyList,self).__iadd__(x)
        #if the item is not iterable, append it
        else:
            return super(MyList,self).__iadd__([x])

用法如下:

>>> c=C()
>>> c.x+=1
>>> c.x
    [1]
>>> c.x+="test"
>>> c.x
    [1,"test"]
>>> c.x+=[3,4]
>>> c.x
    [1,"test",3,4]

总结一下:你不能在 C 的 setitem 方法里重载运算符,因为增加操作并不是在属性上进行的,而是在底层的列表上进行的(因为你的属性只是一个指向那个列表对象的变量)。你可以看看我的评论和其他的回答。

2

不,这样做不行。

基本上,事情是这样的:

c.x += 4

这段代码和下面这段是一样的:

var temp = c.x
temp += 4
c.x = temp

不过,这里的 temp 会是一个包含3个值的列表,而这段代码是不能工作的:

temp = [1, 2, 3]
temp += 4

你会得到:

类型错误:'int'对象不可迭代

所以这和属性没有关系,完全是因为代码需要像这样写:

temp += [4]

这样就可以工作:

c.x += [4]

而这个不能:

c.x += 4
5

你不能为特定的属性重载运算符,因为:

c.x += 4
# is equivalent to
c.x.__iadd__(4)

实际上,你是在调用列表的 __iadd__ 运算符。如果你想要实现这个功能,你需要创建一个新的类,继承自列表,并重载 __iadd____add__ 运算符。

class SuperList(list):
    def __iadd__(self, other):
        if type(other) == list or type(other) == SuperList:
            return super(SuperList, self).__iadd__(other)
        return super(SuperList, self).__iadd__([other])

撰写回答