跳至主要內容
  • Hostloc 空間訪問刷分
  • 售賣場
  • 廣告位
  • 賣站?

4563博客

全新的繁體中文 WordPress 網站
  • 首頁
  • 关于 Python3 重载运算符 与 引用
未分類
30 5 月 2020

关于 Python3 重载运算符 与 引用

关于 Python3 重载运算符 与 引用

資深大佬 : johnnyleaf 1

事情是这样的:

假设我有这样一个需求:

给定三点: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 点的坐标变化而变化呢?

大佬有話說 (38)

  • 資深大佬 : sarvatathagata

    不知道 python 有什么机制做到这个。。。然而这不就是 Qt 中的信号 /槽吗

  • 資深大佬 : aijam

    C 里存 A, B 的 reference

  • 資深大佬 : dayeye2006199

    难不成要这样

    “`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

    @sarvatathagata 是的,其实也可以根据 监听者模式来设计这个模块,但是对于监听者模式本身我掌握的并不牢固。所以在想有没有其他的办法可以实现同样的功能。

  • 主 資深大佬 : johnnyleaf

    @dayeye2006199 哈哈~感谢感谢 这个办法不错,但是 假如现实需求略复杂 可能出现
    > A=(1, 1) B = (4, 1), C=(5, 3), D = C – B + A
    以上代码,可以实现一个平行四边形,如果按照您的思路,我可能需要建立 SumOfPoints 与 Point 之间的__add__ 与 __ sub__ 等操作。

  • 資深大佬 : 1iuh

    你用重载运算符来做这事特别不合理, 你这样写感觉就类似 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

    @1iuh 其实我有同感,我也是在思路探索阶段

  • 資深大佬 : gwy15

    https://gist.github.com/gwy15/2149af5dfc325cfe95eccf23e4997365

  • 資深大佬 : imn1

    你需要一个 setter 来自动改变属性,参考手册__setter__相关,或 @setter
    问题是,你这里实际上是两种 Point,一种 xy 属性不变,另一种 xy 来自外部参数计算结果,不应该定义为同一个 Class
    可以考类 C 继承 Point,并加入自动设置的__setter__类方法

    至于运算符,好像不是需求重点,用了运算符,反而不能自动了(运算符意味着进行运算才触发)

  • 資深大佬 : junnplus

    可以用 memoryview 试试,每个 point 保存两个 memoryview 对象,以及操作符

  • 資深大佬 : InkStone

    其实我很想说,你需要的是函数式编程和 monad……

  • 資深大佬 : aijam

    https://gist.github.com/czheo/851a9f35056f3a3fd5ea9cbd03a7df2f

  • 主 資深大佬 : johnnyleaf

    @aijam 感谢 感谢

  • 主 資深大佬 : johnnyleaf

    @InkStone 关于函数式和 monad 我本身掌握不佳,熟悉后再做尝试。

  • 資深大佬 : nightwitch

    这种做法是严重的设计缺陷, 有可能你的变量引用链条很长, 几百行代码之前的对某个变量的更改,可能引起正在工作的代码的更改,还是悄无声息的更改. ,找 bug 能找到崩溃.

    帮你实现了一份差不多感觉的
    https://paste.ubuntu.com/p/NQ43VjrSYg/

  • 主 資深大佬 : johnnyleaf

    @junnplus 感谢 感谢,已经解决了

  • 主 資深大佬 : johnnyleaf

    @nightwitch 感谢你,你说的问题确实存在,所以我放弃了,决定定义两个类来存储这两种不同的结构。

  • 資深大佬 : hustlibraco

    @gwy15 请教一下,Vec2DF 起什么作用啊?

  • 主 資深大佬 : johnnyleaf

    @imn1 是的,我了解了有关 @setter 的东西。我决定用两个类来分别处理这两种不同的对象。感谢你

  • 資深大佬 : gwy15

    @hustlibraco type alias,带个 F 表示是个 callable,免得嵌套太长不好看

  • 資深大佬 : no1xsyzy

    @johnnyleaf #5 > 我可能需要建立 SumOfPoints 与 Point 之间的__add__ 与 __ sub__ 等操作。
    可以做一个 ABCPoint,然后把 SumOfPoints 和 Point 都注册进去,之后判断就是 isinstance(other, ABCPoint)
    或者让 SumOfPoints 继承 Point

  • 資深大佬 : neoblackcap

    不一样的类型是正确的,其实你需要的不是什么 Point,是一个 LazyPoint 或者一个公式,Excel 的公式用过吧。
    C 应该是代表 Add(A, B),需要用到值的时候再求值。完美满足你的要求。你自己控制一下类似的 Lazy 类对象的求值时机就好了。性能都可控

  • 資深大佬 : no1xsyzy

    @aijam #12 调用被放大了,复杂度 O(2^n)

  • 主 資深大佬 : johnnyleaf

    @neoblackcap 是的 我正在实现一个新类去实现你讲的类似功能。感谢

  • 資深大佬 : jmc891205

    为什么会有这种需求。
    那当你有 100 万个点,修改 1 个点就会导致另外 99 万多个点的坐标都更新,这性能岂不是太差了。

  • 主 資深大佬 : johnnyleaf

    @jmc891205 首先,目前确定的 业务中点的个数在 10 个之内。其次我决定尝试对更新做控制。不会让它自动更新的哈。

  • 資深大佬 : ipwx

    我觉得你需要的是 lazy evaluation 。通过占位符(比如 _1 + _2 )产生一个表达式对象,然后当你把真实的点倒进去的时候,给你把结果算出来。

    Point 这种对象,从语义上通常认为是不可变的常量。否则会让人 confuse 。

  • 資深大佬 : Vegetable

    https://gist.github.com/luliangce/5c1c84392a51c02cb90b62e173d12ed0

  • 資深大佬 : milkpuff

    运行一个 thread 跑死循环,判断变量是否改变,如果改变就重新计算??

  • 資深大佬 : whileFalse

    你需要的是一个新类。

    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

    了解下响应式编程?

  • 資深大佬 : GeruzoniAnsasu

    数学
    约束
    响应式

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

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

    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

    @GeruzoniAnsasu 挺好,不过我估计 lz 还想给 c 赋值,然后反过来影响 a……

  • 資深大佬 : hustlibraco

    @gwy15 Union[Vec2D, Vec2DF]的定义主要是为了 Point 既能接受坐标数组作为初始化参数,也能接受函数作为初始化数据,因此后面可以传 Lamdba 表达式初始化,从而实现 lazy evaluation 的功能?

  • 資深大佬 : noqwerty

    V 站现在能像主一样把自己需求说这么清楚的问题太少了

  • 資深大佬 : jxie0755

    你什么时候需要用 C,就在用之前再调用当时的 A 和 B 嘛。

  • 資深大佬 : lithbitren

    一个继承字典的类就能解决变量展示和修改问题了。

    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

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

文章導覽

上一篇文章
下一篇文章

AD

其他操作

  • 登入
  • 訂閱網站內容的資訊提供
  • 訂閱留言的資訊提供
  • WordPress.org 台灣繁體中文

51la

4563博客

全新的繁體中文 WordPress 網站
返回頂端
本站採用 WordPress 建置 | 佈景主題採用 GretaThemes 所設計的 Memory
4563博客
  • Hostloc 空間訪問刷分
  • 售賣場
  • 廣告位
  • 賣站?
在這裡新增小工具