设置属性的属性
Setting the property of a property
我正在尝试找出一种方法,让属性的设置器在顶层触发某些操作 class。
作为一个虚拟示例,假设我的顶级 class 是 Segment
。如果我将其端点的坐标直接存储为该对象 x0
、y0
、x1
和 y1
的属性,并且让每个端点的设置器触发选定的操作。
但是如果我想将它们分组为两个 Point
成员作为属性 p0
和 p1
,每个成员都具有属性 x
和 y
,每当这些坐标之一被修改,没有明显的方式告诉 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,然后在[=37上调用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...
虽然这符合我的要求,但我不太喜欢手动将 link 添加回父级,也不太喜欢制作 link 的大量冗余副本=8=] 对象,或者如果做这样的事情会冒有趣的错误的风险:
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
有现成的食谱吗?谁能想出更好的方法?
也许您正在寻找 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
我正在尝试找出一种方法,让属性的设置器在顶层触发某些操作 class。
作为一个虚拟示例,假设我的顶级 class 是 Segment
。如果我将其端点的坐标直接存储为该对象 x0
、y0
、x1
和 y1
的属性,并且让每个端点的设置器触发选定的操作。
但是如果我想将它们分组为两个 Point
成员作为属性 p0
和 p1
,每个成员都具有属性 x
和 y
,每当这些坐标之一被修改,没有明显的方式告诉 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,然后在[=37上调用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...
虽然这符合我的要求,但我不太喜欢手动将 link 添加回父级,也不太喜欢制作 link 的大量冗余副本=8=] 对象,或者如果做这样的事情会冒有趣的错误的风险:
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
有现成的食谱吗?谁能想出更好的方法?
也许您正在寻找 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