Python 导读
本文记录 Python 的基本概念。
快速开始¶
如果是第一次学习 Python,可以按照下面的顺序阅读:
- 先阅读 工程实践,完成解释器、虚拟环境和依赖管理配置。
- 再阅读 语法基础,学习数据类型、流程控制、函数、类、异常、迭代器和生成器。
- 然后阅读 常用标准库,掌握路径、日志、拷贝、命令行参数等基础能力。
- 如果对开发感兴趣,学完 Python 基础后,可以移步 软件开发的 Python 专栏 作进一步阅读。
机制¶
Python 的运行机制主要包括源码执行、对象模型、导入机制、内存管理和并发约束等内容。
解释器执行¶
Python 是一门解释型语言。以 CPython 为例,解释器会先把 .py 源码编译为字节码对象 (code object),然后由 C 逐条执行字节码指令。.pyc 是字节码缓存文件,用于加快后续加载。
根据场景的不同,字节码执行方式也不同,目前主流的有以下几种实现:
| 实现 | 开发语言 | 字节码执行方式 | 特点 | 典型应用场景 |
|---|---|---|---|---|
| CPython | C | 逐条解释 | 官方标准实现,生态最全,速度中等 | 默认实现、科研计算、Web 后端等 |
| PyPy | RPython | 逐条解释,但会在运行时将热点字节码即时 (Just In Time, JIT) 编译为机器码,直接在 CPU 上执行 | 执行速度快,适合长时间运行的计算任务;但兼容性稍差 | 高性能计算、高并发等 |
| Jython | Java | 将 Python 源码直接编译成 Java 字节码,然后由 JVM(Java 虚拟机)执行 | 能与 Java 无缝集成;但性能依赖 JVM 优化,启动速度稍慢 | 需要在 Java 环境中使用 Python 脚本 |
变量模型¶
在 C++ 和 Python 中,赋值语句的语义是完全不同的。C++ 变量像「盒子」,赋值就是再拿一个盒子装一份拷贝的数据;而 Python 变量像「标签」,赋值就是多贴几个标签在同一个盒子上。下图生动的展示了 Python 变量的意义:
C++:赋值会产生拷贝。给变量赋值时,会重新申请内存空间,把数据复制过去。
例如下面的程序。输出的内存地址不同,说明 a 和 b 是两份独立的数据。:
#include <iostream>
#include <vector>
int main() {
std::vector<int> a = {1, 2, 3};
std::vector<int> b = a;
std::cout << &a << std::endl; // 0x8b5a3ffa40
std::cout << &b << std::endl; // 0x8b5a3ffa20
return 0;
}
Python:赋值仅仅是增加引用。所有变量其实都是「标签」,指向同一块数据。
例如下面的程序。三个变量的内存地址完全一样,说明它们指向同一个列表对象:
a = [1, 2, 3]
b = a
c = a
print(id(a)) # 1542586187072
print(id(b)) # 1542586187072
print(id(c)) # 1542586187072
如果修改其中一个变量指向的可变对象,其他变量也会观察到同一份对象的变化:
Python 如果确实需要拷贝变量,需要使用 copy 标准库。
内存管理¶
CPython 主要通过引用计数回收对象:当对象的引用计数归零时,对象占用的内存会被释放。对于循环引用,解释器会配合垃圾回收器 (Garbage Collection, GC) 定期检测和清理。开发者通常不需要手动释放普通对象,但需要主动关闭文件、网络连接等外部资源。
全局解释器锁¶
CPython 有全局解释器锁 (Global Interpreter Lock, GIL),同一解释器进程内通常只有一个线程能执行 Python 字节码。它简化了 CPython 的内存管理,但也限制了多线程在 CPU 密集任务上的并行能力。I/O 密集任务可以使用多线程或异步编程;CPU 密集任务通常更适合多进程、原生扩展或其他解释器实现。
