首页 > 解决方案 > 为什么不总是使用 Python 属性?

问题描述

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

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

作为一个例子,这里是一个点类。属性 'x' 和 'y' 真的不应该被直接设置,因为 'distance' 属性需要随时重新计算(这也可能是一个足够复杂的表达式,每次重新计算都会花费太长时间)。

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)

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

标签: pythonpropertiesencapsulation

解决方案


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

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

通常,使用前导下划线_name约定来标记内部属性和公共属性就足够了:

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

按照设计,您的类的xy属性Point是只读属性的良好候选者。但是,我想说灵活地设计你的类并使距离成为一种方法更符合 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)

推荐阅读