我正在编写一个 python 脚本,它根据两个参数计算各种数量,即椭球体的长半径和短半径。我想到我可以编写一个球体类来执行此操作。但是,我是面向对象设计的新手,想知道你们更有经验的人是否可以帮助我。
一个实例是用参数a和b分别为长半径和短半径实例化的,所以我设计了类如下:
class Spheroid:
def __init__(self,a,b):
self.longax = a
self.shortax = b
我要计算的数量之一是体积。球体的体积是 4*pi/3 * a * b * b。
我的问题是,我是在我的类中为此定义方法还是属性?
例如我可以定义一个方法:
def Volume(self):
return 4*pi/3 * self.longax * self.shortax * self.shortax
或者我可以只使用一个属性:
self.volume = 4*pi/3 * self.longax * self.shortax * self.shortax
我也可以将它包含在 init 方法中:
class Spheroid:
def __init__(self,a,b):
self.longax = a
self.shortax = b
self.volume = 4*pi/3 * a * b * b.
哪个更好用,为什么?一般来说,什么时候使用方法,什么时候使用属性?我通常不会关心,但我有一大堆这些要实现,我想了解 OO 设计以供将来引用。
谢谢
编辑:
按照 Martijn 的建议实现属性后,我得到了这样的结果:
class Spheroid(object):
def __init__(self,a,b):
self.shortax = a
self.longax = b
self.alpha=self.longax/self.shortax
@property
def volume(self):
return (4*np.pi/3) * self.shortax * self.shortax * self.longax
@property
def epsilon(self):
return np.sqrt(1-self.alpha**(-2))
@property
def geometricaspect(self):
return 0.5 + np.arcsin(self.epsilon)*0.5*self.alpha/self.epsilon
@property
def surfacearea(self):
return 4*np.pi*self.shortax**2*self.geometricaspect
我实例化了一个实例 s = Spheroid() 但每当我尝试类似 s.volume 或 s.epsilon 的东西时,我都会得到一个 AttributeError:
AttributeError: 'Spheroid' 对象没有属性 'volume'
我在这里做错了什么?
此外,在我的 init 方法中,我使用 self.alpha = self.longax/self.shortax 而不是 a/b,这有什么区别吗?一种方式更可取吗?
最佳答案
您有第三种选择:通过使用 property 使其既是属性又是方法。 :
class Spheroid(object):
def __init__(self, a, b):
self.long = a
self.short = b
@property
def volume(self):
return 4 * pi / 3 * self.long * self.short * self.short
您像访问属性一样访问 .volume
:
>>> s = Spheroid(2, 3)
>>> s.volume
75.39822368615503
为了使property
描述符正常工作,在Python 2 中您需要确保您的类继承自object
;在 Python 3 中,可以安全地省略基类。
在这种情况下,体积的计算足够便宜,但是属性可以让您推迟必须计算体积,直到您真正需要它。
上面的例子创建了一个只读属性;只定义了一个 setter/getter :
>>> s.volume = None
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
您可以轻松缓存属性计算的结果:
class Spheroid(object):
_volume = None
def __init__(self, a, b):
self.long = a
self.short = b
@property
def volume(self):
if self._volume is None:
self._volume = 4 * pi / 3 * self.long * self.short * self.short
return self._volume
因此您只需为每个 Spheroid
实例计算 一次。
您使用什么取决于许多因素;您的 API 需要多容易使用,计算体积的频率,将创建多少 man Spheroid 实例,等等。如果您在一个循环中创建一百万个这样的实例,但只需要它们的音量,使用属性而不是在 __init__
中设置音量是有意义的。
但是,如果您的类(class)可以根据音量调整;例如,通过自动调整其中一个半径,@property
仍然更有意义:
class Spheroid(object):
def __init__(self, a, b):
self.long = a
self.short = b
@property
def volume(self):
return 4 * pi / 3 * self.long * self.short * self.short
@volume.setter
def volume(self, newvolume):
# adjust the short radius
self.short = sqrt(newvolume / (4 * pi / 3 * self.long))
现在您有一个球体,它会在您调整音量时自然地调整它的短属性:
>>> s = Spheroid(2, 1)
>>> s.volume
8.377580409572781
>>> s.volume = 75.39822368615503
>>> s.long, s.short
(2, 3.0)
注意:从技术上讲,任何您使用.name
表示法访问的对象都是一个属性;包括的方法。出于此答案的目的,我将您的 attribute
用作任何未调用的值(名称后不使用 ()
)。
关于python - 属性还是方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15531852/