简介:本文系统梳理Python中类与对象的核心概念,从“一切皆对象”的设计哲学出发,讲清属性与方法的含义、类与实例的关系、__init__初始化机制以及self参数的本质,并配以可运行的示例代码,帮助初学者建立清晰的对象模型思维,避开常见认知误区。
Python有一条广为流传的设计理念:一切皆对象。数字、字符串、列表、函数、模块,甚至类本身,都是对象。理解这句话,是理解类与对象的第一步。
放眼日常生活,我们身边其实充满了“对象”:正在使用的电脑是对象,坐着的椅子是对象,家里养的小狗也是对象。面向对象编程(OOP)正是把这种“以事物为中心”的思维方式引入程序设计:程序不再仅仅是一串按顺序执行的指令,而是由若干相互作用的对象协同构成的系统。
如何完整地描述一个对象?通常从两个方面入手:
以一只小狗为例:
在Python中,这两个维度有明确的术语对应:
由此可以得出一个简洁的结论:
对象 = 属性(数据) + 方法(操作数据的行为)
这个等式背后隐藏着面向对象的核心思想,封装:把数据与操作数据的逻辑绑在一起,形成一个内聚的整体,外部只需关心对象“能做什么”,而不必纠缠其内部细节。
现实中有千千万万只狗,它们颜色不同、年龄各异,但都遵循“狗”这一类生物的共同结构。程序设计中也是如此:把具有相同属性结构和相同方法的对象归为一类,这就是类(class)的概念,例如“人类”“动物类”“图形类”等。
类与对象的关系可以概括为:
一个形象的类比:类如同建筑图纸,对象如同按图纸盖出的房子。图纸只有一份,房子却可以盖很多栋,而且每栋房子的内部陈设(属性值)可以各不相同。
class People:def speak(self):print("hello!")p1 = People() # 创建第一个实例p2 = People() # 创建第二个实例p1.speak() # 输出: hello!
注意两点细节:
p1 = People() 这种“类名加括号”的写法,就是调用类来创建实例的过程,通常称为实例化。上面的类只定义了方法,没有定义属性,这并不是疏漏。在Python中,属性可以动态绑定到实例上,每个实例可以拥有各自不同的属性值:
p1.age = 39 # 为p1添加age属性p1.height = 190 # 为p1添加height属性print(p1.age) # 39print(p1.height) # 190p2.age # AttributeError:p2并没有age属性
这正是“对象 = 属性 + 方法”的体现:方法由类统一提供,属性则由各实例自行持有。这种灵活性是Python动态特性的典型表现,但也意味着如果缺乏约束,不同实例的属性可能参差不齐,这就引出了初始化机制。
Python允许在类中定义一个名为 __init__ 的特殊方法。只要创建该类的实例,解释器就会自动调用它,因此适合用来完成属性的统一初始化:
class Peo:def __init__(self, name, age, sex):self.Name = nameself.Age = ageself.Sex = sexdef speak(self):print("my name is " + self.Name)p = Peo("Thor", 39, "male")p.speak() # 输出: my name is Thor
执行 p = Peo("Thor", 39, "male") 时,背后发生了三件事:
__init__,把 “Thor”、39、”male” 依次传给 name、age、sex 参数;__init__ 内部通过 self.Name = name 等赋值语句,把数据绑定到该实例上。有了 __init__,每个对象在诞生之时就被强制拥有统一的属性集合,避免出现“有的实例有age、有的没有”的不一致问题。这是工程实践中公认的良好做法。
self是初学者最容易困惑的概念。理解它的关键在于:方法被调用时,实例自身会被自动作为第一个参数传入。
p.speak() 在解释器内部近似等价于 Peo.speak(p);self.xxx 读写的属性,都属于这个具体实例,而不是类。换言之,self是“实例”在方法内部的代称。正因如此,同一个speak方法被不同实例调用时,能各自打印出自己的名字,方法只有一份,数据各归各主。这也是原文反复强调“属性属于对象而不属于类”的深层原因。
__init__ 中绑定,也可以在任意时刻动态添加;但为了可读性与可维护性,应统一在 __init__ 中完成初始化。self.xxx = ... 绑定的是实例属性,各实例相互独立。二者的查找顺序不同,混用容易引发隐蔽缺陷。可以用三句话收束全文:
__init__ 保证对象以一致的状态诞生,self 让方法能够区分“这是谁的数据”。掌握这些概念后,再回头看最初那个只含一个speak方法的小类,就能清晰地解释它的每个组成部分:类如何定义行为、实例如何携带数据、方法调用时self如何完成绑定。这种从概念到代码的闭环理解,正是迈向熟练运用面向对象编程的关键一步。