AI for Youth Academy 青少年AI研究计划

第一年 · 第12周

第十二章:什么是对象?

本周我们跨过编程中的一座大桥:从松散的变量和函数走向对象。对象把数据(属性)和动作(方法)打包成一个整齐的包裹。类是蓝图,告诉Python这种类型的每个对象应该长什么样。到这节课结束时,你将创建自己的对象,并理解程序员为什么要这样组织代码。

学习目标

  • 用通俗的话解释对象的区别
  • 为任何现实世界中的"东西"找出属性方法
  • 凭记忆写一个带参数和 if 语句的 Python 函数
  • class 关键字创建自己的类,并从类创建对象

热身(10分钟)——做一张宝可梦卡片!

每人拿到一张空白卡片。在卡片上写下:

  • 名字(比如:皮卡丘)
  • 类型(比如:电系)
  • HP(在1–100之间选一个数字)
  • 一个攻击招式名称(比如:十万伏特)
  • 攻击伤害值(比如:30)

找一个搭档"对战":说"我用十万伏特攻击,造成30点伤害!"你的搭档从自己的HP中减去30。

对战结束后,老师会问你:

  • 每张卡片都有哪些信息?——这些就是属性
  • 每只宝可梦都能做哪些动作?——这些就是方法

为什么需要类?先看混乱的写法

在没有类之前,程序员用单独的变量存储数据。看看这会变得多混乱:

# 混乱的写法——没有类
pikachu_name   = 'Pikachu'
pikachu_hp     = 100
pikachu_damage = 20

charmander_name   = 'Charmander'
charmander_hp     = 90
charmander_damage = 18

def attack(attacker_name, attacker_damage, target_hp):
    new_hp = target_hp - attacker_damage
    print(attacker_name, 'attacks for', attacker_damage, 'damage!')
    return new_hp

charmander_hp = attack(pikachu_name, pikachu_damage, charmander_hp)
print('Charmander HP:', charmander_hp)

想象一下如果有100只宝可梦。你会有300个变量!类让我们把一个对象的所有数据和动作打包成一个整齐的包裹。

你的第一个类

这里是一个最简单的宝可梦类——暂时不用 __init__,我们下节课再改进它。现在看看我们怎么把信息直接附加到对象上:

# 类就是蓝图
class Pokemon:
    pass   # 'pass' 表示'暂时为空'

# 从类创建一个对象
pikachu = Pokemon()
pikachu.name   = 'Pikachu'
pikachu.hp     = 100
pikachu.damage = 20

print(pikachu.name)   # 输出:Pikachu
print(pikachu.hp)     # 输出:100

# 再创建第二只宝可梦
charmander = Pokemon()
charmander.name   = 'Charmander'
charmander.hp     = 90
charmander.damage = 18

注意我们是怎么用点号来访问和设置属性的:

  • pikachu.name —— 对象名 + 点号 + 属性名
  • pikachu.hp —— 每个对象有自己独立的HP值

练习(25分钟)

打开 Thonny,按照下面的题目一步步完成。每道题先自己尝试写代码,再点击"显示答案"核对。

第1题:创建一个 Minecraft

写一个空的 Minecraft 类(跟上面的 Pokemon 一样用 pass)。然后创建一个叫 creeper 的对象,给它设置三个属性:

  • name 设为 'Creeper'
  • hp 设为 20
  • color 设为 'green'

最后用 print 打印出 creeper.namecreeper.hpcreeper.color

显示答案
class Minecraft:
    pass

creeper = Minecraft()
creeper.name  = 'Creeper'
creeper.hp    = 20
creeper.color = 'green'

print(creeper.name)
print(creeper.hp)
print(creeper.color)

先定义类,再用 Minecraft() 创建对象,然后用点号设置属性。

第2题:再创建一个 Minecraft 生物对象

用同一个 Minecraft 类,创建第二个对象叫 zombie

  • name 设为 'Zombie'
  • hp 设为 25
  • color 设为 'dark green'

然后打印 zombie.namezombie.hp

显示答案
zombie = Minecraft()
zombie.name  = 'Zombie'
zombie.hp    = 25
zombie.color = 'dark green'

print(zombie.name)
print(zombie.hp)

注意:不需要再写一次 class Minecraft。类只需要定义一次,可以反复创建多个对象。

第3题:概念题——类 vs 对象

回答以下两个问题:

  1. 在上面第1题和第2题中,是什么?对象有哪些?
  2. 如果 creeper.hp 从 20 变成了 0,zombie.hp 会跟着变吗?为什么?
显示答案

问题1Minecraft 是类(蓝图)。creeperzombie 是两个不同的对象(实体)。

问题2:不会。每个对象有自己独立的属性值。creeper.hpzombie.hp 是两个不同的"抽屉"——改一个不影响另一个。这就是对象的独立性

第4题:找属性和方法

想象一个"学校储物柜"。写出:

  1. 至少 3个属性(它存储的信息)
  2. 至少 2个方法(它能做的动作)
显示答案

属性(它拥有的数据):

  • locker_number —— 柜子编号(比如 42)
  • owner —— 使用者名字(比如 'Alice')
  • is_open —— 是否打开(True 或 False)
  • color —— 柜子颜色

方法(它能执行的动作):

  • open() —— 打开柜门
  • close() —— 关上柜门
  • change_owner(new_name) —— 更换使用者

