一、前言
还在看不懂类、实例、self 是啥?一写面向对象就头大? 别慌!本篇抛弃枯燥教科书话术,用人、坐标点、画图小游戏带你吃透 OOP 基础。从最简单的人物类,到自动绘图,再到能算面积周长的圆形线段,代码复制就能跑,零基础也能看懂!
二、开门见山:类是模板,实例是活生生的人
打个比方: 类 = 一张空白身份证模板,规定每个人都要有姓名、年龄,还能改名字、改年龄; 实例 = 填好信息的身份证,马云、马斯克各一张,互相不干扰,改马云的年龄不会影响马斯克。 __init__ 初始化函数就是填表动作,刚打印身份证那一刻自动执行; self 就是 “本人”,代表当前这张身份证自己。
人物封装趣味代码(带年龄防瞎改校验)
print("===== 身份证模板初始化演示 =====")
class Person:
# 办身份证时自动填表
def __init__(self, name, age):
print(f"新身份证制作完成,证件内存编号:{id(self)}")
self.name = name
self.age = age
# 查看姓名
def get_name(self):
return self.name
# 修改姓名,禁止空名字
def set_name(self, name):
if isinstance(name, str) and len(name.strip()) > 0:
self.name = name
else:
print("名字不能是空字符串,改不了!")
# 查看年龄
def get_age(self):
return self.age
# 修改年龄,杜绝离谱岁数
def set_age(self, age):
if isinstance(age, int) and 0 < age < 150:
self.age = age
else:
print("年龄必须是0~150的正常人岁数,输入无效!")
# 制作第一张身份证:马云
p1 = Person("马云", 25)
print(f"证件编号:{id(p1)} | 姓名:{p1.name} | 年龄:{p1.age}")
# 改名
p1.set_name("马云云")
print(f"改名后:{p1.get_name()}")
# 故意填离谱年龄测试拦截
p1.set_age(250)
# 正常改年龄
p1.set_age(30)
print(f"修正后年龄:{p1.get_age()}")
print("-" * 60)
# 第二张独立身份证:马斯克
p2 = Person("马斯克", 26)
print(f"证件编号:{id(p2)} | 姓名:{p2.name} | 年龄:{p2.age}")
p2.set_name("马斯科")
p2.set_age(260)
p2.set_age(42)
print(f"最终信息:{p2.name},今年{p2.get_age()}岁")
运行效果
===== 身份证模板初始化演示 =====
新身份证制作完成,证件内存编号:2119567890128
证件编号:2119567890128 | 姓名:马云 | 年龄:25
改名后:马云云
年龄必须是0~150的正常人岁数,输入无效!
修正后年龄:30
------------------------------------------------------------
新身份证制作完成,证件内存编号:2119567890672
证件编号:2119567890672 | 姓名:马斯克 | 年龄:26
年龄必须是0~150的正常人岁数,输入无效!
最终信息:马斯科,今年42岁
封装到底有啥用?
不让外界随便乱改对象数据,就像改年龄必须走柜台 (set 方法),柜台会先检查合不合法,避免出现活 200 岁这种离谱数据。
三、趣味实战 1:随机坐标点,海龟自动连线画图小游戏
概念大白话
组合关系 = 收纳盒装零件 DrawBoard 是画板收纳盒,Point 是一个个坐标小零件,画板里面装一堆点,不用继承点类,拿来直接用,灵活又不乱。 运行代码会弹出绘图窗口,随机生成 3 个点位,自动把所有点两两连起来,一键画出三角形!
import random
import turtle
# 坐标小点零件
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def get_x(self):
return self.x
def get_y(self):
return self.y
# 画板收纳盒,装一堆Point小点
class DrawBoard:
def __init__(self, point_list):
self.points = point_list
def draw_all_line(self):
turtle.pensize(2)
turtle.color("#2266cc")
turtle.up()
# 双重循环:任意两个点都连一条线
for i in range(len(self.points)):
for j in range(i + 1, len(self.points)):
turtle.goto(self.points[i].get_x(), self.points[i].get_y())
turtle.down()
turtle.goto(self.points[j].get_x(), self.points[j].get_y())
turtle.up()
turtle.mainloop()
# 随机抓3个坐标点,范围正负200
points = [Point(random.randint(-200, 200), random.randint(-200, 200)) for _ in range(3)]
board = DrawBoard(points)
board.draw_all_line()
小提醒
不加 turtle.mainloop() 画图窗口会一闪就消失,相当于没保存画布!
四、趣味实战 2:万能图形模板,线段圆形自动算面积周长
两个关系通俗区分
- 继承(is-a 是一个) 圆形是图形、线段也是图形 → 共用一套图形规范,继承抽象基类 Shape
- 组合(has-a 拥有) 线段拥有两个坐标点、圆形拥有一个圆心点 → 把 Point 当成零件装进去
抽象基类 Shape 相当于强制合同,只要是图形,必须会算面积、周长、整体平移,少一个功能直接报错,统一规范不混乱。
import math
from abc import ABC, abstractmethod
# 坐标零件
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
# 整体挪动坐标
def translate(self, dx, dy):
self.x += dx
self.y += dy
def __str__(self):
return f"Point({self.x},{self.y})"
# 图形强制合同(抽象基类)
class Shape(ABC):
@abstractmethod
def area(self):
pass
@abstractmethod
def perimeter(self):
pass
@abstractmethod
def translate(self, dx, dy):
pass
# 线段:属于图形,内含两个坐标点
class Line(Shape):
def __init__(self, p1: Point, p2: Point):
self.p1 = p1
self.p2 = p2
# 计算两点距离(线段长度)
def length(self):
dx = self.p1.x - self.p2.x
dy = self.p1.y - self.p2.y
return math.sqrt(dx ** 2 + dy ** 2)
def area(self):
return 0.0 # 线条没有面积
def perimeter(self):
return self.length()
def translate(self, dx, dy):
self.p1.translate(dx, dy)
self.p2.translate(dx, dy)
def __str__(self):
return f"线段:{self.p1} → {self.p2}"
# 圆形:属于图形,内含一个圆心点
class Circle(Shape):
def __init__(self, center: Point, r):
self.center = center
self.r = r
def area(self):
return math.pi * self.r ** 2
def perimeter(self):
return 2 * math.pi * self.r
def translate(self, dx, dy):
self.center.translate(dx, dy)
def __str__(self):
return f"圆形:圆心{self.center},半径{self.r}"
if __name__ == "__main__":
p1 = Point(0, 0)
p2 = Point(3, 4)
line = Line(p1, p2)
print(line, f"线段总长:{line.length()}")
center = Point(1, 2)
circle = Circle(center, 5)
print(circle, f"面积:{circle.area():.2f},周长:{circle.perimeter():.2f}")
print("\n==== 一键平移所有图形,向右上方挪一格 ====")
# 多态魔法:不管线段还是圆,统一调用translate,各自实现逻辑
for shape in [line, circle]:
shape.translate(1, 1)
print(line)
print(circle)
输出效果
线段:Point(0,0) → Point(3,4) 线段总长:5.0
圆形:圆心Point(1,2),半径5 面积:78.54,周长:31.42
==== 一键平移所有图形,向右上方挪一格 ====
线段:Point(1,1) → Point(4,5)
圆形:圆心Point(2,3),半径5
五、本篇干货总结(大白话版)
__init__= 造对象时自动填信息,self 代表当前这个对象本人- 封装:改数据走 set 方法,提前校验,杜绝离谱数据
- 组合:类里面装其他类的对象(收纳盒放零件),适合包含关系
- 抽象基类 ABC:强制子类实现固定功能,统一代码规范
- 多态:不同图形共用同一个方法名,内部逻辑各玩各的,调用超省心
六、新手踩坑避雷清单
- 抽象基类不能直接造实例,所有抽象方法必须全部重写,不然代码报错
- turtle 绘图结尾一定要加
turtle.mainloop(),否则画布秒闪退 - 别直接在外部修改对象属性,破坏封装设计,后期难以维护

175

被折叠的 条评论
为什么被折叠?



