Python property setter on a python list
Trying to summarize and exemplify my comments here. Hope this helps:
Solution #1: Use a regular attribute, and .append()
directly
The purpose of a setter is not to extend the value of the attribute, it is to replace it. In this regard, a list isn't any more different than an int or a string. In your case, since the value is a mutable list, you can simply call the .append()
method directly on it.
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]
Solution #2: Use properties, and .append()
directly
The solution above works if there's nothing to check when getting/setting s
. However if you need to use a property for some reason (e.g. you have some computation or checks to do, and want to prevent users from setting anything they want as s
), you can do so with properties.
In this instance, I'm preventing negative numbers in the list as an example of a validation.
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 sValueError: No negative numbers here!
Note where the error is thrown here: not if I call c.s(...)
, which your question assumed to be calling the setter, but rather when I assign to c.s
, with c.s = ...
.
Also note that the users of this class will modify _s
indirectly, through the setter.
Solution #3: Subclass list
to allow the attribute to be callable
Which I absolutely don't recommend at all because it breaks every expectation the users of this class would have, and I'm only providing it as a trivia, and because it allows the behaviour you asked for initially.
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]
Please don't do this, and if you do make sure it's not traceable back to me :)
try this:
class C:def __init__(self): self._s = [1, 2, 3]@propertydef s(self): return tuple(self._s)@s.setterdef s(self, list_val): self._s = list_valdef add(self, *args): for arg in args: self._s.append(arg)def print_c():print('my list_props')print('-'*5)for p in c.s: print(p)print('-' * 5)print()c = C()print_c()c.s = [8, 9, 10, 11]print_c()c.s = []print_c()c.add(100, 200, 300)print_c()c.add(400, 500, 600)print_c()c.s = []c.add(*[i for i in range(100)])print_c()