Python 2.7 - 用于左值修改的简洁语法

问题描述 投票:0回答:3

具有类似结构的类型是很常见的,这些类型预计不会被远程复制者修改。

字符串是一个基本的例子,但这是一个简单的例子,因为它是不可变的——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 修改的基本语法,该语法不仅限于看起来愚蠢的运算符。我只是似乎找不到它。请帮忙。

python python-2.7 struct mutable lvalue
3个回答
1
投票

在Python中,典型的模式是在修改之前复制而不是在赋值时复制。您可以使用您想要的语义来实现某种数据存储,但这似乎需要大量工作。


0
投票

我觉得我们已经对寻找现有解决方案进行了尽职调查。鉴于“<=" is assignment in some languages (e.g., Verilog) we can quite intuitively introduce:

value_struct_instance<<='field', value

作为

的 Python 形式
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://stackoverflow.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 = ..."

0
投票

多年后,我想我偶然发现了正确的答案:

collections.namedtuple

© www.soinside.com 2019 - 2024. All rights reserved.