记住:属性是名词(它什么),方法是动词(它能什么)。

第5题:用代码为储物柜建模

创建一个 Locker 类,然后创建一个储物柜对象并设置以下属性:

  • number = 42
  • owner = 'Alice'
  • is_open = False

写一段 if/else 代码:如果 is_openTrue,打印 "The locker is open";否则打印 "The locker is closed"

显示答案
class Locker:
    pass

my_locker = Locker()
my_locker.number  = 42
my_locker.owner   = 'Alice'
my_locker.is_open = False

if my_locker.is_open:
    print("The locker is open")
else:
    print("The locker is closed")

这里你把前面学过的创建对象if/else条件判断结合在一起了。对象和条件语句可以一起工作!

第6题:写一个攻击函数

写一个函数 attack(attacker, target),它做两件事:

  1. 打印 "[攻击者名字] attacks [目标名字]!"
  2. 把目标的 HP 减去攻击者的 damage,打印目标的新 HP

然后调用这个函数,让 creeper 攻击 zombie。

显示答案
def attack(attacker, target):
    print(attacker.name + ' attacks ' + target.name + '!')
    target.hp = target.hp - attacker.damage
    print(target.name + ' HP: ' + str(target.hp))

attack(creeper, zombie)

这个函数接收两个对象作为参数,然后用点号读取和修改它们的属性。运行后,zombie 的 HP 会从 25 变成 5(25 - 20 = 5)。

第7题:连续攻击

用第6题的 attack 函数,让 creeper 再攻击一次 zombie。然后写一段 if/else 代码判断 zombie 的 HP:

  • 如果 HP <= 0,打印 "[zombie名字] is defeated!"
  • 否则,打印 "[zombie名字] survived with [HP] HP!"
显示答案
attack(creeper, zombie)  # 第二次攻击

if zombie.hp <= 0:
    print(zombie.name + ' is defeated!')
else:
    print(zombie.name + ' survived with ' + str(zombie.hp) + ' HP!')

第一次攻击后 zombie 的 HP 是 5,第二次攻击后变成 5 - 20 = -15。因为 -15 <= 0,所以会打印 "Zombie is defeated!"

第8题:挑战题——创建你自己的类

选择一个你喜欢的东西(汽车、手机、宠物、食物……什么都可以),完成以下任务:

  1. 创建一个类(名字自己取,但要用英文,首字母大写,比如 CarPhone
  2. 创建两个不同的对象
  3. 每个对象至少有 3个属性(用点号设置)
  4. 写一个函数,接收你的对象作为参数,打印出至少两个属性
  5. 调用函数,传入两个对象
显示示例答案(以汽车为例)
class Car:
    pass

car1 = Car()
car1.brand  = 'Toyota'
car1.color  = 'red'
car1.speed  = 0

car2 = Car()
car2.brand  = 'Tesla'
car2.color  = 'white'
car2.speed  = 60

def show_car(car):
    print('Brand:', car.brand)
    print('Color:', car.color)
    print('Speed:', car.speed)

show_car(car1)
show_car(car2)

你可以选任何东西!关键是:定义类、创建多个对象、用点号设置属性、写函数操作对象。

第二课时:__init__self 和多个对象

时长:60 分钟

学习目标

  • 写一个带 __init__ 方法的类
  • 理解 self 指的是什么
  • 创建多个对象,每个对象有不同的属性值

饼干模具演示(10分钟)

在 Python 中:

class Pokemon:       # ← 模具
pikachu    = Pokemon()  # ← 一块饼干
charmander = Pokemon()  # ← 另一块饼干

上节课我们在创建对象之后一个一个附加属性。那样既麻烦又容易出错。__init__ 就是自动运行的"初始化配方"——每次创建新对象时都会执行。

认识 __init__self(25分钟)

class Pokemon:
    def __init__(self, name, hp, damage):
        self.name   = name    # 把 name 存到这个对象上
        self.hp     = hp      # 把 hp 存到这个对象上
        self.damage = damage  # 把 damage 存到这个对象上

# 现在创建一只宝可梦只需一行简洁的代码:
pikachu    = Pokemon('Pikachu',    100, 20)
charmander = Pokemon('Charmander',  90, 18)
squirtle   = Pokemon('Squirtle',    95, 15)

print(pikachu.name)       # Pikachu
print(charmander.hp)      # 90
print(squirtle.damage)    # 15

常见错误——千万别这样写!

# 错误——忘记写 self. 在属性名前面
class Pokemon:
    def __init__(self, name, hp):
        name = name     # 错!这只是在创建一个局部变量!
        hp   = hp       # __init__ 结束后它就消失了。

# 正确写法
class Pokemon:
    def __init__(self, name, hp):
        self.name = name   # 存到对象上
        self.hp   = hp     # 存到对象上

练习——组建你的队伍(20分钟)

创建3只宝可梦(或者宠物、Minecraft 生物——你选)。打印每个对象的一个属性来检查是否正确。

出口票

在纸上写出下面这个类的 __init__ 方法:

class Spaceship:
    # 它应该存储:name、fuel、speed
    def __init__(self, ???):
        ...

展望未来

本周你学会了用类作为蓝图创建对象,用点号设置和读取属性,还学会了用 __init__ 方法让创建对象变得像调用函数一样简洁。下周我们将学习如何给类添加方法——让对象不仅能存储数据,还能执行动作。