为什么不总是使用Python属性?

时间:2018-11-15 08:11:43

标签: python properties encapsulation

我开始用Python学习编程,并且有一段时间没有接触过public / private的概念。在学习了Java的概念之后,我仍然没有看到它的太多用途,并且总是喜欢Python的“我们都是成年人”的原则。尤其是当通常的get / set方法向单行添加了如此多的代码行时。最终,在用C ++编写大量项目后,我开始了解它的好处,例如允许封装实现细节。它还清楚地表明了何时不应直接设置变量,因为其设置应具有副作用。

当我的Python程序变得足够大时,我常常会忘记一个属性是否仅在内部使用,并且不知道该属性是否可以安全更改。通常,我最终会更改属性,运行程序,遇到异常,修复相关代码中的错误并重复执行。在Python中,我们可以使用领先的下划线pseduo-private约定和属性函数来获得这些优势,同时仍然允许代码访问所需的内容。

作为一个例子,这里是一个点类。实际上,不允许直接设置属性“ x”和“ y”,因为“距离”属性需要随时重新计算(这可能是一个非常复杂的表达式,每次重新计算都将花费很长时间)。

class Point:
def __init__(self, x, y):
    self._x = x
    self._y = y
    self._distance = (x**2 + y**2) ** (1/2)

@property
def x(self):
    return self._x

@property
def y(self):
    return self._x

@property
def distance(self):
    return self._distance

如果您的属性是只读的,则代码可以非常简洁。

class Point:
    x = property(lambda self: self._x)
    y = property(lambda self: self._y)
    distance = property(lambda self: self._distance)

    def __init__(self, x, y):
        self._x = x
        self._y = y
        self._distance = (x**2 + y**2) ** (1/2)

我真的很喜欢后来的风格,但是并没有广泛使用它。设置大多数“公共”属性有什么缺点吗?我只能想到:

  • 可读性?就我个人而言,后一种样式对我来说更具可读性,因为您在不实现该类时关心的所有属性都是清晰明了的。
  • 其他开发者不清楚吗?在单独项目中使用它时,这不是问题。但这显然符合Python的规则,应该很容易理解。
  • 效率低吗?这是间接的另一层,因此有可能,但是属性访问通常不是一个非常有限的情况。
  • 写/设置不是那么快吗?不必为每个类和可能的添加方法都完全遵循这种样式,但是可以在确实开始采用可靠的实现时添加该样式。
  • 缺少Pythonic吗?可能,但是我觉得Pythonic的标准不应是任意的,而应与可读性和高效性有关。

1 个答案:

答案 0 :(得分:0)

在必要时将属性设为只读。默认情况下,不要将每个attr设为只读。

使用属性实现只读属性没有错。这就是属性成为Python一部分的原因之一。另一方面,将大多数属性设置为只读并没有什么特别的权利。它违反了标准的Python习惯用法,使您的类难以与之交互(特别是在您自己的代码库中)。请记住,Python中没有C ++ private关键字的实际等效项。例如,即使x是只读的,也没有阻止用户致力于用脚self._x = 19射击自己的行为。

通常,使用领先的下划线_name标记内部属性和公共属性就足够了:

class Foo:
    def __init__(self, bar, baz):
        self._mine = bar
        self.public = baz

按照设计,您的x类的yPoint属性是只读属性的理想选择。但是,我想说,灵活地设计类并将距离设为方法更像Pythonic:

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def distance(self):
        return (x**2 + y**2) ** (1/2)

或者,如果性能存在问题,请将distance保留为属性,但是只要xy发生更改,就重新对其进行计算:

class Point:
    @staticmethod
    def _distance(x, y):
        return (x**2 + y**2) ** (1/2)

    @property
    def x(self):
        return self._x
    @x.setter
    def x(self, x):
        self._x = x
        self._distance = Point._distance(x, self._y)

    @property
    def y(self):
        return self._y
    @y.setter
    def y(self, y):
        self._y = y
        self._distance = Point._distance(self._x, y)

    distance = property(lambda self: self._distance)

    def __init__(self, x, y):
        self._x = x
        self._y = y
        self._distance = Point._distance(x, y)