尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
面向对象核心:类与对象的区别、原理与实战
我经常被初学者问到一个问题类和对象到底有什么区别带新同学入门的时候几乎每个人都会在这个地方卡一下。这个问题看似基础其实特别值得花时间想透因为后面所有东西——继承、多态、封装、设计模式——全都站在这个地基上。这篇我打算用最直白的方式把类和对象的定义、区别、内存表现、实际写法以及新手最常见的几个坑一次讲清楚。不管你是刚开始学编程还是带人的导师应该都能从这里拿到一点能直接用的东西。1. 先给类和对象一个准确的心理模型1.1 类到底是什么本质是模板和规则类Class这个词来自“分类”。你写下的一个类是对某一类事物的抽象描述它定义了这类事物共有的属性数据和行为方法。比如“学生”这个类会定义学号、姓名、成绩这些属性也会定义选课、交作业这些方法。但要注意类本身不是一个具体的人它只是一套规则、一份模板。写代码的时候也是这样。你用class关键字定义的是一个结构代码运行到这个定义时解释器会创建一个“类对象”放着。这个类对象描述的是类型层面的信息一个学生有哪些字段一个文件有哪些操作。它不会自己变成某个具体的张三李四也不会自己产生一条真实的数据。很多新手容易忽略的一点是类本身在Python里也是一个对象一个type类型的对象。但通常我们说的“对象”默认指的是“实例对象”也就是通过类创建出来的那个具体东西。这一点会在后面第5章的坑里再次遇到。1.2 对象到底是什么类的一次具体实例化对象Object是类的一次实例化结果它是真实存在的、有具体数据的实体。还是拿“学生”举例当你说“张三学号2023001高数95选了操作系统课”这就是一个具体的学生对象。它来自“学生”这个模板但和模板不是一回事。你可以把类理解成“设计图纸”对象就是按图纸造出来的“房子”。图纸上写着“这里有三室一厅那里有一个厨房”这是所有房子的共性描述但真正盖出来的房子才有门牌号、有具体的朝向、有实际的墙体材料和入住的人。你不可能住进图纸里但你也不能没有图纸就随便盖房。类定义了结构对象承载了数据。1.3 为什么这么简单的概念就是容易混淆我带过不少新人观察下来混淆的核心原因是口语表达太容易省略了。有人跟你说“我建了一个User”听起来像创建了类实际上他创建的是User类的对象。有人写代码时又会说“我这个类里存了个数组”其实他往类的某个实例属性里塞了数据。再加上IDE自动补全的时候类名和实例名经常混着提示语法上又都能跑于是概念边界越来越模糊。另一个原因是没有建立“模板和实体”的对应关系。很多初学者把类当成一个用来装函数的容器把对象当成变量名。这种理解在写简单脚本时能用一碰到继承、多态就开始全面崩盘。所以我建议第一步先在心里刻出一个固定句式类是一类事物的规则对象是这类事物中的具体一个。每说一次“对象”脑子里都要跟一句“它是某个类造出来的实例”。2. 类和对象的五个核心区别2.1 定义与实例化一个是蓝图一个是成品最根本的区别在于“定义”和“实例化”是完全两个阶段。看代码最直观class Car: def __init__(self, brand, color): self.brand brand self.color color def drive(self): print(f{self.brand} is driving)上面这段只是定义了一个类代码跑完内存里多了一份“汽车”模板但没有汽车。要得到一辆具体的车必须调用类名并传入参数my_car Car(某品牌, 白色) your_car Car(某品牌, 黑色)这个时候才产生了两个对象my_car和your_car。它们都来自于Car这个模板都拥有 brand 和 color 属性但属性值完全不同。一句话类负责描述所有车应该长什么样对象负责做出某一辆确定的车。2.2 类有共性对象有个性类里面定义的属性是所有实例“共同拥有”的结构。比如每个学生都有学号和姓名这是共性但每个学生的学号具体是多少姓名怎么写这是个性保存在对象里。用小区户型来类比最合适。同一栋楼同一个户型图纸完全一样这就是类但每家装修风格不同、住的家庭成员不同这就是对象之间的差异。你不可能在图纸上住人也不可能让所有家庭都长得一模一样。从代码层面看共性的东西往往放在类变量里个性的东西放在实例变量里。比如class Student: school 某高校 # 共性所有学生同一个学校 def __init__(self, student_id, name): self.student_id student_id # 个性每个学生不同学号 self.name name # 个性每个学生不同姓名这个例子引入了类变量和实例变量的区别。如果你在__init__外面直接写了一个可变对象当属性后面很容易踩到共享数据的坑这个我留到第5章详细说。2.3 状态存于对象规则存于类对象是有状态的而且状态会变化。一辆车刚提出来油箱是满的开几天油就少了这个“油量”就是对象的状态。类定义里只会写“车有油箱”不会写“油箱现在是半满还是全满”。实操中的体现就是方法里的self.xxx属性几乎总是可以被重新赋值的。同一个对象身上的同一属性在不同时刻可以有不同的值。而类本身通常不会在运行中被随意修改——虽然Python允许你动态往类上加属性但那是很少见的操作属于高级技巧正常项目里不该这么干。这也意味着数据放在对象里逻辑放在类里。类的方法定义的是“这个对象能干什么”对象的属性记录的是“这个对象现在是什么状态”。理解这一点写出来的代码会清晰很多。2.4 内存表现和生命周期差异明显类和对象在内存里的存在方式完全不同。类在模块加载时就会创建整个程序运行期间几乎只有一份生命周期特别长。而对象是在你调用类名()的那一刻创建出来的可以创建无数个用完之后可以被垃圾回收。从内存角度讲类对象里存的是方法定义、类变量等元信息实例对象里存的是通过self.xxx yyy赋值的那些实例属性。Python中每个对象有一个__dict__你在里面查一下就能看到print(Car.__dict__) print(my_car.__dict__)my_car.__dict__里只有brand和color而Car.__dict__里有__init__、drive这些方法。这是特别好的一个验证手段能直观看到“规则”和“数据”的位置。2.5 一张速查表先记住再深入对比维度类Class对象Object本质模板、蓝图、抽象实例、实体、具体创建方式用class定义用类名()实例化数量通常一个可以有无数个内存内容方法、类变量、属性定义实例变量、实际数据能否修改一般不修改属性可以随时变化生命周期程序运行期间一直存在创建后用完可被回收存在意义定义有什么、能做什么表示当前的、具体的状态用这个表当提纲往下看第3章的实操你就能把每一条都对应到真正的代码里。3. 实操演示写一个小型库存系统的类与对象3.1 先想清楚要做什么光说不练假把式。我拿一个最常见的业务场景来走一遍某公司的仓库里有很多商品每个商品有名字、单价、库存数量还需要支持扣减库存的操作。快速想一下这个系统该怎么用类和对象来组织。商品有共性所有商品都有名字、价格、库存。这是“类”要表达的。每个具体商品又有不同的值A商品叫某个名字、库存100B商品叫另一个名字、库存3。这是“对象”要表达的。新手容易犯的错误是为了省事直接写一堆变量product1_name、product1_price、product2_name、product2_price这样写到第10个商品就崩溃了。用类来抽象之后代码的扩展性和可读性都会好很多。3.2 定义一个商品类属性、构造函数、业务方法下面是这个类的完整代码我加了注释说明每一部分在干嘛class Product: # __init__ 是构造函数负责在创建对象时完成初始化 def __init__(self, name, price, stock): self.name name # 实例属性商品名 self.price price # 实例属性单价 self.stock stock # 实例属性库存数量 # 一个业务方法扣减库存 def deduct_stock(self, num): if num 0: raise ValueError(扣减数量必须为正数) if num self.stock: raise ValueError(f库存不足当前库存: {self.stock}) self.stock - num return True # 一个业务方法展示商品信息 def show_info(self): print(f商品: {self.name}, 单价: {self.price}, 库存: {self.stock})注意到没有类的定义里没有具体数字。没有写“某个商品是10块钱”只写了“商品必须有价格这个属性”。具体值全部留到创建对象时通过__init__参数传进来。这就是类和对象分工最典型的样子。3.3 创建对象操作对象接着实例化几个商品然后调用方法p1 Product(无线鼠标, 99, 50) p2 Product(机械键盘, 399, 20) p1.show_info() # 输出: 商品: 无线鼠标, 单价: 99, 库存: 50 p2.show_info() # 输出: 商品: 机械键盘, 单价: 399, 库存: 20 p1.deduct_stock(10) p1.show_info() # 输出: 商品: 无线鼠标, 单价: 99, 库存: 40 p2.show_info() # 输出: 商品: 机械键盘, 单价: 399, 库存: 20注意看p1扣了库存p2完全没受影响。这就是“对象之间相互独立”的含义。它们明明来自同一个类但各自维护各自的属性变成不同的个体。3.4 对象独立性的直观验证有些初学者会担心是不是所有对象都共享同一份属性答案是否定的除非你用了类变量。可以这样验证print(p1 is p2) # False两个对象不是同一个 print(p1.__dict__) # {name: 无线鼠标, price: 99, stock: 40} print(p2.__dict__) # {name: 机械键盘, price: 399, stock: 20}每个对象的属性字典都是独立的改一个不会影响另一个。这正是面向对象最重要的好处之一数据和操作被绑定在一起而且互不干扰。等学到继承和多态之后你会发现这套独立性是所有高级特性的基础。4. 类成员与实例成员区别背后的职责分工4.1 类变量与实例变量放错位置就出事前面代码里写的self.xxx是实例变量每个对象一份。但如果在类体里直接赋值那就是类变量所有对象共享。看下面的代码class Demo: shared [] # 类变量所有实例共享 def __init__(self): self.own [] # 实例变量每个实例独立shared是类变量own是实例变量。逻辑上类变量应该用来表达“所有实例共同拥有的状态”比如前面例子里的school实例变量用来表达“每个实例自己的状态”。可变类型当类变量是重灾区。shared []这个写法如果在一个实例里调用了shared.append(...)所有其他实例都会看到这个新增的数据。新手排查半天往往就是因为这里放错了。4.2 实例方法、类方法、静态方法三类方法的取舍Python里方法按用途也分成三类理解了它们就更能理解类与对象的分工class Tool: def instance_method(self): return 我是实例方法 classmethod def class_method(cls): return 我是类方法 staticmethod def static_method(): return 我是静态方法实例方法必须通过对象调用第一个参数是self它访问的是实例数据。类方法第一个参数是cls它操作的是类本身通常用来实现一些不依赖具体对象的功能比如工厂方法。静态方法既不依赖对象也不依赖类逻辑上放在类里只是为了组织代码。新手阶段最常用的是实例方法因为大多数业务逻辑都是在操作对象的数据。类方法偶尔用用静态方法少用。但如果能搞清这三者的区别你对“类与对象分工”的理解会上一个台阶。4.3 设计时的一个判断准则写新业务时可以问自己三个问题来确定成员放哪数据是每个实例各不相同吗如果是放实例变量。数据是所有实例共享的吗如果是放类变量。这个功能是不是必须拿到具体对象的数据才能完成如果完全不依赖对象就没必要做成实例方法。我见过很多新人在写工具类时把一堆函数用class包起来但从不实例化最后每个方法都要传一堆参数。这种代码本质上就是一群静态方法用模块级函数也能实现。没必要强行套类。类和对象的价值在于把状态和行为绑定在一起如果状态都不存在那就别硬造类。5. 现场答疑新手最容易踩的五个坑5.1 拿到的是类不是对象p Product # 少了括号 p(无线鼠标, 99, 50) # 这样调用其实是把 Product 这个类本身给了 pp Product赋值时p 是类对象不是实例。之后调用p(...)其实等于Product(...)如果能跑通纯粹是碰巧。真正正确的写法是p Product(无线鼠标, 99, 50)。排查的时候如果发现类型不对第一反应就是检查是不是漏了括号。5.2 类变量被意外共享前面提过可变类型放类变量会出事。完整演示一遍class Bag: items [] a Bag() b Bag() a.items.append(钥匙) print(b.items) # [钥匙]b 被影响了明明只往 a 里放东西b 却看到了。原因就是items是类变量a和b共享同一个列表。解决办法是把可变类型放回实例变量在__init__里初始化class Bag: def __init__(self): self.items [] a Bag() b Bag() a.items.append(钥匙) print(b.items) # []互不影响这是面向对象初学者最常见的隐蔽Bug推荐每次从类里创建集合、字典等可变对象时都先想一下是共享还是独立。5.3 isinstance 和 type 的误用判断对象类型时isinstance比type更靠谱。type(obj) is X要求完全同类型才算通过而isinstance(obj, X)会考虑继承关系。比如子类实例用type判断父类会得到 False但用isinstance判断父类会得到 True。在实际代码里绝大多数场景应该用isinstance因为你一旦用到继承子类对象本质上也是父类的一种。只要求精确匹配的场景才用type比较。5.4 用 is 还是用 比较对象新手经常把两个对象的比较搞混。比较的是值很多时候需要你在类里定义__eq__方法is比较的是身份也就是是不是同一个对象。class Student: def __init__(self, name): self.name name def __eq__(self, other): return isinstance(other, Student) and self.name other.name s1 Student(张三) s2 Student(张三) print(s1 s2) # True因为定义了 __eq__ print(s1 is s2) # False因为不是同一个对象业务上判断“两个对象内容相等”用判断“两个对象是不是同一个实例”用is。别把两者混着用。5.5 问题排查速查表现象可能原因解决方向调用方法时报“缺少 self 参数”通过类直接调用了实例方法先实例化再调用某属性改了一个对象其他对象也变了属性实际上是类变量移到__init__中初始化使用类名当普通函数使用漏写了实例化的括号补上()类型判断结果总是不对使用了type而不是isinstance改用isinstance两个对象明明内容相同却不相等类里没有定义__eq__按业务逻辑实现__eq__这张表基本覆盖了前六个月初学者在类和对象上会遇到的大部分问题。把这个保存下来排查时按图索骥能省很多时间。6. 写在最后一点实操心得我教新人时特别喜欢让他们做一件事写一个简单的类然后手动创建十几个不同的对象逐个打印它们的id和__dict__。看起来幼稚但真的能让人瞬间明白“类是一份模板对象是独立个体”这句抽象的话。做完这一步再学继承、多态进度会快很多。另外一个经验是写代码时多用“实例化”这个词少用“创建”这种含糊说法。比如你在心里默念“我现在要实例化一个 Product 对象”念几次概念就自然准确了。语言会影响思维技术上也一样。类和对象的区别说到底就一句话类定义规则对象承载状态。把这句话变成你写代码时的默认反应后面碰到的很多设计问题都会迎刃而解。
RELATED

