Python 2.7 - 左值修改的简洁语法
Python 2.7 - clean syntax for lvalue modification
类似结构的类型不希望被远程复制者修改是很常见的。
字符串是一个基本的例子,但这是一个简单的例子,因为它是可以原谅的不可变的——Python 是不寻常的,甚至允许对文字字符串进行方法调用。
问题是(在大多数语言中)我们经常遇到诸如 (x,y)
Point
class 之类的事情。我们偶尔想独立更改 x
和 y
。即,从使用的角度来看,Point
LVALUE 应该是可变的(即使副本不会看到变化)。
但是Python 2.7 似乎没有提供任何选项来启用自动复制作业。所以这意味着我们实际上必须使我们的 Point
class 不可变,因为无意中会在各处创建引用(通常是因为有人在将对象传递给其他人之前忘记克隆对象)。
不,我对仅允许对象发生变异的无数 hack 不感兴趣 "while it's being created",因为这是一个无法扩展的弱概念。
这些情况的逻辑结论是我们需要我们的变异方法来实际修改 LVALUE。例如 %=
支持。问题是如果有一个更合理的语法会更好,比如使用 __setattr__
and/or 定义 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中,典型的模式是修改前复制,而不是赋值时复制。您可以实现某种具有所需语义的数据存储,但这似乎需要大量工作。
我觉得我们已经对预先存在的解决方案进行了尽职调查。鉴于“<=”在某些语言(例如Verilog)中是赋值,我们可以很直观地介绍:
value_struct_instance<<='field', value
作为
的 Pythonic 形式
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
#
#
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 是不寻常的,甚至允许对文字字符串进行方法调用。
问题是(在大多数语言中)我们经常遇到诸如 (x,y)
Point
class 之类的事情。我们偶尔想独立更改 x
和 y
。即,从使用的角度来看,Point
LVALUE 应该是可变的(即使副本不会看到变化)。
但是Python 2.7 似乎没有提供任何选项来启用自动复制作业。所以这意味着我们实际上必须使我们的 Point
class 不可变,因为无意中会在各处创建引用(通常是因为有人在将对象传递给其他人之前忘记克隆对象)。
不,我对仅允许对象发生变异的无数 hack 不感兴趣 "while it's being created",因为这是一个无法扩展的弱概念。
这些情况的逻辑结论是我们需要我们的变异方法来实际修改 LVALUE。例如 %=
支持。问题是如果有一个更合理的语法会更好,比如使用 __setattr__
and/or 定义 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中,典型的模式是修改前复制,而不是赋值时复制。您可以实现某种具有所需语义的数据存储,但这似乎需要大量工作。
我觉得我们已经对预先存在的解决方案进行了尽职调查。鉴于“<=”在某些语言(例如Verilog)中是赋值,我们可以很直观地介绍:
value_struct_instance<<='field', value
作为
的 Pythonic 形式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
#
#
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 = ..."