当前位置:首页>python>《Python 从入门到精通》076|类属性、类方法、静态方法的区别

《Python 从入门到精通》076|类属性、类方法、静态方法的区别

  • 2026-10-11 06:17:28
《Python 从入门到精通》076|类属性、类方法、静态方法的区别
今日分享:沉默成本不参与重大决策!

学到这里,很多人会开始有一种感觉:

类、对象、实例属性、实例方法、__init__、self 这一套,刚刚有点顺了。结果又冒出来三个新词:

类属性 类方法 静态方法

一看名字就很像,一不小心就会混成一团。

这是非常正常的。因为这三样东西确实都和“类”有关,但它们解决的问题并不一样。很多新手卡在这里,不是因为太难,而是因为没先把“它们各自服务谁”这件事想清楚。

这一章,我们就专门把这三个概念讲透,而且不绕弯。

你只要先记住一句总纲:

实例的东西,主要服务某个具体对象。类的东西,主要服务整个类。静态方法,更像是放在类名下面的普通工具函数。

这一句如果先立住,后面就会轻松很多。

一、先从最熟悉的实例属性和实例方法出发

在讲新东西之前,我们先把熟悉的部分拿出来对照。

比如:

classStudent:def__init__(self, name, score):        self.name = name        self.score = scoredefintroduce(self):        print(f'我叫{self.name},成绩是{self.score}')

这里的 self.name 和 self.score,是实例属性。introduce() 是实例方法。

它们的特点非常明确:

属于某个具体对象 不同对象可以有不同值 方法里通常会用到 self

比如:

stu1 = Student('张三', 95)stu2 = Student('李四', 88)stu1.introduce()stu2.introduce()

这里两个对象的数据明显不同。

也就是说,实例这一套,重点在“对象自己”。

那接下来我们就要问:

如果有一些数据,不应该每个对象单独一份,而是整类对象共享一份,该怎么办? 如果有一些方法,不是服务某个具体对象,而是服务整个类,又该怎么办? 如果有一些函数,只是逻辑上和这个类相关,但其实既不依赖对象,也不依赖类本身,又该怎么办?

这三个问题,正好对应今天这章的三个主角。

二、什么是类属性

类属性,先给最直白的定义:

类属性是属于类本身的属性,而不是某个具体对象独有的属性。

换句话说,它是这一类对象共享的数据。

比如一个学生类里,所有学生都属于同一所学校。 那么“学校名称”这个数据,就不适合放成每个对象各自一份。因为张三和李四都在这个学校,没必要一人存一遍。

这时候就很适合用类属性。

看代码:

classStudent:    school = '第一中学'def__init__(self, name, score):        self.name = name        self.score = score

这里的:

school = '第一中学'

就叫类属性。

注意它写的位置。 它不是在 __init__ 里,也不是 self.school。 它是直接写在类体里面,和方法同级。

这说明它属于整个类,而不是某一个对象。

三、类属性和实例属性最大的区别是什么

最核心的区别只有一句话:

实例属性属于对象自己,类属性属于整个类。

还是拿学生类举例。

如果是:

self.name = nameself.score = score

那这是每个学生对象自己的数据。张三和李四当然不一样。

如果是:

school = '第一中学'

那这是所有学生共同拥有的设定。

你可以这么理解:

姓名、成绩,显然是“一个学生一个样”,所以是实例属性。 学校名称、学生总数、统一学期编号这种,更像“这一类对象共享的信息”,所以适合类属性。

这个分类标准,比死记定义有用得多。

四、类属性怎么访问

类属性有两种常见访问方式。

第一种,用类名访问:

print(Student.school)

第二种,用对象访问:

stu1 = Student('张三', 95)print(stu1.school)

这两种通常都能拿到值。

例如:

classStudent:    school = '第一中学'def__init__(self, name, score):        self.name = name        self.score = scorestu1 = Student('张三', 95)stu2 = Student('李四', 88)print(Student.school)print(stu1.school)print(stu2.school)

输出结果:

第一中学第一中学第一中学

但从习惯上说,类属性更推荐用类名来访问。因为它本来就是类级别的数据,用类名访问更清楚,也更不容易混淆。

五、什么时候该用类属性

