27. 优先使用公有属性

Python的类有两种属性:公有属性和私有属性

27. 优先使用公有属性

任何时候都可以通过句点运算符访问公有属性:

27. 优先使用公有属性

私有属性是名字以两个下划线为前缀的属性,它们可以通过类的内部方法进行访问:

27. 优先使用公有属性

如果在类的外部直接访问私有属性会报错:

27. 优先使用公有属性

类方法也可以直接访问类的私有属性,因为类方法也是定义在类内部的:

27. 优先使用公有属性

即使子类也不能访问父类的私有属性:

27. 优先使用公有属性

抛出异常:

>>> AttributeError: 'MyChildObject' object has no attribute '_MyChildObject__private_field'

Python解析器在遇到访问私有属性时会做一个简单的转换。例如上面的方法访问__private_field时会自动转换为访问_MyChildObject__private_field。由于__private_field只定义在了__init__中,所以__private_field的真正名字是_MyParentObject__private_field,所以在子类中访问__private_field肯定是找不到了。

知道了这个逻辑,你就知道怎么访问私有属性了:

27. 优先使用公有属性

如果使用对象字典属性你就能看到这个私有属性了:

27. 优先使用公有属性

为什么私有属性不能绝对可见呢?最简单的答案就是:We are all consenting adults here. 就是我们爱怎么干就怎么干。Python程序员认为,开放的好处大于封闭的坏处(Python programmers believe that the benefits of being open outweigh the downsides of being closed)。

除此之外,Python使得你能够在任何时候访问对象的内部,例如访问对象的属性(语言的钩子功能)。如果你这样做了,那么Python阻止你访问对象私有属性还有什么价值吗?

为了减少访问内部属性造成破坏,Python程序员应遵循PEP 8的命名规范。以一个下划线开头的属性(例如:_protected_field)是受保护属性(protected),意味着外部用户应该小心使用这些属性。

然而有些新手使用私有属性来表示子类或者外部对象不能访问的接口,例如:

27. 优先使用公有属性

这并不是最好的方式。不可避免的,很多人都会通过构建子类或其它方法来避免现有方法中的缺陷(就像上面的MyClass.get_value,永远都能访问私有属性并返回一个字符串)。通过使用私有属性,你能让你的子类重写和扩展变得困难。而你的子类用户仍然能通过其它方式访问私有属性,例如下面代码:

27. 优先使用公有属性

如果类的层级发生改变,那么上面的代码就不能工作了。比如给MyClass增加一个新的基类,将原理的私有属性__value放到新的基类里面:

27. 优先使用公有属性

由于私有属性__value现在被定义在了MyBaseClass里面了,所以在MyIntegerSubclass访问self._MyClass__value 将会出错:

27. 优先使用公有属性

一般来说,最好允许子类通过受保护的属性做更多事情。为每一个私有属性编写文档,表明哪个属性在子类的接口中是可见的,哪个属性完全不可见。这既是对其他程序员的建议,也是对您未来如何安全地扩展自己的代码的指导。

27. 优先使用公有属性

唯一真正需要使用私有属性的情况是,你担心这个属性在子类中可能会产生冲突。

27. 优先使用公有属性

这种担心主要发生在公共接口中,由于子类不受你的控制,所以你没法通过代码重构来解决问题。这种问题通常都是一个非常通用的命名引起的,就像很多时候都会用到value命名变量一样。为了减少这种风险的发生,你可以通过在父类中定义私有属性的方式避免在子类中发生重写:

27. 优先使用公有属性


分享到:


相關文章: