🐸 青蛙编程网
首页 / Python / 面向对象 / 1. 类与对象初识

类与对象初识

这是什么:类(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__ 登场
  • 记忆锚点:类是饼干模具,对象是压出来的饼干——模具一副,饼干随便做,块块独立。

动手改

  1. 给 Dog 加一个 eat 方法,打印 开饭啦!,再让两只狗各吃一次
  2. 再实例化第三只狗 third_dog,让它也 bark()
  3. 试试 print(Dog) 和 print(my_dog)——前者是图纸,后者是实物,输出看不懂没关系,混个眼熟

下一章:属性与方法,让每只狗都有自己的名字 🐸

📥 下载本章代码

ZIP 内含本章全部代码文件与说明,解压即用,与教程内容同步生成。