属性还是方法?

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,这有什么不同吗?有一种方式更可取吗?

Mar*_*ers 9

你有一个第三选择:让两者的属性和方法,通过使用属性:

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任何未调用的值(()在名称后面不使用).