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 s
ValueError: 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 :)