关于 Python3 重载运算符 与 引用

2020-05-28 14:49:10 +08:00
 johnnyleaf

事情是这样的:

假设我有这样一个需求:

给定三点:A 、B 、C ;其中 A 与 B 坐标已知,C 点坐标 = A 点 + B 点, 当 A 点坐标发生变化时,C 点也应该发生变化。

我的代码如下

首先我定义一个 Point 类

class Point:
    
    def __init__(self, coordinate:tuple):
        self.x = coordinate[0]
        self.y = coordinate[1]
    
    def __repr__(self):
        return "(%.2f, %.2f)" % (self.x, self.y)
        
    def __add__(self, other:Point):
        if isinstance(other, Point):
            x = self.x + other.x
            y = self.y + other.y
            return Point(coordinate=(x,y))

然后定义 A 与 B 的坐标 以及 C 的关系:

A = Point((1,1))
B = Point((4,1))
C = A + B

此时 C 点的坐标应该为:(5,2)

但我进行如下操作后

A.x=5

C 点的坐标并没有发生变化...原因我知道,因为__add__给 C 返回的是一个新的 Point 对象。

我想请教各位!假如说 代码里我只能用重载运算符来完成 +-*/ 我该如何能让 C 点的坐标根据 A 点 /B 点的坐标变化而变化呢?

3974 次点击
所在节点    Python
38 条回复
sarvatathagata
2020-05-28 14:57:30 +08:00
不知道 python 有什么机制做到这个。。。然而这不就是 Qt 中的信号 /槽吗
aijam
2020-05-28 14:58:19 +08:00
C 里存 A, B 的 reference
dayeye2006199
2020-05-28 15:00:03 +08:00
难不成要这样

```python
class SumOfPoints:
def __init__(self, left, right):
self.left = left
self.right = right

@property
def x(self):
return self.left.x + self.right.x

@property
def y(self):
return self.left.y + self.right.y

class Point:

def __init__(self, coordinate:tuple):
self.x = coordinate[0]
self.y = coordinate[1]

def __repr__(self):
return "(%.2f, %.2f)" % (self.x, self.y)

def __add__(self, other):
if isinstance(other, Point):
x = self.x + other.x
y = self.y + other.y
return SumOfPoints(self, other)
```

```python
A = Point((1,1))
B = Point((4,1))
C = A + B

assert C.x==5

A.x = 5

assert C.x==9
```
johnnyleaf
2020-05-28 15:00:17 +08:00
@sarvatathagata 是的,其实也可以根据 监听者模式来设计这个模块,但是对于监听者模式本身我掌握的并不牢固。所以在想有没有其他的办法可以实现同样的功能。
johnnyleaf
2020-05-28 15:05:12 +08:00
@dayeye2006199 哈哈~感谢感谢 这个办法不错,但是 假如现实需求略复杂 可能出现
> A=(1, 1) B = (4, 1), C=(5, 3), D = C - B + A
以上代码,可以实现一个平行四边形,如果按照您的思路,我可能需要建立 SumOfPoints 与 Point 之间的__add__ 与 __ sub__ 等操作。
1iuh
2020-05-28 15:06:34 +08:00
你用重载运算符来做这事特别不合理, 你这样写感觉就类似 a = 1, b =1 c = a +b 这时 c = 2, 然后 a = a +2, c 的值就变成了 3 。

实现是可以实现, 把 Point 的类扩展一下,Point A 和 Point B 相加时,在新的对象里放入 Point A 和 Point B 。新对象的 x = self._pointa.x + self.pointb.x
johnnyleaf
2020-05-28 15:09:32 +08:00
@1iuh 其实我有同感,我也是在思路探索阶段 😂,,那您有更加合理的思路吗或者解决方案吗?
gwy15
2020-05-28 15:15:36 +08:00
imn1
2020-05-28 15:17:12 +08:00
你需要一个 setter 来自动改变属性,参考手册__setter__相关,或 @setter
问题是,你这里实际上是两种 Point,一种 xy 属性不变,另一种 xy 来自外部参数计算结果,不应该定义为同一个 Class
可以考类 C 继承 Point,并加入自动设置的__setter__类方法

至于运算符,好像不是需求重点,用了运算符,反而不能自动了(运算符意味着进行运算才触发)
junnplus
2020-05-28 15:22:01 +08:00
可以用 memoryview 试试,每个 point 保存两个 memoryview 对象,以及操作符
InkStone
2020-05-28 15:22:02 +08:00
其实我很想说,你需要的是函数式编程和 monad……
aijam
2020-05-28 15:25:39 +08:00
johnnyleaf
2020-05-28 15:28:18 +08:00
@aijam 感谢 感谢
johnnyleaf
2020-05-28 15:29:51 +08:00
@InkStone 关于函数式和 monad 我本身掌握不佳,熟悉后再做尝试。
nightwitch
2020-05-28 15:30:06 +08:00
这种做法是严重的设计缺陷, 有可能你的变量引用链条很长, 几百行代码之前的对某个变量的更改,可能引起正在工作的代码的更改,还是悄无声息的更改. ,找 bug 能找到崩溃.

帮你实现了一份差不多感觉的
https://paste.ubuntu.com/p/NQ43VjrSYg/
johnnyleaf
2020-05-28 15:30:19 +08:00
@junnplus 感谢 感谢,已经解决了
johnnyleaf
2020-05-28 15:32:46 +08:00
@nightwitch 感谢你,你说的问题确实存在,所以我放弃了,决定定义两个类来存储这两种不同的结构。
hustlibraco
2020-05-28 15:35:11 +08:00
@gwy15 请教一下,Vec2DF 起什么作用啊?
johnnyleaf
2020-05-28 15:36:08 +08:00
@imn1 是的,我了解了有关 @setter 的东西。我决定用两个类来分别处理这两种不同的对象。感谢你
gwy15
2020-05-28 15:42:15 +08:00
@hustlibraco type alias,带个 F 表示是个 callable,免得嵌套太长不好看

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/676315

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX