Python 面向对象小白通关指南!构造函数 / 封装 / 几何绘图手把手玩明白

一、前言

还在看不懂类、实例、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:万能图形模板,线段圆形自动算面积周长

两个关系通俗区分

  1. 继承(is-a 是一个) 圆形是图形、线段也是图形 → 共用一套图形规范,继承抽象基类 Shape
  2. 组合(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

五、本篇干货总结(大白话版)

  1. __init__ = 造对象时自动填信息,self 代表当前这个对象本人
  2. 封装:改数据走 set 方法,提前校验,杜绝离谱数据
  3. 组合:类里面装其他类的对象(收纳盒放零件),适合包含关系
  4. 抽象基类 ABC:强制子类实现固定功能,统一代码规范
  5. 多态:不同图形共用同一个方法名,内部逻辑各玩各的,调用超省心

六、新手踩坑避雷清单

  1. 抽象基类不能直接造实例,所有抽象方法必须全部重写,不然代码报错
  2. turtle 绘图结尾一定要加 turtle.mainloop(),否则画布秒闪退
  3. 别直接在外部修改对象属性,破坏封装设计,后期难以维护
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值