【python零基础教程第9讲】继承、多态、魔法函数
Python面向对象编程深度解析:继承、多态、魔法函数与高级实践
一、继承与多态
1.1 单继承:子类复用父类代码
继承是面向对象编程的核心机制之一,允许子类复用父类的属性和方法,同时可以扩展或修改已有功能。单继承是最简单的继承形式,一个子类只继承一个父类。
class Contact:
all_contacts = []
def __init__(self, name, email):
self.name = name
self.email = email
Contact.all_contacts.append(self)
class Supplier(Contact):
def order(self, order):
print(f"向 {self.name} 下单:{order}")
# 使用
s = Supplier("张三", "zhangsan@example.com")
s.order("100件商品") # 输出:向 张三 下单:100件商品
print(s.name) # 输出:张三
在这个例子中,Supplier 类继承了 Contact 类的 __init__ 方法和 all_contacts 属性,并添加了 order 方法。
1.2 super() 调用父类构造/方法
当子类需要扩展父类的初始化逻辑时,可以使用 super() 调用父类的 __init__ 方法,避免重复代码并确保父类初始化正确执行。
class Friend(Contact):
def __init__(self, name, email, phone):
super().__init__(name, email) # 调用父类构造
self.phone*********** = Friend("李四", "lisi@example.com",***********00")
print(f.name, f.phone) # 输出:李四 13800138000
super() 不仅用于构造函数,也可以用于普通方法。它按照方法解析顺序(MRO)调用下一个类的方法,在多重继承中尤为重要。
1.3 方法重写
子类可以重新定义父类的方法,实现自己的行为。通过 super() 可以在重写的方法中调用父类的版本,实现扩展而非完全覆盖。
class Animal:
def eat(self):
print("Animal can eat")
class Cat(Animal):
def eat(self):
super().eat() # 调用父类方法
print("Cat can eat chicken")
cat = Cat()
cat.eat()
# 输出:
# Animal can eat
# Cat can eat chicken
1.4 多态实现
多态是指不同类的对象对同一消息做出不同响应的能力。Python 是动态类型语言,多态天然支持——只要对象有相应的方法,就可以调用。
class Dog:
def speak(self):
return "汪汪"
class Cat:
def speak(self):
return "喵喵"
def animal_sound(animal):
print(animal.speak())
animal_sound(Dog()) # 输出:汪汪
animal_sound(Cat()) # 输出:喵喵
多态让代码更加灵活,无需关心对象的具体类型,只需保证对象实现了所需的方法。
1.5 多继承与MRO(方法解析顺序)
Python 支持多继承,一个子类可以同时继承多个父类。这带来了强大的代码复用能力,但也引入了方法冲突的问题。
class Father:
def skills(self):
print("园艺、编程")
class Mother:
def skills(self):
print("烹饪、音乐")
class Child(Father, Mother):
def own_skill(self):
print("体育")
c = Child()
c.skills() # 输出:园艺、编程(根据MRO,Father优先)
MRO(Method Resolution Order) 是 Python 确定方法调用顺序的机制,基于 C3 线性化算法。其核心规则包括:
- 子类优先于父类
- 父类声明顺序保留
- 单调性约束
可以通过 __mro__ 属性或 mro() 方法查看:
print(Child.__mro__)
# 输出:(<class '__main__.Child'>, <class '__main__.Father'>, <class '__main__.Mother'>, <class 'object'>)
菱形继承问题:当多个父类继承自同一个基类时,MRO 确保每个类的方法只被调用一次。
class A:
def method(self):
print("A")
class B(A):
def method(self):
print("B")
super().method()
class C(A):
def method(self):
print("C")
super().method()
class D(B, C):
def method(self):
print("D")
super().method()
d = D()
d.method()
# 输出:
# D
# B
# C
# A
print(D.__mro__)
# (D, B, C, A, object)
协作式继承:为了确保所有父类方法都被调用,父类方法中应使用 super() 形成调用链。如果无法修改父类代码,可以通过遍历 __bases__ 或 __mro__ 显式调用每个父类的方法。
class Child(Parent1, Parent2):
def onstart(self):
for cls in self.__class__.__mro__:
if 'onstart' in cls.__dict__:
cls.onstart(self)
二、全套魔法函数(双下方法 __xx__)
魔法函数是 Python 中名称以双下划线开头和结尾的特殊方法,用于实现对象的内置行为。掌握它们可以写出更 Pythonic 的代码。
2.1 字符串展示:__str__ 与 __repr__
__str__:被str()和print()调用,返回用户友好的字符串。__repr__:被repr()调用,返回开发者友好的字符串,通常用于调试,应能通过eval()重建对象。
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"Person: {self.name}, {self.age}岁"
def __repr__(self):
return f"Person('{self.name}', {self.age})"
p = Person("小明", 25)
print(p) # 输出:Person: 小明, 25岁
print(repr(p)) # 输出:Person('小明', 25)
2.2 长度与索引:__len__、__getitem__、__setitem__、__delitem__
这些方法让自定义对象支持类似列表或字典的索引操作。
class MyList:
def __init__(self, items):
self.items = list(items)
def __len__(self):
return len(self.items)
def __getitem__(self, index):
return self.items[index]
def __setitem__(self, index, value):
self.items[index] = value
def __delitem__(self, index):
del self.items[index]
ml = MyList([1, 2, 3, 4])
print(len(ml)) # 输出:4
print(ml) # 输出:2
ml = 99
print(ml) # 输出:99
del ml
print(ml) # 输出:99(原索引1变为0)
2.3 运算符重载:__add__、__sub__、__mul__
通过定义这些方法,可以让自定义对象支持 +、-、* 等运算符。
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __sub__(self, other):
return Vector(self.x - other.x, self.y - other.y)
def __mul__(self, scalar):
return Vector(self.x * scalar, self.y * scalar)
def __repr__(self):
return f"Vector({self.x}, {self.y})"
v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1 + v2) # 输出:Vector(4, 6)
print(v2 - v1) # 输出:Vector(2, 2)
print(v1 * 3) # 输出:Vector(3, 6)
2.4 布尔判断:__bool__
定义 __bool__ 方法可以控制对象在布尔上下文(如 if 语句)中的行为。如果未定义,Python 会调用 __len__,长度为0则返回 False。
class Account:
def __init__(self, balance):
self.balance = balance
def __bool__(self):
return self.balance > 0
acc1 = Account(100)
acc2 = Account(0)
print(bool(acc1)) # 输出:True
print(bool(acc2)) # 输出:False
2.5 迭代协议:__iter__ 与 __next__
实现这两个方法可以让自定义对象支持 for 循环迭代。
class CountDown:
def __init__(self, start):
self.current = start
def __iter__(self):
return self
def __next__(self):
if self.current <= 0:
raise StopIteration
value = self.current
self.current -= 1
return value
for num in CountDown(5):
print(num, end=' ') # 输出:5 4 3 2 1
2.6 上下文魔法:__enter__ 与 __exit__
实现这两个方法可以让对象支持 with 语句,自动管理资源(如文件、锁)。
class ManagedFile:
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
def __enter__(self):
self.file = open(self.filename, self.mode)
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
if self.file:
self.file.close()
# 返回 False 表示不抑制异常,True 表示抑制
return False
with ManagedFile('test.txt', 'w') as f:
f.write('Hello, World!')
# 文件自动关闭
2.7 哈希与判等:__eq__ 与 __hash__
__eq__:定义对象相等比较逻辑。__hash__:定义对象的哈希值,用于字典键和集合成员。如果定义了__eq__,通常也需要定义__hash__,且相等的对象哈希值应相同。
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, other):
if not isinstance(other, Point):
return NotImplemented
return self.x == other.x and self.y == other.y
def __hash__(self):
return hash((self.x, self.y))
def __repr__(self):
return f"Point({self.x}, {self.y})"
p1 = Point(1, 2)
p2 = Point(1, 2)
p3 = Point(3, 4)
print(p1 == p2) # 输出:True
print(p1 == p3) # 输出:False
print(hash(p1) == hash(p2)) # 输出:True
# 可以用作字典键
d = {p1: "origin"}
print(d[p2]) # 输出:origin
2.8 对象生命周期:__new__ 与 __del__
__new__:在__init__之前调用,用于创建实例(控制实例创建过程,常用于单例模式)。__del__:对象被销毁时调用(析构函数),用于清理资源。
class Singleton:
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self, value):
self.value = value
def __del__(self):
print(f"Singleton with value {self.value} is being destroyed")
s1 = Singleton(10)
s2 = Singleton(20)
print(s1 is s2) # 输出:True(单例)
print(s1.value) # 输出:20(因为s2覆盖了s1)
三、OOP综合拓展
3.1 抽象基类(ABC):强制子类实现方法
抽象基类(Abstract Base Class)用于定义接口,强制子类必须实现某些方法。Python 通过 abc 模块实现。
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
@abstractmethod
def perimeter(self):
pass
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14 * self.radius ** 2
def perimeter(self):
return 2 * 3.14 * self.radius
# 尝试实例化抽象类会报错
# s = Shape() # TypeError: Can't instantiate abstract class Shape
c = Circle(5)
print(c.area()) # 输出:78.5
print(c.perimeter()) # 输出:31.400000000000002
抽象基类在大型项目中非常有用,可以确保所有子类遵循统一的接口规范。
3.2 类装饰器简易用法
类装饰器是一种修改类行为的函数,接收类作为参数并返回修改后的类(或新类)。常用于添加属性、方法或修改元数据。
def add_repr(cls):
"""为类自动添加 __repr__ 方法"""
def __repr__(self):
attrs = ', '.join(f"{k}={v}" for k, v in self.__dict__.items())
return f"{cls.__name__}({attrs})"
cls.__repr__ = __repr__
return cls
@add_repr
class Book:
def __init__(self, title, author):
self.title = title
self.author = author
b = Book("Python编程", "张三")
print(b) # 输出:Book(title=Python编程, author=张三)
类装饰器也可以用于注册类、添加缓存、实现单例等高级功能。
3.3 三大特性总结:封装、继承、多态实战
面向对象编程的三大特性——封装、继承、多态——在实际开发中相辅相成。下面通过一个综合示例展示它们的应用:
from abc import ABC, abstractmethod
# 1. 封装:将数据和操作封装在类中
class BankAccount(ABC):
def __init__(self, owner, balance=0):
self._owner = owner # 受保护属性
self.__balance = balance # 私有属性(名称修饰)
def deposit(self, amount):
if amount > 0:
self.__balance += amount
print(f"存款 {amount},余额:{self.__balance}")
def withdraw(self, amount):
if 0 < amount <= self.__balance:
self.__balance -= amount
print(f"取款 {amount},余额:{self.__balance}")
else:
print("余额不足")
def get_balance(self):
return self.__balance
@abstractmethod
def describe(self):
pass
# 2. 继承:子类复用父类代码并扩展
class SavingsAccount(BankAccount):
def __init__(self, owner, balance=0, interest_rate=0.02):
super().__init__(owner, balance)
self.interest_rate = interest_rate
def apply_interest(self):
interest = self.get_balance() * self.interest_rate
self.deposit(interest)
print(f"利息 {interest:.2f} 已添加")
def describe(self):
return f"储蓄账户 - 户主:{self._owner},利率:{self.interest_rate*100}%"
class CheckingAccount(BankAccount):
def __init__(self, owner, balance=0, overdraft_limit=1000):
super().__init__(owner, balance)
self.overdraft_limit = overdraft_limit
def withdraw(self, amount):
if amount <= self.get_balance() + self.overdraft_limit:
# 重写取款方法,允许透支
if amount > self.get_balance():
print("使用透支额度")
super().withdraw(amount)
else:
print("超过透支限额")
def describe(self):
return f"支票账户 - 户主:{self._owner},透支限额:{self.overdraft_limit}"
# 3. 多态:不同对象对同一消息做出不同响应
def show_account_info(account):
print(account.describe())
print(f"当前余额:{account.get_balance()}")
# 使用
savings = SavingsAccount("Alice", 1000, 0.03)
checking = CheckingAccount("Bob", 500, 2000)
savings.deposit(500)
savings.apply_interest()
show_account_info(savings)
# 输出:
# 存款 500,余额:1500
# 利息 45.00 已添加
# 存款 45,余额:1545
# 储蓄账户 - 户主:Alice,利率:3.0%
# 当前余额:1545
checking.withdraw(2000) # 使用透支额度
show_account_info(checking)
# 输出:
# 使用透支额度
# 取款 2000,余额:-1500
# 支票账户 - 户主:Bob,透支限额:2000
# 当前余额:-1500
总结
Python 的面向对象编程提供了丰富的机制来构建可维护、可扩展的代码:
- 继承 通过单继承和多继承实现代码复用,
super()和 MRO 确保方法调用的正确性。 - 多态 让代码更加灵活,无需关心对象的具体类型。
- 魔法函数 赋予自定义对象与内置类型一致的行为,从字符串展示到运算符重载,从迭代到上下文管理。
- 抽象基类 强制子类实现接口,保证代码的规范性。
- 类装饰器 提供了一种优雅的方式修改类行为。
在实际开发中,应遵循“组合优于继承”的原则,谨慎使用多继承,优先使用混入(Mixin)模式。理解这些核心概念,将帮助你写出更 Pythonic、更健壮的代码。
更多推荐
所有评论(0)