类与对象初识
这是什么:类(class) = 给"一类东西"做的图纸,照着它能造出无数个具体的对象(instance)。
为什么用:前置知识是 Python 基础。基础篇用变量和函数写程序,程序一大变量就越摊越多,数据和能力散落各处;有了面向对象,才能把它们"打包"管理。
如何用:先用变量写一只狗体会痛点,再定义第一个 Dog 类,照着图纸造出实例。
为什么需要类
先看痛点。用变量描述宠物店里的一只狗:
name = "旺财"
age = 3
breed = "柴犬"
一只狗要三个变量,店里来三只狗就是九个,名字还不能撞车;狗"会叫"这件事,又得另写一个函数。数据和能力散落各处,正是混乱的开始。
类(class)就是打包工具:把"一类东西"的数据和能力装进一张图纸,要多少个就照着造多少个。
定义第一个类
class Dog:
def bark(self):
print("汪汪!我是一只狗")
| 写法 | 含义 |
|---|---|
class Dog: |
定义名为 Dog 的类;类名习惯大写开头 |
def bark(self): |
类里定义的函数叫方法;self 先照抄,下一章细讲 |
| 缩进 | 类的内容必须缩进,和 if、for 一个规矩 |
注意:这段代码运行后什么都不会发生——定义类只是画好图纸,还没开工。
实例化:照图纸造狗
class Dog:
def bark(self):
print("汪汪!我是一只狗")
my_dog = Dog() # 类名加括号 = 实例化,造出一只具体的狗
your_dog = Dog() # 图纸在手,想造几只造几只
my_dog.bark()
your_dog.bark()
汪汪!我是一只狗
汪汪!我是一只狗
| 代码 | 作用 |
|---|---|
Dog() |
实例化:按图纸造出一个对象(也叫实例) |
my_dog |
普通变量,指向刚造出来的那只狗 |
my_dog.bark() |
用「对象.方法()」让这只狗执行能力 |
从此"三只狗九个变量"的噩梦消失——需要一只狗,Dog() 一下就好。
记忆口诀:类是图纸,对象是照图纸造出的实物;类名加括号,就是开工。
❓ 常见问题
Q1:报 TypeError: Dog.bark() missing 1 required positional argument: 'self' 是怎么回事?
原因:直接拿类去调方法了——写了 Dog.bark()。方法得由"具体的狗"来执行,光有图纸喊不动;用类调用时,self 找不到可以依附的对象,Python 只能报错。
修复三步:① 看报错行,检查是不是写了 Dog.bark();② 先实例化:my_dog = Dog();③ 再用对象调用:my_dog.bark()。
举一反三:以后凡见 missing 1 required positional argument: 'self',九成是"把图纸当实物用了",补一句实例化就好。
Q2:类定义写完,运行却什么都没输出,是程序坏了吗?
原因:不是,这是正常的!定义类只是画图纸,class Dog: 底下的内容要等实例化 + 调用方法才会真正执行。程序按兵不动,是在等你开工。
修复三步:① 检查代码里有没有 xxx = Dog();② 有没有 xxx.bark();③ 两样都补上,输出立刻就来。
举一反三:以后遇到"程序没反应",先分清是定义还没被执行(类、函数都算)还是执行了但没打印,两种情况的查法完全不同。
Q3:报 IndentationError: expected an indented block after class definition on line 1?
原因:class Dog: 冒号后面没缩进。类的内容必须整体缩进一层(4 个空格),和 if、for 一个规矩——Python 靠缩进判断"哪些代码属于这个类"。
修复三步:① 按报错行号找到 class 那一行;② 把下面的方法定义整体往右缩进 4 格;③ 方法体里的语句再缩进一层,保持整齐。
举一反三:IndentationError 家族的报错都查缩进:冒号下一行必须缩进,同一层的语句缩进量必须一致,Tab 和空格别混用。
Q4:报 AttributeError: 'Dog' object has no attribute 'barkk'?
原因:方法名拼错了(多半多个 k)。Python 在这只狗身上翻了个遍,也没找到叫 barkk 的能力,只能报错摊手。
修复三步:① 看报错里 no attribute 'xxx' 的 xxx 到底是什么;② 回到类定义处,逐个字母对照方法名拼写;③ 改成和定义完全一致——大小写也算拼写。
举一反三:所有 AttributeError 的潜台词都一样——"这个对象身上没有这个东西",先查拼写,再查是不是压根没定义。
Q5:print(my_dog) 输出 <__main__.Dog object at 0x0000021A...>,是一串乱码吗?
原因:不是乱码,是"实名制标签":__main__ 表示这个类定义在当前文件,Dog 是类名,0x... 是这只对象在内存里的门牌号。Python 默认就这么介绍对象,因为还没教它说"人话"(那是后面魔术方法章 __str__ 的任务)。
修复三步:① 先记住:这是默认行为,不是错误;② 现阶段想看对象会什么,就调用它的方法:my_dog.bark();③ 想让 print 显示人话,学到魔术方法章解锁 __str__。
举一反三:任何自定义对象直接 print 都长这样,别慌,更不用去解析那串地址。
⚠️ 新手翻车现场
翻车一:拿图纸直接喊
class Dog:
def bark(self):
print("汪汪!")
Dog.bark() # 想让"狗"这个种族叫一声
TypeError: Dog.bark() missing 1 required positional argument: 'self'
逐行分析:前 3 行画图纸没毛病,错在第 6 行——Dog.bark() 用类调用方法,self 没有一只具体的狗可以当,直接被拒。方法必须由对象来执行。正确写法:
class Dog:
def bark(self):
print("汪汪!")
my_dog = Dog() # 先造一只具体的狗
my_dog.bark() # 再让它叫
翻车二:方法忘了写 self
class Dog:
def bark(): # 少写了 self
print("汪汪!")
my_dog = Dog()
my_dog.bark()
TypeError: Dog.bark() takes 0 positional arguments but 1 was given
逐行分析:病根在第 2 行——def bark(): 没给 self 留位置;报错在第 7 行引爆——调用 my_dog.bark() 时,Python 自动把 my_dog 塞给第一个参数,参数表却写明"一个都不收",多出来的被拒收。记住:类里定义的方法,第一个参数永远照抄 self。
翻车三:调用图纸里没有的能力
class Dog:
def bark(self):
print("汪汪!")
my_dog = Dog()
my_dog.eat() # 类里从没定义过 eat
AttributeError: 'Dog' object has no attribute 'eat'
逐行分析:错在第 7 行——这只狗的图纸只教会了 bark,eat 从没定义过。对象做不了图纸上没有的事,调用前先回类定义确认方法名(也别拼错)。
📦 完整小示例
一个会做家务的机器人:把三种能力打包进 Robot 类,想造几台造几台。
class Robot:
def greet(self):
print("你好,我是家务机器人")
def work(self):
print("叮——开始打扫房间")
def charge(self):
print("电量不足,回充电桩补能")
r1 = Robot()
r2 = Robot()
r1.greet()
r1.work()
r1.charge()
r2.greet()
r2.work()
你会看到:
你好,我是家务机器人
叮——开始打扫房间
电量不足,回充电桩补能
你好,我是家务机器人
叮——开始打扫房间
逐段中文精讲
- 第 1~7 行(画图纸):一个类装三个方法,"打招呼、干活、充电"三种能力集中归档;方法里的
self先照抄,它代表"这台机器人自己"。 - 第 9~10 行(照图纸造两台):
Robot()执行两次,r1、r2 是两台互相独立的机器人,各干各的。 - 第 12~14 行(一号机流程):用「对象.方法()」依次点能力,输出顺序就是调用顺序。
- 第 16~17 行(二号机流程):同一张图纸,二号机只执行了打招呼和打扫两个动作——你调谁,谁才动,互不干扰。
跑通后做个小实验:再造一台 r3 = Robot(),让它把三个方法各调一遍;再修改 charge 方法里的文字——图纸改一处,之后所有机器人开跑都用新台词。
🎯 什么时候用它
- 游戏刷怪:一个
Monster类写好"移动、攻击、掉血",游戏运行时Monster()连续造出成百上千只怪,每只都是独立对象——逐个手写变量是不可能的。 - 能力归档:把"发邮件""生成报表"这类一整套操作写成一个类的方法,用时造个对象出来点方法,比满屏散落的函数好找好管。
- 团队协作先立图纸:和人合写项目时,先把"有哪些类、每个类有哪些方法"的名单定好,别人照着
对象.方法()调用即可,不用关心内部怎么实现。
📝 本章小结
- 类是图纸,对象是实物:
class Dog:画图纸,Dog()照图纸开工,一张图纸能造无数对象 - 类里定义的函数叫方法,第一个参数照抄
self - 「对象.方法()」让这只对象干活;用类直接调方法会报缺 self 的错
- 直接
print(对象)只会显示内存地址,想看人话要等__str__登场 - 记忆锚点:类是饼干模具,对象是压出来的饼干——模具一副,饼干随便做,块块独立。
动手改
- 给
Dog加一个eat方法,打印开饭啦!,再让两只狗各吃一次 - 再实例化第三只狗
third_dog,让它也bark() - 试试
print(Dog)和print(my_dog)——前者是图纸,后者是实物,输出看不懂没关系,混个眼熟
下一章:属性与方法,让每只狗都有自己的名字 🐸