这是比定义更重要的问题。

你可以用一个判断标准:

如果一份数据应该被所有对象共享,而且逻辑上属于整个类,就可以考虑用类属性。

比如:

学生类里的学校名称 员工类里的公司名称 商品类里的税率 账户类里的统一利率 游戏角色类里的默认最大等级

这些东西,通常不是某个对象私人独有,而是整个类别共用的规则或背景信息。

再比如我们可以写:

classEmployee:    company = '未来科技有限公司'def__init__(self, name, salary):        self.name = name        self.salary = salary

这里所有员工都属于同一家公司,所以 company 很适合做类属性。

六、类属性最典型的实战用途之一:计数

类属性有一个非常经典的用法,就是记录“总共有多少个对象被创建了”。

例如:

classStudent:    count = 0def__init__(self, name):        self.name = name        Student.count += 1

创建对象:

stu1 = Student('张三')stu2 = Student('李四')stu3 = Student('王五')print(Student.count)

输出结果:

3

这里的 count 就很适合用类属性。

因为它不是某个学生自己的数据。 它表示的是整个 Student 这一类对象一共创建了多少个。

这类场景,你后面会经常见到。

七、什么是类方法

先给定义:

类方法是绑定给类的方法,它的第一个参数通常是 cls,表示当前类本身。

注意,它和实例方法很像,但服务对象不同。

实例方法的第一个参数通常是 self,表示当前对象。 类方法的第一个参数通常是 cls,表示当前类。

它的典型写法要配合一个装饰器:

@classmethod

例如:

classStudent:    school = '第一中学'def__init__(self, name):        self.name = name    @classmethoddefshow_school(cls):        print(f'学校名称是:{cls.school}')

调用:

Student.show_school()

输出结果:

学校名称是:第一中学

这里的 show_school() 就是一个类方法。

八、类方法为什么要用 cls

你现在可以把 cls 粗略理解成:

当前这个类自己。

这和前一章里的 self 是同一个思路。

self 表示当前对象自己。cls 表示当前类自己。

所以这句:

print(cls.school)

意思就是:

读取当前类自己的 school 类属性。

你会发现,这和实例方法的逻辑其实很统一。只是服务层级不同。

九、类方法最适合做什么

类方法最适合做两类事。

第一类,操作类属性。 第二类,写一些“逻辑上更适合由类来完成”的功能。

最常见的就是第一类。

比如前面那个计数例子,我们就可以再加一个类方法来展示当前总人数:

classStudent:    count = 0def__init__(self, name):        self.name = name        Student.count += 1    @classmethoddefshow_count(cls):        print(f'当前一共创建了 {cls.count} 个学生对象')

调用:

stu1 = Student('张三')stu2 = Student('李四')Student.show_count()

输出结果:

当前一共创建了 2 个学生对象

这时候你会明显感觉到,show_count() 更适合归类来管,而不是归某个学生对象来管。

因为“总人数”本来就不是某个对象个人的事,而是整个类层面的信息。

十、类方法和实例方法最根本的区别

一句话概括:

实例方法主要操作对象自己的数据,类方法主要操作类自己的数据。

比如:

defintroduce(self):    print(self.name)

这里关心的是对象的姓名,是实例方法。

而:

@classmethoddefshow_school(cls):    print(cls.school)

这里关心的是学校名称,是类属性,所以更适合类方法。

你可以这样粗略分工:

对象的个体行为,用实例方法。 整类共享信息的处理,用类方法。

这样分,基本不会太乱。

十一、什么是静态方法

现在到了第三个最容易让人懵的东西:静态方法。

先给一个非常接地气的定义:

静态方法是放在类里面的普通函数。它既不依赖对象,也不依赖类本身。

它的写法要配合:

@staticmethod

例如:

classMathTool:    @staticmethoddefadd(a, b):return a + b

调用:

print(MathTool.add(3, 5))

输出结果:

8

这里你会发现一个明显特点:

这个方法里既没有 self,也没有 cls。 因为它根本不需要知道是谁在调用,也不需要访问类属性或实例属性。

它只是一个逻辑上“和这个类放在一起比较合适”的工具函数。

十二、静态方法为什么还要放进类里

