是否可以/ pythonic分隔类的属性和确定它的方法?

时间:2017-06-26 09:36:57

标签: python class

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

假设我有一个代表相当复杂的对象的类,例如: G。点云,具有某些属性(长度,体积......)。通常,我会为点云定义一个类(或者在这种情况下,一个矩形),像这样(示例由A Beginner's Python Tutorial提供,修改):

class Rectangle:
    def __init__(self,x,y):
        self.x = x
        self.y = y
    def area(self):
        return self.x * self.y
    def perimeter(self):
        return 2 * self.x + 2 * self.y

每当我需要知道矩形的区域时,我只需要调用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_rectangle.calc_area()一次,但之后我总是得到my_rectangle.area

这是一个好主意还是我应该在.area()方法中保留区域计算并在需要时随时访问它,同时将当前区域存储在本地变量中,无论哪个脚本使用我的{{1 class?

如果这是基于意见或过于依赖实际应用,请告知如何改进问题。

4 个答案:

答案 0 :(得分:2)

是的,至少原则。另一方面,如果值得缓存计算是一个不同的问题。

至少有一些情况是第一次在标准库中使用属性或属性时计算属性或属性 - 标准库中的内容应该被认为是pythonic。从那里开始使用它来处理在对象生命周期内可以更改的属性的步骤不应该被认为太过分了(但是你需要记住在需要重新计算时使缓存的值无效)。

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

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

请注意del self._area的设置内容中的self.width,该.area会对self._area进行下一次访问,要求重新计算self._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) 方法可以处理这种可能性。对于更一般的方法,您可以定义自己的装饰器:

Rectangle

然后在area的定义中将 @cached def area(self): return self._width * self._height 定义为:

function customtheme_customize_css()
{
 ?>
 <style type="text/css">
 h1{background:<?php echo get_theme_mod('background_color','#fff'); ?>}
 </style>
<?php
}
add_action( 'wp_head', 'wpdocs_style' );

答案 1 :(得分:2)

属性确实是去这里的方式。我会建议以下几点:

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.

答案 2 :(得分:1)

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

...
@property
def area(self):
    ...

然后您可以访问my_rectangle.area

如果计算区域是一个漫长的过程,并且您不希望类用户打扰它,请在第一次访问area属性时进行计算。在后续访问中,只返回计算值。

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

答案 3 :(得分:0)

嗯,那不是真正的&#34; pythonic / not pythonic&#34;题。您正在询问设计:缓存或不缓存。

我要做的是用__init__方法计算面积和周长。如果您的课程是可变的(您将从外部修改x和y),您将要使用setters,其中还应更新区域和周边。

另外,请记住,这是一个经典的CPU / RAM权衡,所以只有在频繁到达区域和周长时才使用它,因此它会对速度产生影响。