Пытаясь обобщить и проиллюстрировать мои комментарии здесь.Надеюсь, что это поможет:
Решение № 1: Используйте обычный атрибут, и .append()
напрямую
Целью установщика не является расширение значения атрибута, это заменить это.В этом отношении список ничем не отличается от int или строки.В вашем случае, поскольку значение является изменяемым списком, вы можете просто вызвать метод .append()
непосредственно для него.
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]
Решение № 2: Использовать свойства и .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 = ...
.
Также обратите внимание, что пользователи этого класса изменят _s
косвенно, через сеттер.
Решение № 3: Подкласс list
, чтобы позволить атрибуту быть вызываемым
Что я абсолютно не рекомендую, потому что оно нарушает все ожидания пользователей этого класса, и я толькопредоставляя это как пустяки, и потому что это позволяет поведение, которое вы просили изначально.
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]
Пожалуйста, не делайте этого, и если вы действительно убедитесь, что это не прослеживается мне:)