Python列表上的Python属性设置器

问题描述 投票:7回答:2

我正在尝试利用Python @property修改类型为List的类属性。在线上的大多数示例都假定@property装饰的属性是单个值,而不是可以由setter扩展的列表。

为了澄清一点问题:我不只是想给属性s赋值(一个int列表的值),而是需要对其进行修改(将一个新的int追加到当前列表中)。

我的目的是希望有:

c = C()
c.s # [1,2,3] is the default value when instance c initiated.
c.s(5)

c.s # [1,2,3,5]

[C0的实现如下:

C

现在如果我执行class C: def __init__(self): self._s = [1,2,3] @property def s(self): return self._s @s.setter def s(self, val): self._s.append(val) ,我会得到

c.s(5)

我已阅读了最相关的文章:--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-99-767d6971e8f3> in <module>() ----> 1 c.s(5) TypeError: 'list' object is not callable Python property on a list

但都不适合我的情况:Python decorating property setter with list可以修改列表的元素,但我想扩展列表。

我当前的任务不接受使用全局属性。

在这方面,最佳解决方案是什么? (或者我从一开始就不期望在可变数据结构上使用@property吗?)谢谢!

------编辑-----

@ Samuel Dion-Girardeau建议参加

子类列表并定义其call魔术方法

,但是我不确定我是否了解解决方案。我应该做这样的事情:

__setitem__
python-3.x oop properties
2个回答
2
投票

[此处尝试总结和举例说明我的评论。希望这会有所帮助:

解决方案1:使用常规属性,然后直接class C(list): # somewhere when implementing class C def __call__(self): # even not sure what i need to do here

setter的目的不是要extend属性的值,而是要replace。在这方面,列表与int或字符串没有什么不同。在您的情况下,由于该值是可变列表,因此可以直接在其上直接调用.append()方法。

.append()

解决方案2:使用属性,直接使用class C(): def __init__(self): self.s = [1, 2, 3] >>> c = C() >>> c.s [1, 2, 3] >>> c.s.append(1) >>> c.s [1, 2, 3, 1] >>> c.s = [0, 0] >>> c.s [0, 0]

以上解决方案在获取/设置.append()时无需检查任何内容。但是,如果出于某些原因需要使用属性(例如,您需要进行一些计算或检查,并且希望阻止用户将其所需的任何内容设置为s),则可以使用属性。

在这种情况下,我将防止列表中出现负数作为验证的示例。

s

注意在这里引发错误:不是如果我打电话给你的问题以为是class C(): def __init__(self): self._s = [1, 2, 3] @property def s(self): return self._s @s.setter def s(self, val): if any(x < 0 for x in val): raise ValueError('No negative numbers here!') self._s = val >>> c = C() >>> c.s [1, 2, 3] >>> c.s.append(1) >>> c.s [1, 2, 3, 1] >>> c.s = [0, 0] >>> c.s [0, 0] >>> c.s = [0, -1] Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 10, in s ValueError: No negative numbers here! ,而是你以c.s(...)分配给c.s时,则为这个问题。

还请注意,此类的用户将通过设置器间接修改c.s = ...

解决方案3:子类_s以允许可调用属性

我绝对不建议这样做,因为它违反了此类用户的所有期望,而我只是将其作为琐事提供,并且因为它允许您最初要求的行为。

list

请不要这样做,如果您确定它不能追溯到我:)


0
投票

尝试一下:

class CallableList(list):
    # This is the method that actually gets called when you `c.s(...)`!
    def __call__(self, *args):
        self.append(*args)

class C():
    def __init__(self):
        self._s = CallableList([1,2,3])
    @property
    def s(self):
        return self._s
    @s.setter
    def s(self, val):
        self._s = CallableList(val)

>>> c = C()
>>> c.s
[1, 2, 3]
>>> c.s(1)
>>> c.s
[1, 2, 3, 1]
>>> c.s = [0, 0]
>>> c.s
[0, 0]
>>> c.s(1337)
>>> c.s
[0, 0, 1337]
© www.soinside.com 2019 - 2024. All rights reserved.