给定三点: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 1
其实我很想说,你需要的是函数式编程和 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 1
我觉得你需要的是 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 1
数学
约束 响应式 我觉得即使你完全不会函数式编程也该现场入门一下。。 每个点类存储一个变换函数,对于自变量来说,这个函数是设置时的闭包: 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.x - 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) |