我正在编写一个python脚本,它根据两个参数计算各种数量,即球体的长半径和短半径。在我看来,我可以写一个球体类来做到这一点。但是,我是面向对象设计的新手,并且想知道你是否有更多经验丰富的人可以帮助我。
实例用参数a和b分别实例化了长半径和短半径,所以我按如下方式设计了类:
class Spheroid:
def __init__(self,a,b):
self.longax = a
self.shortax = b
我想要计算的数量之一是音量。球状体的体积为4 * pi / 3 * a * b * b。
我的问题是,我是否在课堂上为此定义了方法或属性?
e.g。我可以定义一个方法:
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,这有什么不同吗?有一种方式更好吗?
答案 0 :(得分:9)
您有第3个选项:使用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中,可以安全地省略基类。
在这种情况下,音量的计算足够便宜,但属性允许你推迟必须计算音量直到你真正需要它。
上面的示例创建了一个只读属性;只定义了一个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
这样您只需每{/ 1}个实例计算一次 。
您使用的内容取决于许多因素;您需要使用API的容易程度,计算量的频率,创建许多 Spheroid实例的方式等等。如果您在循环中创建了数百万个,但只需要对于其中一小部分的音量,使用属性而不是在Spheroid
中设置音量是有意义的。
但是,如果您的课程可以根据音量调整;比方说,通过自动调整其中一个半径,__init__
更有意义:
@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
)。
答案 1 :(得分:2)
你会一直使用这些数据吗?
如果没有,你可以使用一个属性,然后懒洋洋地计算它......
class Spheroid(object):
def __init__(self,a,b):
self.longax = a
self.shortax = b
self._volume = None
@property
def volume(self):
if self._volume is None :
self._volume = 4*pi/3 * self.longax * self.shortax * self.shortax
return self._volume
答案 2 :(得分:1)
由于以下原因,我将实现卷作为方法: