2025-08-21 Python进阶5——类和对象
文章目录
- 1 名称和对象
- 2 作用域和命名空间
- 2.1 基本概念
- 2.2 作用域层次
- 2.3 `global` 和 `nonlocal` 关键字
- 3 类的基本概念
- 3.1 类定义语法
- 3.2 类对象
- 3.3 `__init__` 方法
- 3.4 实例对象
- 3.5 方法的特殊之处
- 4 类变量和实例变量
- 5 继承
- 5.1 基本语法
- 5.2 方法重写
- 5.3 调用基类方法
- 5.4 继承相关的内置函数
- 5.5 多重继承
- 6 私有变量
- 7 杂项说明
- 7.1 简单数据类
- 7.2 方法对象的属性
- 7.3 专有方法与运算符重载
- 8 总结
1 名称和对象
在深入了解类之前,需要理解 Python 中名称和对象的关系:
- 对象:是 Python 中所有数据的基本单元,具有类型和值
- 名称:是绑定到对象的标识符,可以通过名称访问对象
- 别名:多个名称可以绑定到同一个对象,这种现象称为别名
示例:
a = [1, 2, 3]
b = a # b 是 a 的别名,指向同一个列表对象
b.append(4)
print(a) # 输出: [1, 2, 3, 4],因为 a 和 b 指向同一个对象
别名对于不可变对象(如数字、字符串、元组)影响不大,但对于可变对象(如列表、字典、自定义对象)可能产生意外效果,需要特别注意。
2 作用域和命名空间
理解作用域和命名空间是掌握类机制的基础。
2.1 基本概念
- 命名空间(namespace):是从名称到对象的映射,用于避免名称冲突。常见的命名空间包括:
- 内置名称命名空间(包含
abs()
等内置函数) - 模块的全局命名空间
- 函数的局部命名空间
- 对象的属性集合
- 内置名称命名空间(包含
- 作用域:是代码中可以直接访问某个命名空间的区域。
2.2 作用域层次
执行期间,有 3 或 4 个嵌套的作用域可直接访问:
- 最内层:当前函数的局部作用域
- 外层:闭包函数的作用域(非局部、非全局)
- 倒数第二层:当前模块的全局作用域
- 最外层:内置名称的命名空间
2.3 global
和 nonlocal
关键字
global
:声明变量在全局作用域中nonlocal
:声明变量在外层作用域中(非全局)
示例:
def scope_test():def do_local():spam = "local spam" # 局部变量def do_nonlocal():nonlocal spam # 声明为外层作用域变量spam = "nonlocal spam"def do_global():global spam # 声明为全局变量spam = "global spam"spam = "test spam"do_local()print("After local assignment:", spam) # 输出: test spamdo_nonlocal()print("After nonlocal assignment:", spam) # 输出: nonlocal spamdo_global()print("After global assignment:", spam) # 输出: nonlocal spamscope_test()
print("In global scope:", spam) # 输出: global spam
3 类的基本概念
3.1 类定义语法
class ClassName:<语句-1>...<语句-N>
类定义在执行时创建一个类对象,类中的语句通常是函数定义(方法)。
示例:
class MyClass:"""一个简单的示例类"""i = 12345 # 类变量def f(self): # 方法return 'hello world'
3.2 类对象
类对象支持两种操作:属性引用和实例化。
-
属性引用:使用
ClassName.attribute
语法print(MyClass.i) # 访问类变量 print(MyClass.f) # 访问方法 print(MyClass.__doc__) # 访问文档字符串
-
实例化:使用函数表示法创建类的新实例
x = MyClass() # 创建 MyClass 的实例
3.3 __init__
方法
__init__
方法是一个特殊方法,在实例化时自动调用,用于初始化新创建的对象。
class Complex:def __init__(self, realpart, imagpart):self.r = realpart # 实例变量self.i = imagpart # 实例变量x = Complex(3.0, -4.5)
print(x.r, x.i) # 输出: 3.0 -4.5
3.4 实例对象
实例对象主要用于属性引用,有两种类型的属性:
-
数据属性:相当于实例变量,首次赋值时创建
x = MyClass() x.counter = 1 # 创建数据属性 while x.counter < 10:x.counter *= 2 print(x.counter) # 输出: 16 del x.counter # 删除数据属性
-
方法:是绑定到实例的函数
x = MyClass() print(x.f()) # 调用方法,输出: hello world# 方法对象可以保存后调用 xf = x.f print(xf()) # 输出: hello world
3.5 方法的特殊之处
方法与普通函数的区别在于:调用方法时,实例对象会作为第一个参数自动传入。
# 调用 x.f() 相当于调用 MyClass.f(x)
x = MyClass()
print(x.f()) # 等价于 MyClass.f(x)
按照约定,方法的第一个参数通常命名为 self
,代表实例对象本身。
4 类变量和实例变量
- 类变量:属于类本身,被所有实例共享
- 实例变量:属于每个实例,每个实例有自己的副本
示例:
class Dog:kind = 'canine' # 类变量,所有实例共享def __init__(self, name):self.name = name # 实例变量,每个实例独有self.tricks = [] # 实例变量,每个实例有自己的列表def add_trick(self, trick):self.tricks.append(trick)# 创建两个实例
d = Dog('Fido')
e = Dog('Buddy')# 类变量被共享
print(d.kind) # 输出: canine
print(e.kind) # 输出: canine# 实例变量各自独有
print(d.name) # 输出: Fido
print(e.name) # 输出: Buddy# 为每个实例添加不同的技能
d.add_trick('roll over')
e.add_trick('play dead')
print(d.tricks) # 输出: ['roll over']
print(e.tricks) # 输出: ['play dead']
注意:避免使用可变对象(如列表、字典)作为类变量,因为所有实例会共享它们,可能导致意外结果。
5 继承
继承允许创建新类(派生类),从现有类(基类)继承属性和方法,从而实现代码复用和扩展。
5.1 基本语法
class DerivedClassName(BaseClassName):<语句-1>...<语句-N>
如果基类在另一个模块中:
class DerivedClassName(modname.BaseClassName):...
5.2 方法重写
派生类可以重写基类的方法,即定义与基类同名的方法。
class Animal:def speak(self):return "Some sound"class Dog(Animal):def speak(self): # 重写 speak 方法return "Woof!"class Cat(Animal):def speak(self): # 重写 speak 方法return "Meow"d = Dog()
print(d.speak()) # 输出: Woof!c = Cat()
print(c.speak()) # 输出: Meow# 用子类对象调用父类已被覆盖的方法
print(super(Cat, c).speak()) # 输出: Some sound
5.3 调用基类方法
在派生类中,可以使用 BaseClassName.methodname(self, arguments)
调用基类方法。
class Base:def __init__(self, x):self.x = xclass Derived(Base):def __init__(self, x, y):Base.__init__(self, x) # 调用基类的 __init__ 方法self.y = yd = Derived(3, 5)
print(d.x, d.y) # 输出: 3 5
5.4 继承相关的内置函数
isinstance(obj, class)
:检查对象是否是类的实例(包括派生类)issubclass(sub, sup)
:检查子类与父类的关系
print(isinstance(Dog(), Animal)) # 输出: True
print(issubclass(Dog, Animal)) # 输出: True
5.5 多重继承
Python 支持多重继承,一个类可以继承多个基类:
class Base1:def method1(self):return "Method from Base1"class Base2:def method2(self):return "Method from Base2"class Derived(Base1, Base2):pass # 继承 Base1 和 Base2 的所有方法d = Derived()
print(d.method1()) # 输出: Method from Base1
print(d.method2()) # 输出: Method from Base2
方法解析顺序:当多个基类有同名方法时,Python 使用深度优先、从左到右的规则查找方法,也称为方法解析顺序(MRO)。可以通过 __mro__
属性查看:
print(Derived.__mro__)
# 输出类似: (<class '__main__.Derived'>, <class '__main__.Base1'>, <class '__main__.Base2'>, <class 'object'>)
6 私有变量
Python 没有真正的私有变量,但通过名称改写机制提供有限的支持:
- 形式为
__spam
的变量(至少两个前导下划线,至多一个尾随下划线)会被改写为_classname__spam
- 这种机制主要用于避免子类中的名称冲突
示例:
class Mapping:def __init__(self, iterable):self.items_list = []self.__update(iterable) # 实际会被改写为 _Mapping__updatedef update(self, iterable):for item in iterable:self.items_list.append(item)__update = update # 私有副本,实际为 _Mapping__updateclass MappingSubclass(Mapping):def update(self, keys, values): # 不会与基类的 __update 冲突for item in zip(keys, values):self.items_list.append(item)
注意:名称改写只是一种约定,并非真正的访问限制,仍然可以通过 _classname__spam
访问 “私有” 变量。
7 杂项说明
7.1 简单数据类
对于仅需要存储数据的类,可以使用 dataclasses
模块简化定义:
from dataclasses import dataclass@dataclass
class Employee:name: strdept: strsalary: intjohn = Employee('john', 'computer lab', 1000)
print(john.dept) # 输出: computer lab
print(john.salary) # 输出: 1000
7.2 方法对象的属性
方法对象有两个有用的属性:
__self__
:方法所属的实例__func__
:方法对应的函数对象
class MyClass:def my_method(self):passx = MyClass()
m = x.my_method
print(m.__self__ is x) # 输出: True
print(m.__func__ is MyClass.my_method) # 输出: True
7.3 专有方法与运算符重载
__init__
:构造函数,在生成对象时调用__del__
:析构函数,释放对象时使用__repr__
:打印,转换__setitem__
:按照索引赋值__getitem__
:按照索引获取值__len__
:获得长度__cmp__
:比较运算__call__
:函数调用__add__
:加运算__sub__
:减运算__mul__
:乘运算__truediv__
:除运算__mod__
: 求余运算__pow__
:乘方
Python同样支持运算符重载,我们可以对类的专有方法进行重载,实例如下:
class Vector:def __init__(self, a, b):self.a = aself.b = b# 定义字符串表示def __str__(self):return f'Vector ({self.a}, {self.b})'# 重载加法运算符def __add__(self, other):return Vector(self.a + other.a, self.b + other.b)# 使用重载的加法运算符
v1 = Vector(2, 10)
v2 = Vector(5, -2)
print(v1 + v2) # 输出:Vector (7, 8)
8 总结
- 类是封装数据和方法的重要机制,支持面向对象编程
- 类变量被所有实例共享,实例变量为每个实例独有
- 继承允许创建派生类,实现代码复用和扩展
- 迭代器和生成器提供了便捷的遍历机制
- Python 的类机制灵活,支持多重继承、动态修改等高级特性