簡體   English   中英

當屬性是對象列表時,如何識別屬性更改

[英]How to recognize a attribute change when the attribute is a list of objects

矩形對象如何偵聽Points屬性x和y,如果它們發生變化,矩形對象將重新計算面積?

如果我使用setter和getter進行操作,則每次訪問area屬性時,都會重新計算面積。 如果計算非常昂貴(我在這里還要做更多的事情),那么這對我來說不是最佳解決方案。 是否可以聽取積分,僅在積分改變時重新計算面積?

我有一個叫做Rectangle的類和一個叫做Point的類:

class Point(object):

    def __init__(self, x, y):
        self.x = x
        self.y = y


class Rectangle(object):

    def __init__(self, points=None):
        self.points = [] if points is None else points
        self.area = self.calc_area()

    def calc_area(self):
        return (self.points[0].x - self.points[1].x) * (self.points[0].y - self.points[1].y)

然后創建兩個點和一個包含兩個點的矩形:

# create the points:
points = list()
points.append(Point(0,0))
points.append(Point(1,1))

# create the rectangle:
rect = Rectangle(points)
print(rect.area)

現在,我更改第一點的坐標:

# change the points coordinates:
points[0].x = 0.5
points[0].y = 0.5

# Now the area should be recalculated.
print(rect.area)

解:

您可以將area聲明為property

class Rectangle(object):
    def __init__(self, points=list()):
        self.points = points
        # self.area = self.calc_area() -- removed

    @property
    def area(self):
        return = (self.points[0].x - self.points[1].x) * (self.points[0].y - self.points[1].y)

它將解決問題。

UPD。

如果希望僅在值更改時才重新計算面積,則可以使用自定義標志並將其設置在屬性設置器上。

碼:

class Point(object):
    def __init__(self, x, y):
        self._x = x
        self._y = y
        self.updated = True

    @property
    def x(self):
        return self._x

    @x.setter
    def x(self, value):
        self.updated = True
        self._x = value

    @property
    def y(self):
        return self._y

    @y.setter
    def y(self, value):
        self.updated = True
        self._y = value


class Rectangle(object):
    def __init__(self, points=None):
        self.points = [] if points is None else points
        self._area = 0

    @property
    def area(self):
        if any(point.updated for point in self.points):
            self._area = (self.points[0].x - self.points[1].x) * (self.points[0].y - self.points[1].y)
            for point in self.points:
                point.updated = False
            print("recalculated") # delete it, it's just for test
        return self._area


points = [Point(0, 0), Point(1, 1)]

rect = Rectangle(points)
print(rect.area)
print(rect.area)

points[0].x = 0.5
points[0].y = 0.5

print(rect.area)

輸出:

recalculated
1
1
recalculated
0.25

您可以:

  • 將變量x和y設置為私有,並有getter和setter來訪問它們,並且在setter中,還更新區域
  • rect.area訪問rect.area ,只需在需要該區域時調用rect.calc_area()

謝謝@OlvinR​​oght,我認為您鏈接中的問題是實現此目的的最佳解決方案。 所以現在我實現了觀察者模式。 在這里,我可以將“矩形點”列表中的每個點綁定到更新函數。

class Point(object):

    def __init__(self, x, y):
        self._x = x
        self._y = y
        self._observers = []

    @property
    def x(self):
        return self._x

    @property
    def y(self):
        return self._y

    @x.setter
    def x(self, value):
        self._x = value
        for callback in self._observers:
            print('announcing change')
            callback()

    @y.setter
    def y(self, value):
        self._y = value
        for callback in self._observers:
            print('announcing change')
            callback()

    def bind_to(self, callback):
        print('bound')
        self._observers.append(callback)


class Rectangle(object):

    def __init__(self, points=None):
        self.points = [] if points is None else points
        self.area = []

        for point in self.points:
            point.bind_to(self.update_area)
        self.area = (self.points[0].x - self.points[1].x) * (self.points[0].y - self.points[1].y)

    def update_area(self):
        print('updating area')
        self.area = (self.points[0].x - self.points[1].x) * (self.points[0].y - self.points[1].y)


if __name__ == '__main__':

    # create points:
    points = list()
    points.append(Point(0, 0))
    points.append(Point(1, 1))

    # create the rectangle:
    rect = Rectangle(points)
    print('Area = {}'.format(rect.area))

    # change point coordinates
    points[0].x = 0.5
    points[0].y = 0.5
    print('Area = {}'.format(rect.area))

    # change point coordinates again:
    points[0].x = 0.25
    points[0].y = 0.25
    print('Area = {}'.format(rect.area))

    # just print the area; the area is not recalculated:
    print('Area = {}'.format(rect.area))

輸出:

Area = 1
announcing change
updating area
announcing change
updating area
Area = 0.25
announcing change
updating area
announcing change
updating area
Area = 0.5625
Area = 0.5625

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM