飞翔飞翔
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
  • 学习路径
  • 第1章 Python简介

    • Python是什么
    • 安装与运行
    • 交互式解释器
    • 注释与编码规范
  • 第2章 变量与数据类型

    • 变量与对象
    • 整数 int
    • 浮点数 float
    • 复数 complex
    • 布尔值 bool
    • 字符串 str
    • 空值 None
    • 类型转换
  • 第3章 运算符与表达式

    • 算术运算符
    • 比较运算符
    • 赋值运算符
    • 逻辑运算符
    • 位运算符
    • 身份与成员运算符
    • 海象运算符
    • 运算符优先级
  • 第4章 流程控制

    • if 语句
    • if-else 语句
    • if-elif-else 语句
    • match-case 语句
    • 条件表达式(三元运算符)
    • while 循环
    • for 循环
    • range 函数
    • break 与 continue
    • 循环的 else 子句
    • pass 语句
  • 第5章 数据结构

    • 列表创建与索引
    • 列表方法
    • 列表推导式
    • 元组
    • 序列解包
    • 集合
    • 字典创建与访问
    • 字典方法
    • 字典推导式
    • range 对象
  • 第6章 函数

    • 定义函数
    • 位置参数与关键字参数
    • 默认参数
    • 可变参数
    • 解包实参
    • 函数返回值
    • lambda 表达式
    • 文档字符串与注解
    • 作用域与命名空间
    • global 与 nonlocal
  • 第7章 模块与包

    • 模块导入
    • 模块搜索路径
    • 包与相对导入
    • 标准库概览
  • 第8章 文件与输入输出

    • 文件读写
    • 上下文管理器
    • 字符串格式化
    • JSON 与 CSV
  • 第9章 面向对象

    • 类与对象
    • 方法
    • 实例变量与类变量
    • 私有变量
    • 继承
    • 多重继承
    • 魔术方法
    • 属性装饰器
    • 数据类 dataclass
  • 第10章 异常处理

    • 语法错误与异常
    • try-except
    • 异常链与 raise
    • 清理操作
    • 自定义异常
  • 第11章 迭代器与生成器

    • 迭代器协议
    • 生成器
    • 生成器表达式
    • 迭代工具
  • 第12章 高级特性

    • 装饰器
    • 函数式编程
  • 第13章 工程实践

    • 测试与调试
    • 代码质量
    • 虚拟环境

类与对象

Python 的 class 语句并非声明,而是一条可执行语句。与 def 一样,类定义必须先执行才能生效,因此完全可以把类定义放在 if 分支或函数内部。

类定义的执行过程

当解释器执行到 class 语句时,会创建一个新的命名空间作为局部作用域,类体中的赋值语句和函数定义都发生在这个命名空间内。当类体执行完毕,解释器用这个命名空间创建一个类对象,并在当前作用域中将类名绑定到该类对象。

# 类定义放在 if 分支中
if True:
    class Point:
        """二维点"""
        dimension = 2
        
        def __init__(self, x, y):
            self.x = x
            self.y = y

# 类定义放在函数内部
def make_counter():
    class Counter:
        start = 0
    return Counter

C = make_counter()
print(C.start)  # 0

类对象创建后,它具有两个核心操作:属性引用和实例化。

属性引用与类属性

类对象的属性引用使用标准语法 ClassName.attr。类被创建时,类命名空间中存在的所有名称都成为类属性。

class MyClass:
    """文档字符串也是类属性"""
    i = 12345
    
    def f(self):
        return 'hello world'

# 属性引用
print(MyClass.i)           # 12345
print(MyClass.f)           # <function MyClass.f at ...>
print(MyClass.__doc__)     # 文档字符串也是类属性

# 类属性可以被重新赋值
MyClass.i = 99999
print(MyClass.i)           # 99999

类属性通常用于存放所有实例共享的数据或默认配置。如果类属性是可变对象(如列表、字典),所有实例看到的是同一个对象,这一点在后续章节会详细讨论。

实例化与 init

实例化使用函数调用语法 ClassName()。这会先创建一个空对象,如果类定义了 __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

__init__ 方法可以有任意数量的参数,但第一个参数必须是实例本身。提供给类实例化运算符的参数会被原样传递给 __init__。如果忘记提供必需的参数,调用时会抛出 TypeError:

# y = Complex()  # TypeError: Complex.__init__() missing 2 required arguments

__init__ 不返回任何值(隐式返回 None),试图显式返回非 None 值会引发 TypeError。

self 参数的本质

方法的第一个参数通常命名为 self,但这纯粹是约定,Python 并未将其作为保留字。该参数代表方法被调用时所关联的实例对象,由调用机制隐式传入。

class Bag:
    def __init__(self):
        self.data = []
    
    def add(self, x):
        self.data.append(x)
    
    def addtwice(self, x):
        self.add(x)
        self.add(x)

bag = Bag()
bag.add('apple')       # 等价于 Bag.add(bag, 'apple')
bag.addtwice('pear')   # 内部通过 self.add() 调用另一个方法
print(bag.data)        # ['apple', 'pear', 'pear']

在方法内部引用数据属性时,必须通过 self.attr 访问。这虽然略显冗长,但消除了局部变量与实例变量混淆的可能,显著提升了代码可读性。

类属性与实例属性

实例属性不需要预先声明,在首次赋值时即产生。每个实例拥有独立的实例属性命名空间。

class Warehouse:
    purpose = 'storage'
    region = 'west'

w1 = Warehouse()
w2 = Warehouse()

# 读取类属性
print(w1.purpose, w1.region)   # storage west

# 给实例赋值同名属性,会遮蔽类属性
w2.region = 'east'
print(w2.purpose, w2.region)   # storage east
print(w1.region)               # west(w1 仍读取类属性)

# 删除实例属性后,重新暴露类属性
del w2.region
print(w2.region)               # west

属性查找遵循实例优先原则:当通过实例访问属性时,先在实例的命名空间中查找,找不到再去类的命名空间中查找。因此,给实例的类属性同名变量赋值,并不会修改类属性,而是在实例中创建了一个同名的新属性。

客户端代码甚至可以向实例动态添加类定义中不存在的数据属性,这不会影响方法的可用性,只要避免名称冲突即可:

w1.capacity = 1000   # 动态添加新属性
print(w1.capacity)   # 1000

这种动态特性是 Python 的核心特征之一,但也意味着类无法强制封装数据属性——数据保护完全依赖命名约定和编程规范。

动态修改类

类对象在创建后仍可被修改:可以新增、修改或删除类属性,这些变更会立即反映到所有实例上(只要实例没有同名属性遮蔽)。

class Employee:
    pass

e = Employee()
Employee.company = 'TechCorp'   # 运行时添加类属性
print(e.company)                # TechCorp

Employee.greet = lambda self: f"Hi, I'm {self.name}"   # 运行时添加方法
e.name = 'Alice'
print(e.greet())                # Hi, I'm Alice

这种能力使得 Python 的类具有极高的灵活性,但也要求开发者对命名空间的管理保持清醒。

下一页
方法