相关推荐

WGS84转ENU坐标转换从原理到实战,避免那些坑

WGS84转ENU坐标转换从原理到实战,避免那些坑

在户外测量、无人机航线规划、自动驾驶路测或者机器人室内外定位的时候,你一定遇到过这种场景:手里拿到的是GPS设备输出的经纬度——也就是WGS84坐标系下的数据,但你要用这套数据去控制车辆或者飞机,去跟激光雷达点云做对齐&#…

📅 2026/10/16 11:08:43
giflib 4.1.6 64位静态库在Windows MSVC工程中的配置与避坑指南

giflib 4.1.6 64位静态库在Windows MSVC工程中的配置与避坑指南

简介:面向需要在 osg 3.2.1 中编译 Plugin gif 的开发者,这份资源提供了 4.1.6 版本的 64 位静态链接库 giflib.lib 及配套头文件,解决了 GIF 图像编解码库与 OpenSceneGraph 集成时的链接难题。库内包含 giflib.lib、giflibd.lib&#xff08…

📅 2026/10/16 11:08:42
从爬虫到Flutter影视App:移动端全栈实践完整指南

从爬虫到Flutter影视App:移动端全栈实践完整指南

电影全栈系统的前几篇,我们把爬虫、后端接口、数据库、推荐服务都搭了起来,服务端的数据管道已经能稳定输出。但整个系统始终缺一个真正面向用户的东西,数据在接口里躺着,后台能看到,却没有任何一个贴近日常使用场景的…

