
给定三点:A 、B 、C ;其中 A 与 B 坐标已知,C 点坐标 = A 点 + B 点, 当 A 点坐标发生变化时,C 点也应该发生变化。
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 = Point((1,1)) B = Point((4,1)) C = A + B A.x=5 1 sarvatathagata 2020-05-28 14:57:30 +08:00 不知道 python 有什么机制做到这个。。。然而这不就是 Qt 中的信号 /槽吗 |
2 aijam 2020-05-28 14:58:19 +08:00 C 里存 A, B 的 reference |
3 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 ``` |
4 johnnyleaf OP @sarvatathagata 是的,其实也可以根据 监听者模式来设计这个模块,但是对于监听者模式本身我掌握的并不牢固。所以在想有没有其他的办法可以实现同样的功能。 |
5 johnnyleaf OP @dayeye2006199 哈哈~感谢感谢 这个办法不错,但是 假如现实需求略复杂 可能出现 > A=(1, 1) B = (4, 1), C=(5, 3), D = C - B + A 以上代码,可以实现一个平行四边形,如果按照您的思路,我可能需要建立 SumOfPoints 与 Point 之间的__add__ 与 __ sub__ 等操作。 |
6 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 |
7 johnnyleaf OP @1iuh 其实我有同感,我也是在思路探索阶段 ,,那您有更加合理的思路吗或者解决方案吗? |
8 gwy15 2020-05-28 15:15:36 +08:00 |
9 imn1 2020-05-28 15:17:12 +08:00 你需要一个 setter 来自动改变属性,参考手册__setter__相关,或 @setter 问题是,你这里实际上是两种 Point,一种 xy 属性不变,另一种 xy 来自外部参数计算结果,不应该定义为同一个 Class 可以考类 C 继承 Point,并加入自动设置的__setter__类方法 至于运算符,好像不是需求重点,用了运算符,反而不能自动了(运算符意味着进行运算才触发) |
10 junnplus 2020-05-28 15:22:01 +08:00 可以用 memoryview 试试,每个 point 保存两个 memoryview 对象,以及操作符 |
11 InkStone 2020-05-28 15:22:02 +08:00 其实我很想说,你需要的是函数式编程和 monad…… |
12 aijam 2020-05-28 15:25:39 +08:00 |
13 johnnyleaf OP @aijam 感谢 感谢 |
14 johnnyleaf OP @InkStone 关于函数式和 monad 我本身掌握不佳,熟悉后再做尝试。 |
15 nightwitch 2020-05-28 15:30:06 +08:00 这种做法是严重的设计缺陷, 有可能你的变量引用链条很长, 几百行代码之前的对某个变量的更改,可能引起正在工作的代码的更改,还是悄无声息的更改. ,找 bug 能找到崩溃. 帮你实现了一份差不多感觉的 https://paste.ubuntu.com/p/NQ43VjrSYg/ |
16 johnnyleaf OP @junnplus 感谢 感谢,已经解决了 |
17 johnnyleaf OP @nightwitch 感谢你,你说的问题确实存在,所以我放弃了,决定定义两个类来存储这两种不同的结构。 |
18 hustlibraco 2020-05-28 15:35:11 +08:00 @gwy15 请教一下,Vec2DF 起什么作用啊? |
19 johnnyleaf OP |
20 gwy15 2020-05-28 15:42:15 +08:00 @hustlibraco type alias,带个 F 表示是个 callable,免得嵌套太长不好看 |
21 no1xsyzy 2020-05-28 15:47:11 +08:00 @johnnyleaf #5 > 我可能需要建立 SumOfPoints 与 Point 之间的__add__ 与 __ sub__ 等操作。 可以做一个 ABCPoint,然后把 SumOfPoints 和 Point 都注册进去,之后判断就是 isinstance(other, ABCPoint) 或者让 SumOfPoints 继承 Point |
22 neoblackcap 2020-05-28 15:47:59 +08:00 不一样的类型是正确的,其实你需要的不是什么 Point,是一个 LazyPoint 或者一个公式,Excel 的公式用过吧。 C 应该是代表 Add(A, B),需要用到值的时候再求值。完美满足你的要求。你自己控制一下类似的 Lazy 类对象的求值时机就好了。性能都可控 |
24 johnnyleaf OP @neoblackcap 是的 我正在实现一个新类去实现你讲的类似功能。感谢 |
25 jmc891205 2020-05-28 16:01:03 +08:00 为什么会有这种需求。 那当你有 100 万个点,修改 1 个点就会导致另外 99 万多个点的坐标都更新,这性能岂不是太差了。 |
26 johnnyleaf OP @jmc891205 首先,目前确定的 业务中点的个数在 10 个之内。其次我决定尝试对更新做控制。不会让它自动更新的哈。 |
27 ipwx 2020-05-28 16:06:35 +08:00 我觉得你需要的是 lazy evaluation 。通过占位符(比如 _1 + _2 )产生一个表达式对象,然后当你把真实的点倒进去的时候,给你把结果算出来。 Point 这种对象,从语义上通常认为是不可变的常量。否则会让人 confuse 。 |
28 Vegetable 2020-05-28 16:35:06 +08:00 https://gist.github.com/luliangce/5c1c84392a51c02cb90b62e173d12ed0 你这里涉及到一个问题,那就是一旦赋值一个 Point,马上就会破坏调用链,这就让代码变得非常怪异 |
29 milkpuff 2020-05-28 17:22:20 +08:00 运行一个 thread 跑死循环,判断变量是否改变,如果改变就重新计算?? |
30 whileFalse 2020-05-28 19:04:45 +08:00 你需要的是一个新类。 class AddPoint(Point): def __init__(self, p1, p2): self.p1 = p1 self.p2 = p2 @property def x(self): return self.p1.x + self.p2.x 明白了吗。 另外,基础都没打好就别玩儿这些高级特性了。你如果对面向对象编程有 [基础] 的了解就不会问出这个问题。 |
31 xxapp 2020-05-28 19:37:17 +08:00 via Android 了解下响应式编程? |
32 GeruzoniAnsasu 2020-05-28 20:04:56 +08:00 via Android 数学 约束 响应式 我觉得即使你完全不会函数式编程也该现场入门一下。。 每个点类存储一个变换函数,对于自变量来说,这个函数是设置时的闭包: a=A(1,2) 得 a.x==lambda :1 a.y==lambda :2 对于因变量来说,这个函数是变换函数 c=a+b 得 c.x==lambda:a.x()+b.x() c.y==lambda:a.y()+b.y() 当要取得 c 的值时调用 c.x() =>(lambda:a.x()+b.x())() =>(lambda:(lambda:1)()+(lambda:xb)())() =>(lambda:1+xb)() 这不是比你递归通知约束对象计算新值要容易多了 至于怎么实现,约束方法本身已经是实现方法了,品一品 |
33 whileFalse 2020-05-28 20:14:32 +08:00 @GeruzoniAnsasu 挺好,不过我估计 lz 还想给 c 赋值,然后反过来影响 a…… |
34 hustlibraco 2020-05-28 21:17:26 +08:00 @gwy15 Union[Vec2D, Vec2DF]的定义主要是为了 Point 既能接受坐标数组作为初始化参数,也能接受函数作为初始化数据,因此后面可以传 Lamdba 表达式初始化,从而实现 lazy evaluation 的功能? |
35 noqwerty 2020-05-28 21:23:24 +08:00 via Android V 站现在能像楼主一样把自己需求说这么清楚的问题太少了 |
36 jxie0755 2020-05-28 21:28:13 +08:00 via iPhone 你什么时候需要用 C,就在用之前再调用当时的 A 和 B 嘛。 |
37 lithbitren 2020-05-29 04:07:31 +08:00 一个继承字典的类就能解决变量展示和修改问题了。 class Point(dict): def __init__(self, x, y): self['x'] = x self['y'] = y def __getattr__(self, attr): return self[attr]() if callable(self[attr]) else self[attr] def __setattr__(self, attr, value): self[attr] = value def __add__(self, other): return Point(lambda: self.x + other.y, lambda: self.x + other.y) def __sub__(self, other): return Point(lambda: self.x - other.y, lambda: self. - other.y) def __str__(self): return f'Point({self.x:.2f}, {self.y:.2f})' if __name__ == "__main__": a = Point(1, 2) print('a', a) # Point(1.00, 2.00) print('a.x', a.x) # 1 a.x = 3 print('a', a) # Point(3.00, 2.00) print('a.x', a.x) # 3 b = Point(0, 6) print('b', b) # Point(0.00, 6.00) c = a + b - Point(100, 100) print('c', c) # Point(-91.00, -91.00) b.y = 1000 print('c', c) # Point(903.00, 903.00) |
38 lithbitren 2020-05-29 04:12:51 +08:00 我 r,加减写错了,重写重写。。。 class Point(dict): def __init__(self, x, y): self['x'] = x self['y'] = y def __getattr__(self, attr): return self[attr]() if callable(self[attr]) else self[attr] def __setattr__(self, attr, value): self[attr] = value def __add__(self, other): return Point(lambda: self.x + other.x, lambda: self.y + other.y) def __sub__(self, other): return Point(lambda: self.x - other.x, lambda: self.y - other.y) def __str__(self): return f'Point({self.x:.2f}, {self.y:.2f})' if __name__ == "__main__": a = Point(1, 2) print('a', a) # Point(1.00, 2.00) print('a.x', a.x) # 1 a.x = 3 print('a', a) # Point(3.00, 2.00) print('a.x', a.x) # 3 b = Point(0, 6) print('b', b) # Point(0.00, 6.00) c = a + b - Point(100, 100) print('c', c) # Point(-97.00, -92.00) b.y = 1000 print('c', c) # Point(-97.00, 902.00) |