在开始之前,我已经意识到Python中的对象不变性通常是一个坏主意,但是,我相信在我的情况下它是合适的。
假设我正在使用我的代码中的坐标系,这样每个坐标都使用X,Y,Z的结构。我已经重载减法,加法等方法来做我想要的。我当前的问题是赋值运算符,我读过它不能重载。问题是当我有以下内容时,我不希望A指向与B相同的点,我希望两者是独立的,以防我需要覆盖一个坐标而不是另一个坐标:
B = Point(1,2,3)
A = B
我知道我可以使用深度复制,但这看起来像是一个黑客,特别是因为我可以有一个点列表,我可能需要采取一片(在这种情况下,它将再次有一点点参考,而不是点)。我也考虑使用元组,但我的观点有我需要的成员方法,而且我的代码的很大一部分已经使用了结构。
我的想法是将Point修改为不可变的,因为它实际上只有3个浮点数据,并且通过做一些研究_new _()似乎是为此覆盖的正确函数。我不知道如何实现这一目标,它会是这样的还是我离开的?
def __new__(self):
return Point(self.x, self.y, self.z)
编辑: 我的不好,我在阅读katrielalex的帖子后意识到,一旦定义了不可变对象的参数,我就无法修改它,在这种情况下,A和B都指向相同的数据不是问题,因为重新分配需要创建一个新的观点。我会说katrielalex和vonPetrushev的帖子实现我想要的,我想我会选择vonPetrushev的解决方案,因为我不需要重写所有当前代码来使用元组(额外的括号集而不能引用坐标为point.x)
答案 0 :(得分:4)
结合katrielalex的建议,将Point
命名为元组也会很好。在这里,我刚刚将tuple
父级替换为namedtuple('Point', 'x y z')
- 这足以让它发挥作用。
>>> from collections import namedtuple
>>> class Point(namedtuple('Point', 'x y z')):
... def __add__(self, other):
... return Point((i + j for i, j in zip(self, other)))
...
... def __mul__(self, other):
... return sum(i * j for i, j in zip(self, other))
...
... def __sub__(self, other):
... return Point((i - j for i, j in zip(self, other)))
...
... @property
... def mod(self):
... from math import sqrt
... return sqrt(sum(i*i for i in self))
...
然后你可以:
>>> Point(1, 2, 3)
Point(x=1, y=2, z=3)
>>> Point(x=1, y=2, z=3).mod
3.7416573867739413
>>> Point(x=1, y=2, z=3) * Point(0, 0, 1)
3
>>> Point._make((1, 2, 3))
Point(x=1, y=2, z=3)
(感谢katrielalex建议扩展namedtuple而不是复制生成的代码。)
答案 1 :(得分:1)
你可以使Point
成为tuple
的子类 - 记住,内置类型(至少在最近的Pythons中)只是更多的类。这将为您提供所需的不变性。
但是,我对你建议的用例感到有些困惑:
如果我需要覆盖一个坐标而不是另一个坐标:
如果Point
是不可变的,那就没有意义......
>>> class Point(tuple):
... def __add__(self, other):
... return Point((i + j for i, j in zip(self, other)))
...
... def __mul__(self, other):
... return sum(i * j for i, j in zip(self, other))
...
... def __sub__(self, other):
... return Point((i - j for i, j in zip(self, other)))
...
... @property
... def mod(self):
... from math import sqrt
... return sqrt(sum(i*i for i in self))
...
>>> a = Point((1,2,3))
>>> b = Point((4,5,6))
>>> a + b
(5, 7, 9)
>>> b - a
(3, 3, 3)
>>> a * b
32
>>> a.mod
3.7416573867739413
>>> a[0] = 1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'Point' object does not support item assignment
答案 2 :(得分:0)
试试这个:
class Point(object):
def __init__(self, x, y, z):
self._x=x
self._y=y
self._z=z
def __getattr__(self, key):
try:
key={'x':'_x','y':'_y','z':'_z'}[key]
except KeyError:
raise AttributeError
else:
return self.__dict__[key]
def __setattr__(self, key, value):
if key in ['_x','_y','_z']:
object.__setattr__(self, key, value)
else:
raise TypeError("'Point' object does not support item assignment")
因此,您可以构造一个Point对象,但不能更改其属性。