python - 为什么不总是使用 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)
我真的很喜欢后来的风格,但并没有真正广泛地使用它。制作大多数“公共”属性属性有什么缺点吗?我只能想到:
- 可读性?就我个人而言,后一种风格对我来说更具可读性,因为您在不实现该类时关心的任何属性都是清晰且最重要的。
- 其他开发人员不清楚?在个人项目中使用它时,这不是问题。但这显然遵循 Python 的规则,应该很容易理解。
- 效率低?这是另一层间接性,因此这是可能的,但是属性访问通常不是非常有限的情况。
- 编写/设置不那么快?不必对每个类和可能的添加都完全遵循这种风格,但可以在可靠的实现真正开始形成时添加。
- 不那么 Pythonic?可能,但我觉得 Pythonic 的标准不应该是任意的,并且与可读性和效率有关。
解决方案
如果有必要,将属性设为只读。默认情况下,不要将每个 attr 设为只读。
使用属性来实现只读属性并没有错。这就是为什么属性是 Python 的一部分的原因之一。另一方面,将大多数属性设置为只读并没有什么特别正确的。它违背了标准的 Python 习惯用法,使您的类更难交互(尤其是在您自己的代码库中)。private
请记住, Python 中的 C++ 关键字没有实际等效项。例如,即使x
是只读的,也没有什么能阻止一个致力于射击自己的用户self._x = 19
无论如何都要做。
通常,使用前导下划线_name
约定来标记内部属性和公共属性就足够了:
class Foo:
def __init__(self, bar, baz):
self._mine = bar
self.public = baz
按照设计,您的类的x
和y
属性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
为属性,但随时重新计算x
或y
更改:
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)
推荐阅读
- facebook-javascript-sdk - 为什么我无法在即时游戏上加载插页式广告?
- c++ - 为什么我的代码中的根函数不起作用?
- eclipse - 使用 minikube 部署 Che 失败 -> ✖ 验证是否安装了 kubectl → E_REQUISITE_NOT_FOUND
- som - 如何使用 MINISOM 库标记数据
- flutter - Flutter WEB请求到本地服务器
- .net - 使用 SSH 公钥/私钥连接到 sFTP 服务器。错误 {Invalid private key file.} 使用 SSH.NET
- algorithm - Barnes-Hut 算法插入质量中心偏移
- parsing - Altair Monarch 能否连接到 Azure blob 存储以使用数据?
- r - 将分隔的字符串拆分为多列并将它们分成行
- timer - 我应该在 asp net core 3.1 中使用什么进行定期操作?