Python Day15 面向对象核心特性笔记 及 例题分析
一、运算符重载
运算符重载允许自定义类的对象支持算术运算、比较运算等,通过重写对应的魔术方法实现。
1. 算术运算符重载
运算符 | 对应魔术方法 | 说明 |
---|---|---|
+ | __add__ | 加法 |
- | __sub__ | 减法 |
* | __mul__ | 乘法 |
/ | __truediv__ | 真除法(返回浮点数) |
// | __floordiv__ | 整除(返回整数) |
% | __mod__ | 取模(取余数) |
** | __pow__ | 幂运算 |
示例(Cat 类算术运算):
class Cat:def __init__(self, name, age):self.name = nameself.age = agedef __add__(self, other): # 加法return self.age + other.agedef __sub__(self, other): # 减法return self.age - other.age# 其他算术方法省略...
2. 关系运算符重载
运算符 | 对应魔术方法 | 说明 |
---|---|---|
> | __gt__ | 大于 |
>= | __ge__ | 大于等于 |
< | __lt__ | 小于 |
<= | __le__ | 小于等于 |
== | __eq__ | 等于 |
!= | __ne__ | 不等于 |
示例(Cat 类比较运算):
def __gt__(self, other):if not isinstance(other, self.__class__):raise TypeError(f'{other} 必须是Cat类型')return self.age > other.agedef __ge__(self, other):# 实现大于等于逻辑return self.age >= other.age
二、可迭代对象与迭代器
可迭代对象和迭代器用于表示多值集合,支持for...in
循环遍历。
1. 可迭代对象
需满足两个条件:
- 能表示多值
- 支持
len()
函数获取长度
需实现的魔术方法:
__len__
:返回元素数量__iter__
:返回迭代器对象(可通过生成器或iter()
转换实现)
示例(Array 类):
class Array:def __init__(self):self.lst = []def add(self, val):self.lst.append(val)def __len__(self):return len(self.lst)def __iter__(self):# 生成器方式返回迭代器for x in self.lst:yield x
2. 迭代器
迭代器是可迭代对象的具体实现,支持逐个获取元素。
需实现的魔术方法:
__iter__
:返回自身(迭代器本身也是可迭代对象)__next__
:返回下一个元素,无元素时抛出StopIteration
示例(C 类迭代器):
class C:def __init__(self):self.lst = []def add(self, val):self.lst.append(val)def __iter__(self):return self # 返回自身作为迭代器def __next__(self):if len(self.lst) == 0:raise StopIterationreturn self.lst.pop(0) # 弹出第一个元素
三、对象创建与初始化
对象的创建和初始化通过__new__
和__init__
两个魔术方法控制。
1. __new__
与__init__
的区别
__new__
:负责创建对象(静态方法),返回创建的实例__init__
:负责初始化对象属性,在__new__
之后自动调用
2. 单例模式
单例模式确保一个类只能创建一个对象,通过__new__
实现:
class Sun:__instance = None # 存储唯一实例def __new__(cls, *args, **kwargs):if cls.__instance is None:# 首次创建对象cls.__instance = super().__new__(cls)return cls.__instance # 返回唯一实例
四、可调用对象与装饰器
通过__call__
方法可使对象像函数一样被调用,结合类可实现装饰器。
1. 可调用对象
实现__call__
方法后,对象可直接被调用:
class D:def __call__(self, *args):print(f'被调用了,参数:{args}')d = D()
d(123) # 输出:被调用了,参数:(123,)
2. 类装饰器
类装饰器通过__call__
实现,可用于统计函数执行时间、参数校验等。
示例 1:统计执行时间(Timer 类)
class Timer:def __init__(self, func):self.__func = funcdef __call__(self, *args, **kwargs):start = time.time()self.__func(*args, **kwargs)end = time.time()print(f'执行时长:{end - start}')
示例 2:参数类型校验(LimitParameterType 类)
class LimitParameterType:def __init__(self, *args, **kwargs):self.args = args # 位置参数类型self.kwargs = kwargs # 关键字参数类型def __call__(self, func):def wrapper(*args, **kwargs):# 校验位置参数类型for i, arg_type in enumerate(self.args):if not isinstance(args[i], arg_type):raise TypeError(f'参数{i}类型错误')# 校验关键字参数类型(省略)return func(*args, **kwargs)return wrapper
装饰器使用示例:
@LimitParameterType(str, str) # 校验参数为字符串
@Timer # 统计执行时间
def sum_num(a, b):return a + b
五、面向对象三大特性
1. 封装
将数据和操作数据的方法封装在类中,通过访问控制隐藏内部实现。
2. 继承
子类继承父类的属性和方法,可扩展或重写父类功能。
继承语法
class 子类(父类):# 子类实现
方法重写与调用父类方法
- 子类可重写父类方法(如
eat
、sleep
) - 通过
super()
调用父类方法:
class VipDog(Dog): # 继承Dog类def __init__(self, name, age, gender):super().__init__(age, gender) # 调用父类初始化self.name = name # 子类特有属性def eat(self):super().eat() # 调用父类eat方法print(f'{self.name}吃上大骨头啦!')
3. 多态
不同子类对同一方法的不同实现,调用时自动适配具体类型(Python 中通过动态类型实现)。
六、关键问题解答
问题:能否让变量a
满足a + 1 == 2 and a + 1 == 3 and a + 1 == 5
?
解答:可以通过重载__add__
和__eq__
实现:
class B:def __add__(self, other):return self # 加法返回自身def __eq__(self, other):return True # 相等判断永远返回Truea = B()
print(a + 1 == 2 and a + 1 == 3 and a + 1 == 5) # 输出:True
原理:a + 1
返回a
本身,a == 任意值
均为True
。
一、魔术方法应用
1. 单例模式实现
需求:定义一个Sington
类,确保该类只能创建唯一对象。
class Sington:"""单例模式类:一个类只能创建一个对象"""__instance = None # 私有类属性,存储唯一实例def __new__(cls, *args, **kwargs):# 判断是否已创建过对象if cls.__instance is None:# 首次创建对象时,调用父类new方法cls.__instance = super().__new__(cls)return cls.__instance # 返回唯一实例def __init__(self, val):self.val = val # 初始化属性# 测试
if __name__ == '__main__':a = Sington(1)a1 = Sington(2)print(a1, a2, a3, a, sep='\n') # 所有对象地址相同(单例特性)
2. Person 类(对象比较与去重)
需求:
- 打印对象时显示姓名和年龄
- 支持按姓名和年龄比较相等性
- 存储到 Set 中时按 “姓名 + 年龄” 去重
class Person:def __init__(self, name, age):self.name = nameself.age = age# 打印对象格式:姓名、年龄def __repr__(self):return f'{self.name}、{self.age}'# 比较相等性(姓名和年龄均相同则相等)def __eq__(self, other):if other is None:return Falseif self is other:return Trueif not isinstance(other, Person):return Falsereturn self.name == other.name and self.age == other.age# 支持Set去重(基于姓名和年龄生成哈希值)def __hash__(self):return hash((self.name, self.age))# 测试
if __name__ == '__main__':a = Person('王五', 18)b = Person('赵磊', 18)c = Person('王五', 18)print(a) # 王五、18print(a == c) # True(姓名和年龄相同)print(set([a, b, c])) # {王五、18, 赵磊、18}(去重)
3. Num 类(算术运算重载)
需求:
- 私有化
value
属性 - 支持两个 Num 对象的加减乘除、整除、取余运算
- 打印格式为
Num(值)
class Num:def __init__(self, value):self.value = value # 通过属性装饰器私有化# 算术运算重载(返回新的Num对象)def __add__(self, other):return Num(self.value + other.value)def __sub__(self, other):return Num(self.value - other.value)def __mul__(self, other):return Num(self.value * other.value)def __truediv__(self, other):return Num(self.value / other.value)def __floordiv__(self, other):return Num(self.value // other.value)def __mod__(self, other):return Num(self.value % other.value)# 打印格式def __repr__(self):return f'Num({self.value})'# 私有化value属性@propertydef value(self):return self.__value@value.setterdef value(self, value):self.__value = value# 测试
if __name__ == '__main__':num1 = Num(2)num2 = Num(3)print(num1 + num2) # Num(5)print(num1 * num2) # Num(6)
4. 特殊表达式满足(a + 1 == 2 and a + 1 == 3)
需求:创建类 A,使其实例a
满足a + 1 == 2 and a + 1 == 3
class A:def __add__(self, other):return self # 加法运算返回自身def __eq__(self, other):return True # 相等判断永远返回True# 测试
if __name__ == '__main__':a = A()print(a + 1 == 2 and a + 1 == 3) # True
原理:a + 1
返回a
本身,a == 2
和a == 3
均返回True
。
5. 可迭代与可相加类 B
需求:
- 实例支持
for
循环遍历传入的参数 - 两个实例相加时合并数据并返回新结果
class B:def __init__(self, *args):self.args = args # 存储传入的多个参数# 支持for循环(返回迭代器)def __iter__(self):return iter(self.args)# 支持加法(合并两个实例的参数)def __add__(self, other):return B(*(self.args + other.args)) # 返回新B实例# 打印格式def __repr__(self):return f'B{self.args}'# 测试
if __name__ == '__main__':a = B(1, 2, 3)b = B(4, 5)for x in a:print(x) # 1、2、3print(a + b) # B(1, 2, 3, 4, 5)
6. 基于类的装饰器 GeneratorKey
需求:
- 为类实例自动生成或校验
id
属性 - 未传入
id
时自动分配(从seq
开始,每次 + 1) - 传入
id
时需大于当前seq
,否则抛异常
class GeneratorKey:def __init__(self, *, seq=1):self.seq = seq # 初始序列值def __call__(self, cls):def wrapper(*args, **kwargs):if 'id' not in kwargs:# 未传入id:自动分配并递增seqkwargs['id'] = self.seqself.seq += 1else:# 传入id:检查是否大于当前seqif kwargs['id'] <= self.seq:raise RuntimeError('id必须大于当前seq')self.seq = kwargs['id'] # 更新seq为传入的idreturn cls(*args, **kwargs) # 创建并返回实例return wrapper# 使用装饰器
@GeneratorKey(seq=1)
class User:def __init__(self, name, id):self.name = nameself.id = iddef __repr__(self):return f'User(name={self.name}, id={self.id})'# 测试
if __name__ == '__main__':u1 = User('张三') # 自动分配id=1,seq变为2u2 = User('李四') # 自动分配id=2,seq变为3u3 = User('王五', id=5) # 传入id=5(>3),seq更新为5u4 = User('赵六') # 自动分配id=6,seq变为7print(u1, u2, u3, u4)# u5 = User('陆奇', id=4) # 抛异常(4 <= 5)
7. 对象创建次数统计类 D
需求:通过__new__
记录类的实例创建次数
class D:instance_count = 0 # 类属性:记录创建次数def __new__(cls):cls.instance_count += 1 # 每次创建对象时+1return super().__new__(cls)# 测试
if __name__ == '__main__':a = D()b = D()c = D()print(D.instance_count) # 3
二、继承与图形计算
1. 基础图形类设计
按要求实现图形类及其子类,支持周长、面积、体积计算。
import math# 1. 基础图形类
class Shape:"""图形基类:定义周长和面积方法"""def perimeter(self):passdef area(self):pass# 2. 长方形
class Rectangle(Shape):"""长方形:继承Shape,实现周长和面积"""def __init__(self, length, width):self.__length = length # 私有属性self.__width = width@propertydef length(self):return self.__length@propertydef width(self):return self.__width# 周长 = 2*(长+宽)def perimeter(self):return 2 * (self.length + self.width)# 面积 = 长*宽def area(self):return self.length * self.width# 3. 正方形(继承长方形)
class Square(Rectangle):"""正方形:继承Rectangle,边长作为参数"""def __init__(self, side):super().__init__(side, side) # 调用父类构造(长=宽=边长)# 4. 圆形
class Circle(Shape):"""圆形:继承Shape,实现周长和面积"""def __init__(self, radius):self.__radius = radius@propertydef radius(self):return self.__radius# 周长 = 2πrdef perimeter(self):return 2 * math.pi * self.radius# 面积 = πr²def area(self):return math.pi * (self.radius **2)# 5. 立体图形基类
class Stereograph(Shape):"""立体图形:继承Shape,定义体积方法"""def volume(self):raise RuntimeError("请使用具体立体图形完成体积的计算")# 6. 圆柱体
class Cylinder(Stereograph, Circle):"""圆柱体:继承立体图形和圆形,实现表面积和体积"""def __init__(self, radius, height):Circle.__init__(self, radius) # 初始化半径self.__height = height@propertydef height(self):return self.__height# 圆柱体周长:不支持(抛异常)def perimeter(self):raise TypeError("图形不支持该计算")# 表面积 = 2个底面积 + 侧面积(周长×高)def area(self):return 2 * Circle.area(self) + self.perimeter() * self.height# 体积 = 底面积×高def volume(self):return Circle.area(self) * self.height# 7. 圆锥体(继承圆柱体)
class Cone(Cylinder):"""圆锥体:继承Cylinder,重写表面积和体积"""def volume(self):# 体积 = 1/3 × 底面积 × 高return super().volume() / 3def area(self):# 表面积 = 底面积 + 侧面积(π×半径×母线)母线 = math.sqrt(self.radius** 2 + self.height **2)return math.pi * self.radius** 2 + math.pi * self.radius * 母线def perimeter(self):raise TypeError("图形不支持该计算")
2. 图形测试代码
if __name__ == '__main__':# 长方形rect = Rectangle(5, 3)print(f"长方形周长:{rect.perimeter()},面积:{rect.area()}")# 正方形square = Square(4)print(f"正方形周长:{square.perimeter()},面积:{square.area()}")# 圆形circle = Circle(2)print(f"圆形周长:{circle.perimeter()},面积:{circle.area()}")# 圆柱体cylinder = Cylinder(2, 5)print(f"圆柱体表面积:{cylinder.area()},体积:{cylinder.volume()}")# 圆锥体cone = Cone(2, 5)print(f"圆锥体表面积:{cone.area()},体积:{cone.volume()}")
三、总结
本次作业重点练习:
- 魔术方法:运算符重载(
__add__
、__eq__
等)、对象创建(__new__
)、迭代(__iter__
)等 - 继承与多态:通过图形类层级实现方法重写与扩展
- 装饰器:基于类的装饰器实现属性自动生成与校验
- 封装:通过私有属性和属性装饰器隐藏内部实现