Python 并不是一门必须重度依赖面向对象的语言,但只要你开始接触稍微复杂一点的业务代码、框架代码或第三方库,就一定会和类、实例、属性、方法这些概念打交道。
这一节我们不会把面向对象讲成抽象口号,而是从最直接的代码视角出发,帮助你理解类到底解决了什么问题、实例和属性如何配合,以及为什么方法本质上只是“绑定在对象上的行为”。
为什么要学习面向对象?
很多人刚接触面向对象时,最常见的困惑是:明明函数已经能组织代码了,为什么还需要类?
答案不是“所有代码都该写成类”,而是类适合解决另一类问题。函数擅长封装一个动作,而类更适合描述“带状态的对象”。
比如一个购物车,它不只是“执行一次计算”,而是长期拥有:
- 商品列表;
- 增加商品的行为;
- 移除商品的行为;
- 计算总价的行为。
这时候,如果你把这些状态和行为拆成很多彼此分离的函数,也不是不能做,但边界会越来越松散。类的价值就在于:把一组相关状态和围绕它的行为收拢到一起。
所以,学习面向对象不是为了追求术语,而是为了掌握一种“以对象为中心组织代码”的方式。
如何定义类与创建实例?
Python 中定义类的方式很直接:
class User:
def __init__(self, name, age):
self.name = name
self.age = age类本身更像是一个“蓝图”或“说明书”,而实例则是根据这个蓝图创建出来的具体对象:
user = User("Colin", 28)这里的 user 就是实例。它拥有自己独立的 name 和 age。
__init__ 的作用,是在实例创建后完成初始化。很多初学者会把它误以为是“构造函数本体”,但更准确地说,它是实例创建后的初始化入口。
这个区别现在不用过度纠结,但要先建立一个基本认知:类定义的是结构和行为,实例才是真正参与运行的数据对象。
实例属性与类属性
面向对象里一个特别容易混淆的点,是实例属性和类属性。
先看实例属性:
class User:
def __init__(self, name):
self.name = name这里的 self.name 属于每个实例自己。你创建两个对象,它们各自会保存自己的值。
而类属性则定义在类体内:
class User:
category = "member"
def __init__(self, name):
self.name = namecategory 属于类本身,所有实例默认都可以访问到它。
它们的差别可以简单理解为:
- 实例属性描述“这个对象自己的状态”;
- 类属性描述“这类对象共同拥有的信息”。
如果把两者混用,就很容易出现奇怪问题。尤其当类属性是可变对象时,多个实例可能会共享同一份数据,行为就会和你预期的不一样。
所以经验上:
- 用户名、余额、购物车内容这类变化中的数据,通常应放实例属性;
- 常量、配置项、统计值这类类级别信息,才更适合放类属性。
实例方法、类方法与静态方法
类中最常见的方法,是实例方法:
class User:
def __init__(self, name):
self.name = name
def greet(self):
return f"Hello, {self.name}"实例方法的第一个参数通常写作 self,表示当前实例本身。调用时:
user = User("Colin")
print(user.greet())除此之外,还有类方法和静态方法。
类方法用 @classmethod 标记,第一个参数通常写作 cls:
class User:
def __init__(self, name):
self.name = name
@classmethod
def from_dict(cls, data):
return cls(data["name"])它适合做备用构造器、类级配置读取等与“类本身”相关的操作。
静态方法则用 @staticmethod 标记:
class User:
@staticmethod
def is_valid_name(name):
return len(name.strip()) > 0它和类、实例都没有直接绑定关系,只是因为语义上“属于这个类相关的工具逻辑”,所以放进类里。
self 到底指向什么?
很多初学者会把 self 当成 Python 的特殊关键字,其实它只是一个约定俗成的参数名。
真正重要的是它代表的含义:当前正在调用这个方法的实例对象。
user1 = User("Colin")
user2 = User("Alice")当你调用 user1.greet() 时,self 指向 user1;调用 user2.greet() 时,self 指向 user2。
所以,方法本质上并不神秘。你完全可以把它理解成:定义在类里的函数,只不过调用时会自动把实例传进去。
一旦这一点想通,很多 OOP 语法都会变得自然很多。
封装的第一步:把数据和行为放在一起
面向对象里经常会讲“封装”,但它很容易被说得过于抽象。
对初学阶段来说,最有价值的理解其实很简单:让和某个对象强相关的数据与行为尽量放在一起。
比如订单对象:
- 它有订单金额;
- 它有订单状态;
- 它可以支付;
- 它可以取消;
- 它可以计算应付金额。
如果这些逻辑到处散落在不同函数里,阅读和维护都会越来越困难。相反,把它们收拢到一个类中,至少能让系统的边界更清楚。
当然,封装不意味着什么都往类里塞。真正好的封装,是只把“确实围绕这个对象展开”的状态和行为放进去,而不是把不相干的工具逻辑硬塞进类里。
所以,面向对象的第一步,不是背“封装、继承、多态”八股,而是问清楚:这组数据和行为,是不是天然属于同一个对象。
总结与预告
这一节我们没有把面向对象停留在抽象概念上,而是从代码角度理解了类、实例、属性和方法的关系。只要把这几个核心部件的职责分清楚,后面读框架源码和设计业务对象都会轻松很多。
下一节我们会继续深入复用与设计层面,讨论继承、组合、多态以及 super() 的真正作用。