📅 2026/10/16 11:08:42
MORE NEWS

更多资讯

📰

角度调制、强度调制与孔径调制:光场操控的三种核心手段

很多人第一次接触“光线的角度调制”、“像面的强度调制”、“光场的孔径调制”这三个词时,第一反应是:这怕不是光学课本里三个独立的章节?我最初也这么以为,直到自己动手搭了一套尺寸测量系统,才意识到它们其实是描述…

📰

若依微服务接入MySQL与达梦双数据源实战避坑指南

接手一个老项目改造任务,主库是 MySQL,业务方要求把一部分报表数据和历史归档数据放到国产数据库达梦(DM)上,同时旧的业务接口还得继续读写 MySQL——说白了,就是要给若依微服务接上 MySQL DM 多数据源。 …

📰

深度学习调参指南:从科学实验到增量策略的实战方法

简介:深度学习模型的性能高度依赖超参数配置,而学习率、Batch Size、Adam 参数之间强耦合,使得孤立调参几乎无效。理解超参数分类与实验设计是系统性优化的前提:通过复现基线、增量调整、准随机搜索等策略,可逐步明确每…

📰

PaddleOCR训练数据实战指南:构建高鲁棒OCR数据流水线

1. 项目概述:为什么“训练数据”才是PaddleOCR落地成败的真正分水岭很多人第一次接触PaddleOCR,注意力全在模型结构、识别精度、部署速度这些光鲜指标上——下载预训练模型,跑通demo,看到“准确率98.2%”就以为万事大吉。但真正带…

📰

SpringBoot+Vue3+MyBatis乡村政务办公系统开发实战指南

先聊点背景。前阵子接了个活儿,某乡镇要做一套政务办公系统,把村里那套纸质流转、电话通知、微信群传文件的路子换成线上办理,我当时定的技术组合就是项目标题里这套:Java SpringBoot 做后端服务,Vue3 负责前端界面&am…

📰

IMU标定与Allan方差分析:从确定性误差补偿到随机噪声建模的工程实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