我希望以前没有问过这个问题,我的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?
如果这是基于意见或过于依赖实际应用,请告知如何改进问题。
答案 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权衡,所以只有在频繁到达区域和周长时才使用它,因此它会对速度产生影响。