Python 类与对象
Python 支持面向对象编程(OOP):把数据与操作数据的行为封装在一起,便于复用、扩展与维护。OOP 中常见概念包括封装、继承、多态等;在 Python 里,类(class) 与 对象(object,即实例) 是最基础的载体。
本篇建立整体认识;self、构造函数、继承等可继续阅读 self 变量、类构造函数、类的继承。
类是什么?
类是创建对象的蓝图:描述这类对象有哪些数据(属性)、有哪些行为(方法)。
例如 Employee 可以有属性 employee_id、方法 work();根据同一个类可以创建多个员工对象,各自 employee_id 可以不同。
方法若需访问当前实例的数据,第一个参数习惯为 self(见 self 变量)。
定义类:class
使用关键字 class 定义类。空类可用 pass 占位:
class Employee:
pass为类添加类属性与实例方法:
class Employee:
employee_id = 0 # 类属性(本节稍后对比「实例属性」)
def work(self):
print(f"{self.employee_id} is working")
emp = Employee()
print(type(emp))
emp.work()输出:
<class '__main__.Employee'>
0 is working此处实例上未单独赋值 employee_id 时,读取 self.employee_id 会找到类属性 0(所有实例默认共享该展示值,直到在实例上绑定自己的属性)。
内置函数 type(obj) 返回对象类型,可用于调试。
对象(实例)
对象是类实例化的结果。一个类可以有任意多个对象;对象可调用类上定义的方法,并拥有自己的属性命名空间。
上例中 emp 即 Employee 的实例。实例通过 类名(...) 调用创建,背后会经历分配对象与初始化等步骤(见下文 __init__)。
默认与基类 object
在 Python 3 中,类声明若未写父类,则隐式继承内置类 object。未自定义 __init__ 时,仍会使用继承链上的默认机制完成实例创建;需要在创建后设置实例属性时,通常定义 __init__(self, ...)(常被称为初始化方法,习惯上也叫构造函数)。详见 类构造函数。
使用 __init__ 设置实例属性
若希望每个员工有各自的 employee_id,应在 __init__ 里绑定到 self:
class Employee:
def __init__(self, i):
self.employee_id = i
def work(self):
print(f"{self.employee_id} is working")
emp = Employee(100)
emp.work()输出:
100 is working此时不能再无参调用 Employee(),否则会报错:
TypeError: __init__() missing 1 required positional argument: 'i'能否定义多个 __init__?
Python 不像部分语言那样支持「构造函数重载」。若在类里写两个 __init__,后定义的会覆盖先定义的,只保留最后一个:
class Employee:
def __init__(self, id):
self.employee_id = id
def __init__(self, id, n): # 覆盖上一个 __init__
self.employee_id = id
self.emp_name = n
def work(self):
print(f"{self.emp_name}[{self.employee_id}] is working")
emp = Employee(100, "Pankaj")
emp.work()
# emp = Employee(100) # TypeError: 缺少参数 n需要多种构造方式时,常用 @classmethod 备选构造器、默认参数、*args / **kwargs 等模式,而不是重复定义 __init__。
类变量与实例变量
| 类变量 | 实例变量 | |
|---|---|---|
| 定义位置 | 类体中、方法外 | 通常在 __init__ 里赋给 self.xxx |
| 共享方式 | 被所有实例共享同一绑定(注意可变对象) | 每个实例各自一份 |
| 访问方式 | ClassName.var 或 self.var(查找顺序有关) | self.var |
employee_id 若随员工变化,应作为实例变量在 __init__ 中设置。
下面用类变量统计创建了多少个实例,并用类变量列表记录已分配的 id。向共享列表追加时,建议显式用类名,避免读者误以为是「每个实例独有列表」:
class Employee:
count = 0
ids_list = []
def __init__(self, i):
self.id = i
Employee.count += 1
Employee.ids_list.append(i)
for x in range(10):
Employee(x)
print(f"已创建员工数 = {Employee.count}")
print(f"已分配 id 列表 = {Employee.ids_list}")
emp = Employee(1000)
print(f"通过实例访问同一列表 = {emp.ids_list}")输出(示意):
已创建员工数 = 10
已分配 id 列表 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
通过实例访问同一列表 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1000]注意:ids_list 是可变的类属性,所有实例看到的是同一个列表。若本意是「每个实例自己的列表」,应在 __init__ 里写 self.ids_list = [],不要与共享类变量混淆。
小结
- 类描述结构与行为;对象是类的实例。
__init__用于初始化实例属性;不要重复定义多个__init__。- 类变量共享、实例变量 per 实例;可变类变量要特别小心共享引用。