Python 将一个类';属性及其确定方法?

Python 将一个类';属性及其确定方法?,python,class,Python,Class,我希望以前没有人问过这个问题,我的google/SX fu在这方面不是很好,因为我可能不知道正确的关键字 假设我有一个表示相当复杂对象的类,例如。G具有特定属性(长度、体积…)的点云。通常,我会为点云(或在本例中为矩形)定义一个类,如下所示(示例由提供,修改): 每当我需要知道矩形的面积时,我只需调用my_rectangle.area(),即使矩形的尺寸发生变化,它也会给出正确的结果 现在在我的应用程序中,计算周长或面积要复杂得多,并且需要花费相当多的时间。此外,通常,我需要比修改对象更频繁地了

我希望以前没有人问过这个问题,我的google/SX fu在这方面不是很好,因为我可能不知道正确的关键字

假设我有一个表示相当复杂对象的类,例如。G具有特定属性(长度、体积…)的点云。通常,我会为点云(或在本例中为矩形)定义一个类,如下所示(示例由提供,修改):

每当我需要知道矩形的面积时,我只需调用
my_rectangle.area()
,即使矩形的尺寸发生变化,它也会给出正确的结果

现在在我的应用程序中,计算周长或面积要复杂得多,并且需要花费相当多的时间。此外,通常,我需要比修改对象更频繁地了解周长。因此,将计算与访问值本身分离是有意义的:

class Rectangle:
    def __init__(self,x,y):
        self.x = x
        self.y = y
    def calc_area(self):
        self.area = self.x * self.y
    def calc_perimeter(self):
        self.perimeter = 2 * self.x + 2 * self.y
现在,如果我需要知道矩形的面积,我需要在任何修改后调用
my\u rectangle.calc\u area()
至少一次,但之后我总是可以得到
my\u rectangle.area

这是个好主意还是我应该将面积计算保留在
.area()
方法中,并在需要时访问它,同时将当前面积存储在使用我的
矩形
类的任何脚本中的局部变量中

如果这是一个过于基于意见或过于依赖实际应用程序的问题,请建议如何改进该问题。

好吧,这不是一个真正的“pythonic/not pythonic”问题。你问的是设计:缓存还是不缓存

我要做的是在
\uuuu init\uuuu
方法中计算面积和周长。如果您的类是可变的(您将从外部修改x和y),您将需要使用,其中还应该更新面积和周长


另外,请记住,这是一个经典的CPU/RAM折衷方案,因此只有在获得足够频繁的面积和周长时才使用它,这样会在速度上产生差异。

首先,这似乎是对属性的一个很好的使用:

...
@property
def area(self):
    ...
class Rectangle(object):
    def __init__(self, w, h):
        self._width = w
        self._height = h

    @property 
    def width(self):
        return self._width

    @width.setter
    def width(self, w):
        self._width = w 
        del self._area

    @property 
    def height(self):
        return self._height

    @height.setter
    def height(self, w):
        self._height = w 
        del self._area

    @cached
    def area(self):
        return self._width * self._height
然后您可以访问我的矩形区域

如果计算面积是一个漫长的过程,并且您不想让类用户为此烦恼,请在第一次访问
area
属性时计算面积。在后续访问中,只返回计算值


如果对象中的某些内容发生变化,并且需要重新计算该区域,则只需在x或y发生变化时将该区域标记为未计算(您也可以将其设置为
@property
s)

是,至少是原则。另一方面,计算是否值得缓存是另一个问题

至少在某些情况下,属性或属性是在第一次在标准库中使用时计算出来的,而标准库中的内容可能应该被视为pythonic。从那里开始,将其用于在对象生命周期内可能更改的属性的步骤不应考虑太远(但是您需要记住,每当需要重新计算缓存值时,都要使其无效)

但是在这种情况下,他们使用
@property
装饰器使属性像属性一样被访问。此外,如果依赖于其他属性,我们有更多的理由使用属性:

...
@property
def area(self):
    ...
class Rectangle(object):
    def __init__(self, w, h):
        self._width = w
        self._height = h

    @property 
    def width(self):
        return self._width

    @width.setter
    def width(self, w):
        self._width = w 
        del self._area

    @property 
    def height(self):
        return self._height

    @height.setter
    def height(self, w):
        self._height = w 
        del self._area

    @cached
    def area(self):
        return self._width * self._height
注意
self.width
设置器中的
del self.\u区域
,它将使下一次访问
区域
时需要重新计算
self.\u区域

在这种情况下,您可以将
self.\u area
设置为
None
,并在其他答案中进行检查。但是,如果属性可以将
None
作为有效值,则此技术可能不起作用。
try
-
except
方法可以处理这种可能性。对于更一般的方法,您可以定义自己的装饰器:

def cached(calc):
    def getter(self):
        key = "_" + calc.__name__

        try:    
            return getattr(self, key)
        except AttributeError:
            val = calc(self)
            setattr(self, key, val)
            return val
    return property(getter)
然后在
矩形
的定义中,将
区域
定义为:

    @cached
    def area(self):
        return self._width * self._height

房地产确实是我们的出路。我会提出以下建议:

class Rectangle:
    def __init__(self, x, y):
        # member names starting with underscore indicate private access
        self._x = x
        self._y = y

        # it's good practice to initialize all members in __init__
        self._area = None

    @property
    def area(self):
        # this is a read-only property.
        # access it like:
        #   rect = Rectangle(1, 1)
        #   print(rect.area)
        # note the missing parentheses.
        if self._area is None:
            # lengthy computation here, but only when needed
            self._area = self._x * self._y
        return self._area

    @property
    def x(self):
        # getter for self._x
        return self._x

    @x.setter
    def x(self, value):
        # setter for self._x
        self._x = value
        # invalidate self._area - it will get recalculated on next access
        self._area = None

    # getters and setters for y are similar.

我认为这是一个更好的回答这个问题的地方。我可以问一下,为什么使用try/except比检查self.\u区域是否为
None
,并将其设置为
None
,而不是在设置宽度时将其删除更好?@lagerber在这种特殊情况下,两种技术之间几乎没有区别。但是,
try
-
except
方法可以处理无法找到用作标记的属性的无效值的情况。对我来说,这似乎只适用于Python 3,但这很好。@Raketenolli和python2必须从
对象
继承。我已经用这个更新了代码(纠正了一些错误,使实现更加完整)。在python3中,为了使属性可用,您不必从
对象继承,但这也不会造成任何伤害。