Очень часто встречаются структурные типы, которые не должны изменяться удаленными держателями копий.
Строка является базовым примером, но это простой случай, потому что это извиняемо неизменное - Python необычен даже для того, чтобы разрешать такие вещи, как вызовы методов для литералов.
Проблема в том, что (на большинстве языков) мы часто имеем такие вещи, как класс (x,y)
Point
. Иногда мы хотим независимо менять x
и y
. I.e., с точки зрения использования, Point
LVALUE должен быть изменчивым (даже если копии не будут видеть мутацию).
Но Python 2.7, похоже, не предоставляет никаких параметров для включения автоматического копирования при назначении. Таким образом, мы фактически ДОЛЖНЫ сделать наш Point
класс IMMUTABLE, потому что непреднамеренные ссылки будут создаваться повсюду (обычно, потому что кто-то забыл клонировать объект, прежде чем передавать его кому-то другому).
И нет, меня не интересуют бесчисленные хаки, которые позволяют объекту мутировать только "пока он создается", поскольку это слабое понятие, которое не масштабируется.
Логическое завершение этих обстоятельств состоит в том, что нам нужны наши методы мутаций для фактического изменения LVALUE. Например, %=
поддерживает это. Проблема в том, что было бы намного лучше иметь более разумный синтаксис, например, используя __setattr__
и/или определяющие методы set_x
и set_y
, как показано ниже.
class Point(object):
# Python doesn't have copy-on-assignment, so we must use an immutable
# object to avoid unintended changes by distant copyholders.
def __init__(self, x, y, others=None):
object.__setattr__(self, 'x', x)
object.__setattr__(self, 'y', y)
def __setattr__(self, name, value):
self %= (name, value)
return self # SHOULD modify lvalue (didn't work)
def __repr__(self):
return "(%d %d)" % (self.x, self.y)
def copy(self, x=None, y=None):
if x == None: x = self.x
if y == None: y = self.y
return Point(x, y)
def __eq__ (a,b): return a.x == b.x and a.y == b.y
def __ne__ (a,b): return a.x != b.x or a.y != b.y
def __add__(a,b): return Point(a.x+b.x, a.y+b.y)
def __sub__(a,b): return Point(a.x-b.x, a.y-b.y)
def set_x(a,b): return a.copy(x=b) # SHOULD modify lvalue (didn't work)
def set_y(a,b): return a.copy(y=b) # SHOULD modify lvalue (didn't work)
# This works in Python 2.7. But the syntax is awful.
def __imod__(a,b):
if b[0] == 'x': return a.copy(x=b[1])
elif b[0] == 'y': return a.copy(y=b[1])
else: raise AttributeError, \
"Point has no member '%s'" % b[0]
my_very_long_and_complicated_lvalue_expression = [Point(10,10)] * 4
# modify element 0 via "+=" -- OK
my_very_long_and_complicated_lvalue_expression[0] += Point(1,-1)
# modify element 1 via normal "__set_attr__" -- NOT OK
my_very_long_and_complicated_lvalue_expression[1].x = 9999
# modify element 2 via normal "set_x" -- NOT OK
my_very_long_and_complicated_lvalue_expression[2].set_x(99)
# modify element 3 via goofy "set_x" -- OK
my_very_long_and_complicated_lvalue_expression[3] %='x', 999
print my_very_long_and_complicated_lvalue_expression
Результат:
[(11 9), (10 10), (10 10), (999 10)]
Как вы можете видеть, +=
и %=
работают нормально, но практически ничего не работает. Разумеется, изобретатели языка создали базовый синтаксис для модификации LVALUE, который не ограничивается тупыми операторами. Я просто не могу найти его. Пожалуйста, помогите.