继承和多态
1. 继承
- 定义 class 时可以从现有 class 继承;新 class 称为子类(Subclass),被继承的称为基类/父类/超类(Base/Super class)。
- 语法:
class 子类(父类):。
class Animal(object):
def run(self):
print('Animal is running...')
class Dog(Animal):
pass
class Cat(Animal):
pass
- 继承的好处:子类自动获得父类的全部功能;Dog、Cat 未写任何代码即可调用
dog.run()、cat.run()(此时打印的是父类的Animal is running...)。
2. 方法覆盖与多态
- 子类可新增方法(如 Dog 的
eat()),也可重写父类方法(如子类定义自己的run())。 - 当子类与父类有同名方法时,子类覆盖父类;运行时总是调用子类的该方法。这种“同一接口、不同实现”即多态。
class Dog(Animal):
def run(self):
print('Dog is running...')
class Cat(Animal):
def run(self):
print('Cat is running...')
dog.run() # Dog is running...
cat.run() # Cat is running...
3. 类型与 isinstance
- 定义 class 即定义一种数据类型;自定义类型与 list、str 等用法一致。
isinstance(实例, 类):判断实例是否属于某类。子类实例同时也是父类类型;父类实例不是子类类型。
c = Dog()
isinstance(c, Dog) # True
isinstance(c, Animal) # True,Dog 是 Animal 的一种
b = Animal()
isinstance(b, Dog) # False,Animal 不能看成 Dog
记忆:子类实例 → 可看作父类;父类实例 → 不能看作子类。
4. 多态的好处
- 函数只依赖父类类型(如
Animal),传入任意子类实例均可;具体调用哪个类的run()由运行时对象类型决定。 - 新增子类(如 Tortoise)时,不必修改依赖
Animal的函数(如run_twice()),只要子类正确实现run()即可。
def run_twice(animal):
animal.run()
animal.run()
run_twice(Animal()) # Animal is running... (x2)
run_twice(Dog()) # Dog is running... (x2)
run_twice(Cat()) # Cat is running... (x2)
run_twice(Tortoise()) # Tortoise is running slowly... (x2),无需改 run_twice
- 开闭原则:对扩展开放(可新增 Animal 子类);对修改封闭(不需改依赖 Animal 的
run_twice()等函数)。
5. 继承链
- 继承可一级一级传递(爷爷 → 爸爸 → 儿子);任何类最终都追溯到根类
object,继承关系呈倒树形(如 object → Animal/Plant → Dog/Cat/...)。
6. 静态语言 vs 动态语言(鸭子类型)
- 静态语言(如 Java):传入参数必须是 Animal 或其子类,才能保证有
run()方法。 - 动态语言(如 Python):不要求严格继承;只要对象有
run()方法即可使用,这就是鸭子类型——“看起来像鸭子、走起来像鸭子,就可以当鸭子”。
class Timer(object):
def run(self):
print('Start...')
run_twice(Timer()) # 也可工作,不要求继承 Animal
- file-like object:只要有
read()方法的对象都可视为“类文件对象”;很多函数接收的就是这类对象,不必传真正的文件对象。
小结
| 要点 | 说明 |
|---|---|
| 继承 | class 子类(父类):;子类自动拥有父类全部功能 |
| 覆盖 | 子类定义同名方法则覆盖父类;运行时调用子类方法 |
| 多态 | 同一接口(如 run()),不同实现;具体调用谁由运行时类型决定 |
| isinstance | 子类实例 可看 作父类类型;父类实例 不能看作子类类型 |
| 开闭原则 | 对扩展开放(新增子类),对修改封闭(不改依赖父类的函数) |
| 鸭子类型 | Python 不强制继承;有对应方法即可,如 file-like 有 read() 就行 |
记忆:继承拿父类功能,覆盖得多态;子类实例是父类类型;鸭子类型——有方法就行。