大家好,面向对象编程是 Python 的核心范式之一。在掌握了类的基本定义和对象创建之后,我们需要进一步了解 Python 类中的魔法函数(特殊方法)、类的组合以及类的继承。这三个概念是构建复杂、可复用、可扩展代码体系的关键。
这篇文章将系统性地讲解这三个知识点,从基本概念到实际应用,配合完整的代码示例,帮助你全面理解 Python 面向对象编程的进阶内容。
一、魔法函数(特殊方法)
1.1 什么是魔法函数
魔法函数(Magic Methods)是 Python 中以双下划线开头和结尾的特殊方法,如 __init__、__str__、__len__ 等。它们不需要手动调用,而是在特定操作(如对象创建、字符串转换、运算符运算)时被 Python 自动调用。
这些方法定义了对象在特定场景下的行为,是 Python 面向对象编程中非常核心的机制。
| __init__ | 对象初始化时 |
| __str__ | str(obj) 或 print(obj) 时 |
| __repr__ | repr(obj) 或在交互式环境中直接输入对象时 |
| __len__ | len(obj) 时 |
| __getitem__ | obj[key] 时 |
| __setitem__ | obj[key] = value 时 |
| __call__ | obj() 时 |
| __add__ | obj1 + obj2 时 |
| __eq__ | obj1 == obj2 时 |
1.2 基础魔法方法
class Person:
def __init__(self, name: str, age: int):
"""构造方法:创建对象时调用"""
self.name = name
self.age = age
print(f"创建了 Person 对象: {name}")
def __str__(self) -> str:
"""字符串表示:print() 和 str() 时调用"""
return f"Person(name={self.name}, age={self.age})"
def __repr__(self) -> str:
"""开发人员友好的表示:交互式环境或 repr() 时调用"""
return f"Person('{self.name}', {self.age})"
def __len__(self) -> int:
"""长度:len() 时调用"""
return self.age # 用年龄作为长度(示例)
def __call__(self, *args, **kwargs):
"""使对象可以像函数一样被调用"""
print(f"{self.name} 被调用了,参数: {args}")
# 测试
p = Person("张三", 25) # 创建了 Person 对象: 张三
print(p) # Person(name=张三, age=25) 触发 __str__
print(repr(p)) # Person('张三', 25) 触发 __repr__
print(len(p)) # 25 触发 __len__
p(1, 2, 3) # 张三 被调用了,参数: (1, 2, 3) 触发 __call__
1.3 运算符重载
通过魔法函数,我们可以自定义对象的算术运算行为。
class Vector:
"""二维向量"""
def __init__(self, x: float, y: float):
self.x = x
self.y = y
def __add__(self, other: 'Vector') -> 'Vector':
"""加法:v1 + v2"""
return Vector(self.x + other.x, self.y + other.y)
def __sub__(self, other: 'Vector') -> 'Vector':
"""减法:v1 – v2"""
return Vector(self.x – other.x, self.y – other.y)
def __mul__(self, scalar: float) -> 'Vector':
"""乘法:v * n"""
return Vector(self.x * scalar, self.y * scalar)
def __rmul__(self, scalar: float) -> 'Vector':
"""右乘法:n * v(当左侧不支持乘法时调用)"""
return self.__mul__(scalar)
def __eq__(self, other: 'Vector') -> bool:
"""相等比较:v1 == v2"""
if not isinstance(other, Vector):
return False
return self.x == other.x and self.y == other.y
def __str__(self) -> str:
return f"Vector({self.x}, {self.y})"
# 测试
v1 = Vector(3, 4)
v2 = Vector(1, 2)
print(v1 + v2) # Vector(4, 6)
print(v1 – v2) # Vector(2, 2)
print(v1 * 2) # Vector(6, 8)
print(3 * v1) # Vector(9, 12) 触发 __rmul__
print(v1 == Vector(3, 4)) # True
print(v1 == v2) # False
1.4 序列/容器模拟
通过实现 __getitem__、__setitem__、__len__ 等魔法方法,可以创建类似列表或字典的容器类型。
class StudentList:
"""自定义容器:管理学生列表"""
def __init__(self):
self._students = []
def add(self, name: str):
self._students.append(name)
def __getitem__(self, index: int) -> str:
"""索引访问:students[0]"""
return self._students[index]
def __setitem__(self, index: int, value: str):
"""索引赋值:students[0] = '新名字'"""
self._students[index] = value
def __len__(self) -> int:
"""长度:len(students)"""
return len(self._students)
def __iter__(self):
"""迭代:for student in students"""
return iter(self._students)
def __contains__(self, item: str) -> bool:
"""成员判断:'张三' in students"""
return item in self._students
def __str__(self) -> str:
return f"StudentList({self._students})"
# 测试
students = StudentList()
students.add("张三")
students.add("李四")
students.add("王五")
print(students[0]) # 张三 触发 __getitem__
students[1] = "李四改" # 触发 __setitem__
print(len(students)) # 3 触发 __len__
for name in students: # 触发 __iter__
print(name)
print("张三" in students) # True 触发 __contains__
print("赵六" in students) # False
1.5 比较运算符
class Score:
def __init__(self, value: int):
self.value = value
def __lt__(self, other: 'Score') -> bool:
"""小于:s1 < s2"""
return self.value < other.value
def __le__(self, other: 'Score') -> bool:
"""小于等于:s1 <= s2"""
return self.value <= other.value
def __eq__(self, other: 'Score') -> bool:
"""等于:s1 == s2"""
return self.value == other.value
def __str__(self) -> str:
return f"Score({self.value})"
scores = [Score(85), Score(92), Score(78), Score(90)]
sorted_scores = sorted(scores)
for s in sorted_scores:
print(s) # Score(78), Score(85), Score(90), Score(92)
1.6 常用的魔法函数速查表
| __init__(self, …) | 构造方法 | obj = Class(…) |
| __del__(self) | 析构方法(对象被回收时) | del obj |
| __str__(self) | 字符串表示(用户友好) | print(obj) |
| __repr__(self) | 字符串表示(开发调试) | repr(obj) |
| __len__(self) | 长度 | len(obj) |
| __getitem__(self, key) | 索引访问 | obj[key] |
| __setitem__(self, key, value) | 索引赋值 | obj[key] = value |
| __delitem__(self, key) | 删除索引 | del obj[key] |
| __iter__(self) | 迭代器 | for x in obj |
| __contains__(self, item) | 成员判断 | item in obj |
| __call__(self, …) | 对象作为函数调用 | obj() |
| __add__(self, other) | 加法 | obj1 + obj2 |
| __sub__(self, other) | 减法 | obj1 – obj2 |
| __mul__(self, other) | 乘法 | obj1 * obj2 |
| __eq__(self, other) | 等于 | obj1 == obj2 |
| __lt__(self, other) | 小于 | obj1 < obj2 |
二、类的组合
2.1 什么是组合
组合是指一个类中包含另一个类的实例作为其属性。它是一种"有一个"(has-a)的关系,用于构建更复杂的对象。
组合的优势:
-
代码复用:直接复用已有类的功能
-
灵活性强:可以在运行时动态替换组件
-
解耦:各个组件之间相对独立
2.2 组合的基本用法
class Engine:
"""引擎类"""
def __init__(self, horsepower: int):
self.horsepower = horsepower
self.is_running = False
def start(self):
self.is_running = True
print(f"引擎启动,马力: {self.horsepower}")
def stop(self):
self.is_running = False
print("引擎关闭")
class Wheels:
"""轮子类"""
def __init__(self, count: int = 4):
self.count = count
def info(self) -> str:
return f"{self.count}个轮子"
class Car:
"""汽车类:通过组合使用 Engine 和 Wheels"""
def __init__(self, brand: str, horsepower: int):
self.brand = brand
self.engine = Engine(horsepower) # 组合:汽车有一个引擎
self.wheels = Wheels() # 组合:汽车有轮子
def start(self):
print(f"{self.brand} 汽车启动")
self.engine.start()
def stop(self):
print(f"{self.brand} 汽车停止")
self.engine.stop()
def info(self):
return f"{self.brand},{self.wheels.info()},引擎马力: {self.engine.horsepower}"
# 测试
car = Car("宝马", 200)
print(car.info()) # 宝马,4个轮子,引擎马力: 200
car.start() # 宝马 汽车启动 / 引擎启动,马力: 200
car.stop() # 宝马 汽车停止 / 引擎关闭
2.3 组合与继承的区别
| 关系类型 | "有一个"(has-a) | "是一个"(is-a) |
| 代码复用方式 | 通过包含其他类 | 通过继承父类 |
| 耦合度 | 较低 | 较高 |
| 灵活性 | 高(可动态替换组件) | 低(继承关系固定) |
| 适用场景 | 复杂对象的组装 | 明确的层次关系 |
三、类的继承
3.1 什么是继承
继承是指一个类(子类)获取另一个类(父类)的属性和方法。它是一种"是一个"(is-a)的关系,用于构建类的层次结构。
继承的核心优势:
-
代码复用:子类自动拥有父类的所有属性和方法
-
扩展性:子类可以在父类基础上添加或重写功能
-
多态性:不同子类可以以不同方式实现相同的方法
3.2 基本继承语法
class Animal:
"""父类:动物"""
def __init__(self, name: str, age: int):
self.name = name
self.age = age
def eat(self):
print(f"{self.name} 正在吃东西")
def sleep(self):
print(f"{self.name} 正在睡觉")
def info(self) -> str:
return f"{self.name},{self.age}岁"
class Dog(Animal):
"""子类:狗 继承自 Animal"""
def __init__(self, name: str, age: int, breed: str):
super().__init__(name, age) # 调用父类构造方法
self.breed = breed # 新增属性
def bark(self):
print(f"{self.name} 汪汪叫!")
def info(self) -> str:
"""重写父类方法"""
return f"{self.name},{self.age}岁,品种: {self.breed}"
class Cat(Animal):
"""子类:猫 继承自 Animal"""
def __init__(self, name: str, age: int, color: str):
super().__init__(name, age)
self.color = color
def meow(self):
print(f"{self.name} 喵喵叫~")
def info(self) -> str:
return f"{self.name},{self.age}岁,毛色: {self.color}"
# 测试
dog = Dog("旺财", 3, "金毛")
cat = Cat("小花", 2, "橘色")
# 继承父类方法
dog.eat() # 旺财 正在吃东西
cat.sleep() # 小花 正在睡觉
# 子类特有方法
dog.bark() # 旺财 汪汪叫!
cat.meow() # 小花 喵喵叫~
# 重写方法
print(dog.info()) # 旺财,3岁,品种: 金毛
print(cat.info()) # 小花,2岁,毛色: 橘色
四、总结
| __init__ | 构造方法,创建对象时初始化实例属性 |
| __str__ / __repr__ | 字符串表示,控制 print() 和 repr() 的输出 |
| __len__ / __getitem__ | 模拟序列/容器行为 |
| __add__ / __eq__ 等 | 运算符重载,自定义算术和比较运算 |
| __call__ | 使对象可像函数一样被调用 |
| 组合 | "有一个"关系,通过包含其他类的实例来复用功能 |
| 继承 | "是一个"关系,子类继承父类的属性和方法 |
| super() | 调用父类的方法,常用于构造方法和方法重写 |
| 方法重写 | 子类重新定义父类的方法,实现自己的逻辑 |
魔法函数让对象的行为更自然、更符合 Python 的语法习惯;组合提供了灵活的对象组装方式;继承则建立了清晰的类层次结构。三者结合使用,可以构建出结构清晰、功能强大、易于维护的面向对象系统。
如果觉得这篇内容对你有帮助,欢迎收藏备用。
网硕互联帮助中心
评论前必须登录!
注册