Python 2.7 - чистый синтаксис для модификации lvalue

Очень часто встречаются структурные типы, которые не должны изменяться удаленными владельцами копий.

Строка является базовым примером, но это простой случай, потому что она исключительно неизменна - Python необычен даже в том, что допускает такие вещи, как вызовы методов для литеральных строк.

Проблема в том, что (в большинстве языков) у нас часто бывают такие вещи, как, скажем, (x,y)Point учебный класс. Мы иногда хотим изменить x а также y независимо. Т.е. с точки зрения использования 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, который не ограничивается глупыми операторами. Я просто не могу найти это. Пожалуйста помоги.

2 ответа

Решение

Я чувствую, что мы предоставили поиск уже существующих решений должной осмотрительности. Учитывая, что "<=" является присваиванием в некоторых языках (например, Verilog), мы можем довольно интуитивно представить:

value_struct_instance<<='field', value

как пифоническая форма

value_struct_instance.field = value

Вот обновленный пример для поучительных целей:

# Python doesn't support copy-on-assignment, so we must use an
# immutable object to avoid unintended changes by distant copyholders.
# As a consequence, the lvalue must be changed on a field update.
#
# Currently the only known syntax for updating a field on such an
# object is:
#
#      value_struct_instance<<='field', value
# 
# https://stackru.com/questions/45788271/

class Point(object):

    def __init__(self, x, y, others=None):
        object.__setattr__(self, 'x', x)
        object.__setattr__(self, 'y', y)

    def __setattr__(self, name, value):
        raise AttributeError, \
            "Use \"point<<='%s', ...\" instead of \"point.%s = ...\"" \
            % (name, name)

    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 __ilshift__(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]

    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)



my_very_long_and_complicated_lvalue_expression = [Point(10,10)] * 3

# modify element 0 via "+="
my_very_long_and_complicated_lvalue_expression[0] += Point(1,-1)

# modify element 1 via "<<='field'," (NEW IDIOM)
my_very_long_and_complicated_lvalue_expression[1]<<='x', 15
print my_very_long_and_complicated_lvalue_expression
# result:
# [(11 9), (15 10), (10 10)]

my_very_long_and_complicated_lvalue_expression[1]<<='y', 25
print my_very_long_and_complicated_lvalue_expression
# result:
# [(11 9), (15 25), (10 10)]

# Attempt to modify element 2 via ".field="
my_very_long_and_complicated_lvalue_expression[2].y = 25
# result:
# AttributeError: Use "point<<='y', ..." instead of "point.y = ..."

В Python типичным шаблоном является копирование перед изменением, а не копирование по назначению. Вы могли бы реализовать какое-то хранилище данных с нужной вам семантикой, но это кажется большой работой.

Другие вопросы по тегам