关于 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 点的坐标变化而变化呢?

3975 次点击
所在节点    Python
38 条回复
no1xsyzy
2020-05-28 15:47:11 +08:00
@johnnyleaf #5 > 我可能需要建立 SumOfPoints 与 Point 之间的__add__ 与 __ sub__ 等操作。
可以做一个 ABCPoint,然后把 SumOfPoints 和 Point 都注册进去,之后判断就是 isinstance(other, ABCPoint)
或者让 SumOfPoints 继承 Point
neoblackcap
2020-05-28 15:47:59 +08:00
不一样的类型是正确的,其实你需要的不是什么 Point,是一个 LazyPoint 或者一个公式,Excel 的公式用过吧。
C 应该是代表 Add(A, B),需要用到值的时候再求值。完美满足你的要求。你自己控制一下类似的 Lazy 类对象的求值时机就好了。性能都可控
no1xsyzy
2020-05-28 15:54:01 +08:00
@aijam #12 调用被放大了,复杂度 O(2^n)
johnnyleaf
2020-05-28 15:55:33 +08:00
@neoblackcap 是的 我正在实现一个新类去实现你讲的类似功能。感谢
jmc891205
2020-05-28 16:01:03 +08:00
为什么会有这种需求。
那当你有 100 万个点,修改 1 个点就会导致另外 99 万多个点的坐标都更新,这性能岂不是太差了。
johnnyleaf
2020-05-28 16:05:19 +08:00
@jmc891205 首先,目前确定的 业务中点的个数在 10 个之内。其次我决定尝试对更新做控制。不会让它自动更新的哈。
ipwx
2020-05-28 16:06:35 +08:00
我觉得你需要的是 lazy evaluation 。通过占位符(比如 _1 + _2 )产生一个表达式对象,然后当你把真实的点倒进去的时候,给你把结果算出来。

Point 这种对象,从语义上通常认为是不可变的常量。否则会让人 confuse 。
Vegetable
2020-05-28 16:35:06 +08:00
https://gist.github.com/luliangce/5c1c84392a51c02cb90b62e173d12ed0

你这里涉及到一个问题,那就是一旦赋值一个 Point,马上就会破坏调用链,这就让代码变得非常怪异
milkpuff
2020-05-28 17:22:20 +08:00
运行一个 thread 跑死循环,判断变量是否改变,如果改变就重新计算??
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


明白了吗。

另外,基础都没打好就别玩儿这些高级特性了。你如果对面向对象编程有 [基础] 的了解就不会问出这个问题。
xxapp
2020-05-28 19:37:17 +08:00
了解下响应式编程?
GeruzoniAnsasu
2020-05-28 20:04:56 +08:00
数学
约束
响应式



我觉得即使你完全不会函数式编程也该现场入门一下。。

每个点类存储一个变换函数,对于自变量来说,这个函数是设置时的闭包:

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)()


这不是比你递归通知约束对象计算新值要容易多了


至于怎么实现,约束方法本身已经是实现方法了,品一品
whileFalse
2020-05-28 20:14:32 +08:00
@GeruzoniAnsasu 挺好,不过我估计 lz 还想给 c 赋值,然后反过来影响 a……
hustlibraco
2020-05-28 21:17:26 +08:00
@gwy15 Union[Vec2D, Vec2DF]的定义主要是为了 Point 既能接受坐标数组作为初始化参数,也能接受函数作为初始化数据,因此后面可以传 Lamdba 表达式初始化,从而实现 lazy evaluation 的功能?
noqwerty
2020-05-28 21:23:24 +08:00
V 站现在能像楼主一样把自己需求说这么清楚的问题太少了😂
jxie0755
2020-05-28 21:28:13 +08:00
你什么时候需要用 C,就在用之前再调用当时的 A 和 B 嘛。
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)
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)

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

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

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

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

© 2021 V2EX