(八)继承、重写与同一个实例
1. 子类沿用已有字段与方法
# EBook(Book) 表示 EBook 继承 Book;先沿用父类的初始化。
class Book:
def __init__(self, title):
self.title = title
def describe(self):
return f"书籍:{self.title}"
class EBook(Book):
def download_name(self):
return f"{self.title}.pdf"
book = EBook("Python")
print(book.describe())
print(book.download_name())
print(isinstance(book, Book), type(book).__name__)输出“书籍:Python”“Python.pdf”与 True EBook。Book 是基类,也叫父类;EBook 是派生类,也叫子类。EBook 没有自己定义 __init__,调用时找到继承的初始化方法,并用它设置这个 EBook 实例的属性。
isinstance(book, Book) 判断对象是否为 Book 或其子类实例,结果为真;type(book) 则取得实际类型,这里是 EBook。创建一个 EBook 并没有额外创建一个独立的 Book 实例再放进去,父类方法与子类方法操作的是同一个对象。
2. 重写方法并接着执行已有处理
现在给电子书增加大小字段,同时在说明中显示它:
# 子类自己定义 __init__ 后,明确用 super 接着执行已有初始化。
class Book:
def __init__(self, title):
self.title = title
def describe(self):
return f"书籍:{self.title}"
def show(self):
print(self.describe())
class EBook(Book):
def __init__(self, title, megabytes):
super().__init__(title)
self.megabytes = megabytes
def describe(self):
base = super().describe()
return f"{base},{self.megabytes} MB"
book = EBook("Python", 12)
book.show()
print(book.title, book.megabytes)输出“书籍:Python,12 MB”与 Python 12。子类定义同名方法,称为重写。创建时,EBook 的初始化把当前实例继续交给父类初始化,父类写入 title 后返回,子类再写入 megabytes。
调用 book.show() 找到 Book 中的 show,但 self 仍是这个 EBook 实例。内部的 self.describe() 因而找到重写后的版本;重写版本通过 super().describe() 取得已有说明,再补上大小。
若子类自己定义初始化方法,Python 不会再额外替它执行一次父类初始化;需要的共同初始化由这次显式调用连接起来。这样也能看清字段在何时建立,而不是仅仅把父类当成已经准备好的另一份对象。
3. 子类调用类方法与 Self 注解
前面的类方法把接收到的类记作 cls。通过子类调用时,它得到的也会是子类:
# Self 表示与实际调用类相对应的实例类型,来自标准库 typing。
from typing import Self
class Book:
def __init__(self, title):
self.title = title
@classmethod
def from_title(cls, title: str) -> Self:
return cls(title.strip())
class EBook(Book):
pass # 先完整沿用已有行为,pass 作为不执行动作的占位语句。
book = EBook.from_title(" Python ")
print(type(book).__name__, book.title)结果是 EBook Python。Self 是类型注解名称,和实例方法中的形参 self 不是同一个东西。实际创建对象的仍是 cls(...);注解只是向类型工具说明,返回值与这次调用所用的类相对应。
本例 EBook 沿用 Book 的创建参数。前一段增加 megabytes 的版本,还需要在自己的创建入口中安排那个值。类方法能复用多少处理,也要沿实际的参数绑定过程阅读。