use*_*183 6 python methods attributes class
我正在编写一个python脚本,它根据两个参数计算各种数量,即球体的长半径和短半径.在我看来,我可以写一个球体类来做到这一点.但是,我是面向对象设计的新手,并且想知道你是否有更多经验丰富的人可以帮助我.
实例用参数a和b分别实例化为长半径和短半径,所以我按如下方式设计了类:
class Spheroid:
def __init__(self,a,b):
self.longax = a
self.shortax = b
Run Code Online (Sandbox Code Playgroud)
我想要计算的数量之一是音量.球状体的体积为4*pi/3*a*b*b.
我的问题是,我在课堂上为此定义一个方法或属性吗?
例如,我可以定义一个方法:
def Volume(self):
return 4*pi/3 * self.longax * self.shortax * self.shortax
Run Code Online (Sandbox Code Playgroud)
或者我可以使用一个属性:
self.volume = 4*pi/3 * self.longax * self.shortax * self.shortax
Run Code Online (Sandbox Code Playgroud)
我也可以将它包含在init方法中:
class Spheroid:
def __init__(self,a,b):
self.longax = a
self.shortax = b
self.volume = 4*pi/3 * a * b * b.
Run Code Online (Sandbox Code Playgroud)
哪个更好用,为什么?一般来说,我什么时候使用方法?何时使用属性?我通常不会关心,但我有很多这些要实现,我想了解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
Run Code Online (Sandbox Code Playgroud)
我实例化了一个实例s = Spheroid(),但每当我尝试像s.volume或s.epsilon这样的东西时,我得到一个AttributeError:
AttributeError:'Spheroid'对象没有属性'volume'
我在这做错了什么?
另外,在我的init方法中,我使用了self.alpha = self.longax/self.shortax而不是a/b,这有什么不同吗?有一种方式更可取吗?
你有一个第三选择:让两者的属性和方法,通过使用属性:
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
Run Code Online (Sandbox Code Playgroud)
您可以.volume
像属性一样访问:
>>> s = Spheroid(2, 3)
>>> s.volume
75.39822368615503
Run Code Online (Sandbox Code Playgroud)
为了使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
Run Code Online (Sandbox Code Playgroud)
您可以轻松缓存属性计算的结果:
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
Run Code Online (Sandbox Code Playgroud)
这样你只需要为每个实例计算一次Spheroid
.
你使用什么取决于许多因素; 多么容易贵API需要注册后才能使用,隔多少量来计算,怎么许多球体实例将被创建,等等.如果你在一个循环中创建的这些百万,但只有永远都需要的容积为他们的福,使用属性而不是在中设置音量是有意义的__init__
.
但是,如果您的班级可以根据音量调整自己; 比方说,通过自动调整其中一个半径,a @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))
Run Code Online (Sandbox Code Playgroud)
现在你有一个球体,当你调节音量时它会自然地调整它的短属性:
>>> s = Spheroid(2, 1)
>>> s.volume
8.377580409572781
>>> s.volume = 75.39822368615503
>>> s.long, s.short
(2, 3.0)
Run Code Online (Sandbox Code Playgroud)
注意:从技术上讲,您在带符号的对象上访问的任何内容.name
都是属性; 方法包括在内 出于这个答案的目的,我使用了你的attribute
任何未调用的值(()
在名称后面不使用).