这正是很多人疑惑的地方。

既然它这么像普通函数,那为什么不直接写成类外面的函数?

答案是:

因为逻辑上它和这个类有关,把它放进类里更利于组织代码。

比如一个用户类,可能会有一个检查手机号是否合法的工具方法。这个方法不需要访问某个具体用户对象,也不需要依赖整个类属性,但它和“用户”这个主题明显相关。

例如:

classUser:    @staticmethoddefis_valid_phone(phone):return len(phone) == 11and phone.isdigit()

调用:

print(User.is_valid_phone('13800138000'))print(User.is_valid_phone('abc'))

输出结果:

TrueFalse

你看,这个逻辑放在 User 类下面就很顺。因为它本来就是“和用户数据校验有关的一个工具”。

所以静态方法的本质,不是能力更强,而是分类更清晰。

十三、实例方法、类方法、静态方法,先从“第一个参数”来区分

这是一个很实用的入门区分法。

实例方法:

def 方法名(self, ...)

类方法:

@classmethoddef 方法名(cls, ...)

静态方法:

@staticmethoddef 方法名(...)

你会发现:

实例方法有 self类方法有 cls静态方法啥都没有

为什么?

因为它们依赖的信息层级不同。

实例方法要操作对象,所以要有 self。 类方法要操作类,所以要有 cls。 静态方法两边都不依赖,所以都不要。

这个角度一旦想通,三者的边界就会清晰很多。

十四、再从“它们能访问什么”来区分

这是第二个很实用的判断角度。

实例方法,通常可以访问对象属性,也能间接访问类属性。 类方法,通常可以访问类属性,但不能直接访问某个对象独有的数据。 静态方法,默认不自动拿到对象和类,所以它更像一个独立工具。

比如:

classStudent:    school = '第一中学'def__init__(self, name):        self.name = namedefmethod1(self):        print(self.name)        print(Student.school)    @classmethoddefmethod2(cls):        print(cls.school)    @staticmethoddefmethod3():        print('这是一个静态方法')

这里:

method1 能用对象姓名,也能用学校method2 更适合用学校这种类级别信息method3 只是执行自己那点逻辑

你不一定要立刻把所有边角都抠透,但这个大框架一定要立住。

十五、三者最直白的生活化类比

你可以把一个班级想成一个类。

实例方法像什么?

像“某个学生介绍自己”。 这显然得看具体是哪位学生,所以要用 self。

类方法像什么?

像“教务处统计这个班一共有多少人”。 这个事情属于整个班级层面,不是某个学生个人,所以用 cls。

静态方法像什么?

像“判断一个学号格式是否正确的规则”。 这个规则和班级主题有关,但既不依赖某个具体学生,也不依赖班级当前状态,所以它更像一个独立工具。

这样一类比,很多人就不再混了。

十六、三者怎么调用

这点也很重要。

实例方法通常通过对象调用:

stu1.introduce()

类方法通常通过类名调用更自然:

Student.show_count()

静态方法也常通过类名调用:

User.is_valid_phone('13800138000')

当然,Python 里有些情况下对象也能调类方法和静态方法,但在入门阶段,你最好先养成更清晰的习惯:

实例方法,优先对象调。 类方法,优先类名调。 静态方法,优先类名调。

这样代码表达会更稳。

十七、一个完整案例,把三者放一起看

下面这段代码建议你认真看一遍:

classStudent:    school = '第一中学'    count = 0def__init__(self, name, score):        self.name = name        self.score = score        Student.count += 1defintroduce(self):        print(f'我叫{self.name},来自{Student.school},成绩是{self.score}')    @classmethoddefshow_count(cls):        print(f'当前共有 {cls.count} 个学生对象')    @staticmethoddefis_valid_score(score):return0 <= score <= 100

调用:

stu1 = Student('张三', 95)stu2 = Student('李四', 88)stu1.introduce()stu2.introduce()Student.show_count()print(Student.is_valid_score(90))print(Student.is_valid_score(120))

输出结果:

我叫张三,来自第一中学,成绩是95我叫李四,来自第一中学,成绩是88当前共有 2 个学生对象TrueFalse

这段代码非常有代表性。

