2015-01-08 252 views
2

我想弄清楚一個屬性屬性的setter觸發頂級類的某些操作的方法。設置屬性的屬性

作爲一個虛擬的例子,可以說我的頂級課程是Segment。如果我將其端點的座標直接作爲此對象的屬性x0,y0,x1y1存儲,並且讓每個人的設置者觸發所選動作,那麼這一切都很好。

但是,如果我想將它們組合成兩個Point成員屬性p0p1,各有特性xy,只要這些座標中的一個被修改,還有告訴Segment做什麼的沒有明顯的方式。這是我想什麼能夠做到:

>>> segment = Segment(Point(0, 0), Point(3, 3)) 
>>> segment.p0 
Point(0, 0) 
>>> segment.p0.x 
0 
>>> segment.p1.y = 4 
Length of segment changed to 5.0! # This can only be printed by segment, not p1! 

的問題是該行segment.p1.y = 4首先調用的p1segment情況下,吸氣,再y上一調用的返回二傳手在這一點上,沒有簡單的方法讓segment實例知道已經做出更改。

我能想到的,現在最好是沿着以下線的東西:

class Point(object): 
    def __init__(self, x, y, parent=None, name=None): 
     self.parent, self.name = parent, name 
     self._x, self._y = x, y 

    @property 
    def x(self): 
     return self._x 
    @x.setter 
    def x(self, value): 
     self._x = value 
     if parent is not None: 
      setattr(self.parent, self.name, self) 

    # Similar code for y goes here... 


class Segment(object): 
    def __init__(self, p0, p1): 
     self.p0, self.p1 = p0, p1 

    @property 
    def p0(self): 
     return self._p0 
    @p0.setter 
    def p0(self, point): 
     self._p0 = point 
     self.p0.parent = self 
     self.p0.name = 'p0' 
     if not self._silent: 
      self.do_something() # This would print the length in the above example 

    # Similar code for p1 goes here... 

雖然這確實我想它,我不太喜歡不必手動添加鏈接回什麼父,也不怎麼我要麼必須使大量風險有趣的臭蟲Point對象的冗餘副本,或者,如果做這樣的事情:

p0, p1, p2 = Point(0, 0), Point(1, 1), Point(2, 2) 
seg0 = Segment(p0, p1) 
seg1 = Segment(p0, p2) 
# The following line changes the value on both seg0 and seg1, but triggers 
# the do_something call on seg1 only! 
seg0.p0.x = 6 

有一些現成的配方嗎?任何人都可以想出一個更好的方法來做到這一點?

回答

2

也許你正在尋找Observer design pattern

import math 

class Point(object): 
    def __init__(self, x, y, name=None): 
     self.name = name 
     self._x, self._y = x, y 
     self.observers = [] 

    def observe(self, observer): 
     self.observers.append(observer) 

    def __repr__(self): 
     return 'Point({}, {})'.format(self.x, self.y) 

    @property 
    def x(self): 
     return self._x 
    @x.setter 
    def x(self, value): 
     self._x = value 
     for o in self.observers: 
      o.notify() 

    @property 
    def y(self): 
     return self._y 
    @y.setter 
    def y(self, value): 
     self._y = value 
     for o in self.observers: 
      o.notify() 

class Segment(object): 
    def __init__(self, p0, p1): 
     self._p0, self._p1 = p0, p1 
     p0.observe(self) 
     p1.observe(self) 

    def __repr__(self): 
     return 'Segment({}, {})'.format(self.p0, self.p1) 

    def notify(self): 
     print('Length of {} changed to {}'.format(self, self.length())) 

    def length(self): 
     return math.sqrt((self.p0.x - self.p1.x)**2 
         + (self.p0.y - self.p1.y)**2) 

    @property 
    def p0(self): 
     return self._p0 
    @p0.setter 
    def p0(self, point): 
     self._p0 = point 

    @property 
    def p1(self): 
     return self._p1 
    @p1.setter 
    def p1(self, point): 
     self._p1 = point 

segment = Segment(Point(0, 0), Point(3, 3)) 
print(segment.p0) 
# Point(0, 0) 
print(segment.p0.x) 
# 0 
segment.p1.y = 4 

產量

Length of Segment(Point(0, 0), Point(3, 4)) changed to 5.0 

p0, p1, p2 = Point(0, 0), Point(1, 1), Point(2, 2) 
seg0 = Segment(p0, p1) 
seg1 = Segment(p0, p2) 
seg0.p0.x = 6 

收益率

Length of Segment(Point(6, 0), Point(1, 1)) changed to 5.09901951359 
Length of Segment(Point(6, 0), Point(2, 2)) changed to 4.472135955 
+0

這當然看起來像我之後,謝謝!重溫GOF書的時間,這一次真正關注它... – Jaime