【问题标题】:Setting the property of a property设置属性的属性
【发布时间】:2015-01-08 18:52:54
【问题描述】:

我正在尝试找出一种方法,让属性的属性的设置器触发顶级类的某些操作。

作为一个虚拟示例,假设我的顶级课程是Segment。如果我将其端点的坐标直接存储为对象x0y0x1y1 的属性,并让每个对象的设置器触发所选操作,那么一切都很好。

但是如果我想将它们分成两个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这行首先在segment实例上调用p1的getter,然后在上一个调用的返回上调用y的setter,此时不简单让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

有现成的食谱吗?任何人都可以想出更好的方法吗?

【问题讨论】:

    标签: python properties


    【解决方案1】:

    也许您正在寻找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
    

    【讨论】:

    • 这看起来确实像我所追求的,谢谢!是时候重温一下GOF的书了,这次真的是在关注它……
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-30
    • 2023-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多