Python 面向对象编程(上)专项练习:6 道编程题从入门到精通
配套专栏:Python 全栈修炼之路 第 10 篇《面向对象编程(上)—— 类与对象》难度分布:⭐ → ⭐⭐ → ⭐⭐ → ⭐⭐⭐ → ⭐⭐⭐ → ⭐⭐⭐⭐核心覆盖:类定义、init、self、类属性 vs 实例属性、访问控制、命名修饰、slots、描述符入门、type/object/class 三角关系题目一:设计一个 Point 类 ⭐📌 题目描述设计一个Point类表示二维平面上的点,支持以下操作:# 创建点p1=Point(3,4)p2=Point(0,0)# 访问坐标print(p1.x,p1.y)# 3 4# 计算到原点的距离print(p1.distance_to_origin())# 5.0# 计算两点间距离print(p1.distance_to(p2))# 5.0# 移动点p1.move(1,1)print(p1)# Point(4, 5)# 支持 == 比较p3=Point(3,4)print(p1==p3)# False(p1 已移动)💡 编程思路这道题考察类定义基础 + 实例属性 + 特殊方法:类定义:使用class Point:定义类,__init__方法初始化x和y坐标。实例方法:distance_to_origin和distance_to计算距离,使用self访问实例属性。修改方法:move方法修改点的位置,展示可变对象的行为。特殊方法:实现__str__和__repr__提供友好的字符串表示,实现__eq__支持==比较。🖥️ 参考代码importmathclassPoint:"""二维平面上的点。"""def__init__(self,x:float=0,y:float=0):"""初始化点的坐标。 Args: x: x 坐标,默认 0 y: y 坐标,默认 0 """self.x=x self.y=ydefdistance_to_origin(self)-float:"""计算到原点 (0, 0) 的距离。"""returnmath.sqrt(self.x**2+self.y**2)defdistance_to(self,other:'Point')-float:"""计算到另一个点的距离。 Args: other: 另一个 Point 对象 Returns: 两点间的欧几里得距离 """dx=self.x-other.x dy=self.y-other.yreturnmath.sqrt(dx**2+dy**2)defmove(self,dx:float,dy:float)-'Point':"""移动点的位置。 Args: dx: x 方向的位移 dy: y 方向的位移 Returns: self,支持链式调用 """self.x+=dx self.y+=dyreturnselfdefclone(self)-'Point':"""创建点的副本。"""returnPoint(self.x,self.y)def__str__(self)-str:"""用户友好的字符串表示。"""returnf"Point({self.x},{self.y})"def__repr__(self)-str:"""开发者友好的字符串表示。"""returnf"Point({self.x!r},{self.y!r})"def__eq__(self,other)-bool:"""支持 == 比较。"""ifnotisinstance(other,Point):returnNotImplementedreturnself.x==other.xandself.y==other.ydef__add__(self,other:'Point')-'Point':"""支持 + 运算:向量相加。"""returnPoint(self.x+other.x,self.y+other.y)def__sub__(self,other:'Point')-'Point':"""支持 - 运算:向量相减。"""returnPoint(self.x-other.x,self.y-other.y)# 测试if__name__=="__main__":p1=Point(3,4)p2=Point(0,0)print(f"p1:{p1}")print(f"到原点距离:{p1.distance_to_origin()}")print(f"p1 到 p2 距离:{p1.distance_to(p2)}")# 移动p1.move(1,1)print(f"移动后:{p1}")# 比较p3=Point(4,5)print(f"p1 == p3:{p1==p3}")# 运算p4=Point(1,2)+Point(3,4)print(f"Point(1,2) + Point(3,4) ={p4}")# 链式调用p5=Point(0,0).move(1,2).move(3,4)print(f"链式移动:{p5}")print("\n所有测试通过 ✓")🔗 关联知识点知识点说明__init__构造方法,初始化实例属性self代表实例本身,访问实例属性__str__/__repr__字符串表示__eq__相等比较__add__/__sub__运算符重载链式调用方法返回self题目二:类属性与实例属性辨析 ⭐⭐📌 题目描述设计一个Counter类,统计创建了多少个实例,同时每个实例有自己的计数:# 类属性:统计所有实例的数量c1=Counter()c2=Counter()print(Counter.total_count)# 2# 实例属性:每个实例独立的计数c1.increment()c1.increment()c2.increment()print(c1.count)# 2print(c2.count)# 1# 类方法获取统计信息print(Counter.get_statistics())# {'total_instances': 2, 'total_increments': 3}💡 编程思路这道题考察类属性 vs 实例属性 + 类方法 + @classmethod:类属性:total_count和total_increments是类属性,所有实例共享,用于全局统计。实例属性:count是每个实例独立的计数。类方法:get_statistics使用@classmethod装饰器,可以访问类属性而不需要实例。初始化计数:在__init__中增加类属性total_count,记录实例创建次数。🖥️ 参考代码classCounter:"""计数器类,演示类属性和实例属性的区别。"""# 类属性:所有实例共享total_instances=0total_increments=0def__init__(self,name:str=""):"""初始化计数器。 Args: name: 计数器名称(可选) """# 实例属性:每个实例独立self.name=nameorf"Counter-{Counter.total_instances+1}"self.count=0# 增加类属性Counter.total_instances+=1defincrement(self)-int:"""增加计数,返回当前值。 Returns: 增加后的计数值 """self.count+=1Counter.total_increments+=1returnself.countdefreset(self)-None:"""重置实例计数。"""# 只重置实例属性,不影响类属性self.count=0@classmethoddefget_statistics(cls)-dict:"""获取全局统计信息。 Returns: 包含 total_instances 和 total_increments 的字典 """return{'total_instances':cls.total_instances,'total_increments':cls.total_increments}@classmethoddefreset_global(cls)-None:"""重置全局统计(谨慎使用)。"""cls.total_instances=0cls.total_increments=0def__str__(self)-str:returnf"{self.name}:{self.count}"def__repr__(self)-str:returnf"Counter(name={self.name!r}, count={self.count})"# 测试if__name__=="__main__":print("=== 创建实例 ===")c1=Counter("A")c2=Counter("B")c3=Counter()# 使用默认名称print(f"总实例数:{Counter.total_instances}")print("\n=== 增加计数 ===")c1.increment()c1.increment()c2.increment()c3.increment()c3.increment()c3.increment()print(f"c1:{c1}")print(f"c2:{c2}")print(f"c3:{c3}")print(f"\n全局统计:{Counter.get_statistics()}")print("\n=== 重置实例计数 ===")c1.reset()print(f"c1 重置后:{c1}")print(f"c2 不受影响:{c2}")print(f"全局统计不变:{Counter.get_statistics()}")print("\n=== 类属性 vs 实例属性陷阱 ===")# 陷阱:通过实例修改类属性c1.total_instances=100# 这创建了一个实例属性!print(f"Counter.total_instances:{Counter.total_instances}")# 还是 3print(f"c1.total_instances:{c1.total_instances}")# 100(实例属性)print(f"c2.total_instances:{c2.total_instances}")# 3(类属性)print("\n所有测试通过 ✓")🔗 关联知识点知识点说明类属性所有实例共享,通过ClassName.attr访问实例属性每个实例独立,通过self.attr访问@classmethod类方法,第一个参数是cls属性查找顺序实例 → 类 → 父类陷阱通过实例赋值会创建实例属性,遮蔽类属性题目三:实现一个安全的属性访问类 ⭐⭐📌 题目描述实现一个SafeBox类,支持密码保护的私有属性访问:box=SafeBox("secret_data",password="1234")# 正确密码可以访问print(box.get_data("1234"))# "secret_data"# 错误密码抛出异常try:box.get_data("wrong"