Python学习笔记29:继承的优缺点
- 最近因为一些别的事所以没有继续学习和更新,抱歉了。今天回到学习Python的道路上,我们继续旅途。
- 本系列今后的示例代码都将存放在Github项目:https://github.com/icexmoon/python-learning-notes
从最近的几篇笔记开始,我们开始探索Python中的OOP。
前一篇笔记Python学习笔记28:从协议到抽象基类我们从协议和抽象类两个方面探讨了协议和抽象类在Python中的定位和用途。
今天我们要讨论的是Python中继承的特点,和其中的优缺点。
内建类的继承缺陷
这是一个隐蔽的问题和缺陷,至少在Java或者PHP中我并没有发现类似的问题。
至于我说的是什么,不要着急,我们往下看。
我们创建一个字典类型的子类,并重写__setitem__
魔术方法:
class SubDict(dict):
def __setitem__(self, k, v):
v = "sub_item_"+str(v)
super().__setitem__(k, v)
sd = SubDict(one="one")
print(sd)
sd['two']='two'
print(sd)
sd.update(three="three")
print(sd)
# {'one': 'one'}
# {'one': 'one', 'two': 'sub_item_two'}
# {'one': 'one', 'two': 'sub_item_two', 'three': 'three'}
我们分别通过初始化方法、直接对元素赋值、调用update
方法进行测试。
其中只有直接对元素赋值成功调用了我们重写的__setitem__
,实质上__setitem__
魔术方法本来就是对字典元素赋值操作的运算符重载,所以这是个必然结果。
而我们需要关注的是其它两种方式为何没有成功。
有一个事实我们是需要知道的,就是虽然Python的最大特点是让普通的开发者拥有和语言底层开发者一样的语言特性,但是实质上为了优化性能,并非所有的内建组件都用一致性的Python语言开发,比如最常用的组件:字典、列表和字符串,都是使用效率更高的C语言进行编译的可执行模块(对CPython解释器来说)。
这样做的好处显而易见,可以极大地提升性能,并且对普通开发者基本是没有任何影响的,但是在极个别情况下就会发生一些奇怪的现象,比如我们上面展示的那样。
如果你对这些C语言构建的内部组件进行继承并重写某些方法,很可能在调用的时候其行为并不会符合OOP中多态的行为。即父类并不会使用你重写的方法(比如__setitem__
),这就是我所说的内建类的继承缺陷。
当然Python官方也给出了解决办法,在需要对此类内建类进行继承的时候,我们不应该直接继承,而是应该继承collections
模块中的UserXXX
类,这些类是纯Python编写,不存在以上所说的问题。
我们用这种方式改写刚才的示例:
from collections import UserDict
class SubDict(UserDict):
def __setitem__(self, k, v):
v = "sub_item_"+str(v)
super().__setitem__(k