school 和 count 是类属性introduce() 是实例方法show_count() 是类方法is_valid_score() 是静态方法

如果这一段你能彻底看顺,这章主干就基本拿下了。

十八、什么时候该用类属性,什么时候别乱用

再强调一次,类属性很适合共享数据。 但不是所有数据都该写成类属性。

比如学生姓名、年龄、成绩,这些显然是每个对象都不同的,就应该是实例属性。 如果你把这些也写成类属性,所有对象看起来就容易共用同一份数据,那就会很奇怪。

所以一个很重要的判断是:

这份数据是“每个对象各不相同”,还是“整类对象共用一份”。

前者一般是实例属性。 后者才更适合类属性。

十九、什么时候该用类方法,什么时候别硬套

类方法也不是越多越好。

如果你的方法逻辑明显围绕某个对象在展开,比如展示姓名、修改余额、更新电量、判断某个学生是否及格,这些都更适合实例方法。

如果你的逻辑明显和整个类有关,比如统计总数、读取统一配置、修改共享规则,那才更适合类方法。

也就是说,不是加了 @classmethod 就显得高级。 关键还是看它到底服务谁。

二十、什么时候该用静态方法

静态方法最容易被误用。

你可以先用一个很稳的判断法:

如果一个函数逻辑上和这个类有关,但既不需要访问实例属性,也不需要访问类属性,那就可以考虑静态方法。

比如:

判断分数是否合法 判断手机号格式是否正确 把某个字符串做标准化处理 计算两个值的简单关系

这些都可能适合静态方法。

但如果这个函数和类压根没什么关系,那其实完全可以写成类外的普通函数,也没必要为了“显得懂面向对象”硬塞进类里。

二十一、很多新手为什么会混

因为表面上看,它们都写在类里。 可真正的区别不在“写在哪”,而在“依赖谁”。

实例方法依赖对象。 类方法依赖类。 静态方法两边都不依赖。

所以你以后别从语法表面去背,而是先问自己:

这个方法是服务某个对象的吗 还是服务整个类的 还是只是一个放在这里比较顺手的工具函数

一旦先想清这个,语法只是顺手一写。

二十二、给你一个最实用的判断口诀

你可以先用下面这个入门口诀,特别够用:

要用对象数据,用实例方法。 要用类共享数据,用类方法。 谁都不用,只是工具,用静态方法。

再补一句:

每个对象不同的数据,用实例属性。 整类对象共享的数据,用类属性。

这个口诀不是百分之百覆盖所有细节,但对你当前阶段已经非常够用了。

二十三、本章小练习

你可以自己做一个非常适合巩固的练习。

定义一个 Book 类,要求如下:

有一个类属性 category,值是 编程类图书有一个类属性 count,用于统计创建了多少本书__init__ 接收 title 和 price,并更新 count写一个实例方法 show_info(),输出书名、价格、类别 写一个类方法 show_count(),输出当前一共创建了多少本书 写一个静态方法 is_valid_price(price),判断价格是否大于 0

参考代码如下:

classBook:    category = '编程类图书'    count = 0def__init__(self, title, price):        self.title = title        self.price = price        Book.count += 1defshow_info(self):        print(f'书名:{self.title},价格:{self.price}元,类别:{Book.category}')    @classmethoddefshow_count(cls):        print(f'当前一共创建了 {cls.count} 本书')    @staticmethoddefis_valid_price(price):return price > 0

你把它补完整,再自己创建两个对象试一遍,这章会非常扎实。

二十四、本章总结

这一章,你要真正分清三件事。

类属性,是属于整个类的共享数据。 实例属性,是属于每个对象自己的独立数据。 类方法,用 @classmethod 定义,第一个参数通常是 cls,主要服务类本身。 实例方法,用 self,主要服务具体对象。 静态方法,用 @staticmethod 定义,既不依赖对象,也不依赖类,更像放在类里的工具函数。 判断它们的关键,不是背名字,而是先看它们到底依赖谁、服务谁。

到这里,面向对象的基础结构已经越来越完整了。 下一章我们继续往前走,进入一个非常重要、也非常有现实感的话题:077|封装:为什么说“隐藏细节”很重要。

最新文章

随机文章