python基础
Python2和Python3的区别
Python2 与 Python3 是 Python 语言的两个主要版本,Python2 已于 2020 年停止官方维护,目前主流开发均使用 Python3。以下是核心区别:
1. 语法差异
- print 语句:
- Python2:
print "Hello"(语句) - Python3:
print("Hello")(函数)
- Python2:
- 整数除法:
- Python2:
3 / 2结果为1(向下取整) - Python3:
3 / 2结果为1.5(浮点数),3 // 2才是1
- Python2:
- Unicode 字符串:
- Python2:默认 ASCII 编码,需显式声明
u"中文"为 Unicode - Python3:默认 Unicode 编码,
"中文"直接是 Unicode 字符串
- Python2:默认 ASCII 编码,需显式声明
2. 库与内置函数变化
- 库重命名 / 合并:如
urllib2合并为urllib.request,ConfigParser改为configparser - range 函数:
- Python2:
range()返回列表,xrange()返回迭代器(更省内存) - Python3:
range()直接返回迭代器,无xrange()
- Python2:
3. 异常处理
- Python2:
except Exception, e - Python3:
except Exception as e(更清晰的语法)
4. 其他特性
- Python3 新增
nonlocal关键字,支持嵌套函数修改外层变量 - Python3 对类型提示(Type Hints)支持更完善
- Python3 性能整体优于 Python2(如字典、列表操作优化)
with语句和上下文管理器
一、with 语句:简化资源管理的语法糖
with 语句是 Python 用于自动管理资源的语法,核心作用是替代 try-finally 块,确保资源(如文件、数据库连接、锁)在使用后被正确释放,避免资源泄漏。
基本语法
with 上下文管理器 as 变量:
# 使用资源的代码块
# 离开 with 块后,资源自动释放
常见例子:文件操作
# 传统写法(需手动关闭)
f = open('test.txt', 'r')
try:
content = f.read()
finally:
f.close()
# with 写法(自动关闭)
with open('test.txt', 'r') as f:
content = f.read()
二、上下文管理器:实现 __enter__ 和 __exit__ 的对象
上下文管理器是实现了两个特殊方法的 Python 对象,with 语句的底层逻辑由这两个方法驱动:
表格
| 方法 | 作用 |
|---|---|
__enter__() |
进入 with 块前执行,返回值会赋值给 as 后的变量(可选)。 |
__exit__(exc_type, exc_val, exc_tb) |
离开 with 块时执行(无论是否发生异常),用于资源清理。参数接收异常信息(无异常时均为 None)。 |
自定义上下文管理器示例
模拟一个简单的 “资源获取 - 释放” 过程:
class MyResource:
def __enter__(self):
print("获取资源")
return "资源对象" # 返回给 as 后的变量
def __exit__(self, exc_type, exc_val, exc_tb):
print("释放资源")
# 可处理异常:返回 True 表示异常已处理,不向外抛出
if exc_type:
print(f"捕获异常:{exc_val}")
return True
# 使用
with MyResource() as res:
print(f"使用 {res}")
# 故意触发异常
raise ValueError("出错了")
三、更简单的方式:contextlib 模块
对于简单的上下文管理器,无需定义类,可使用 contextlib.contextmanager 装饰器,通过生成器函数实现:
from contextlib import contextmanager
@contextmanager
def my_resource():
# __enter__ 的逻辑(yield 之前)
print("获取资源")
res = "资源对象"
try:
yield res # 返回给 as 后的变量
finally:
# __exit__ 的逻辑(yield 之后)
print("释放资源")
# 使用
with my_resource() as res:
print(f"使用 {res}")
总结
with语句是语法糖,让资源管理代码更简洁、安全;- 上下文管理器的核心是
__enter__(获取资源)和__exit__(释放资源); - 简单场景推荐用
contextlib.contextmanager快速实现。
GIL全局解释器锁是什么?
一、GIL 是什么?(定义)
GIL(Global Interpreter Lock,全局解释器锁) 是 CPython 解释器(Python 最主流的实现)中的一把互斥锁,它的核心规则是:同一时刻,只有一个线程能在 CPU 上执行 Python 字节码。
注意:GIL 是 CPython 的特性,Jython、IronPython 等其他 Python 解释器没有 GIL;PyPy 虽有 GIL,但通过 JIT 编译大幅优化了性能。
二、为什么要有 GIL?(历史原因)
GIL 的存在主要是为了简化 CPython 的内存管理:
- 非线程安全的引用计数:CPython 用「引用计数」管理内存(对象被引用时计数 + 1,销毁时 - 1,归零时释放)。如果没有 GIL,多线程同时修改引用计数会导致「数据竞争」,造成内存泄漏或对象被错误释放。
- 历史遗留:早期 Python 社区选择了「单线程执行 + GIL」的简单方案,后续虽想移除,但因大量 C 扩展依赖 GIL 的线程安全保证,移除成本过高。
三、GIL 的影响(核心考点)
GIL 并不意味着「Python 完全无法并行」,需分场景讨论:
1. CPU 密集型任务(计算为主)
- 多线程反而更慢:因为 GIL 会在「字节码执行一定数量 / 时间后」强制释放(默认 100 条字节码或 0.005 秒),线程间频繁切换、争夺 GIL 会产生额外开销,导致多线程性能甚至不如单线程。
2. I/O 密集型任务(网络、文件读写为主)
- 多线程有效:当线程执行 I/O 操作(如
time.sleep()、网络请求、文件读写)时,会主动释放 GIL,其他线程可趁机执行。因此多线程能充分利用 I/O 等待时间,提升效率。
四、如何绕过 GIL?(解决方案)
表格
| 方案 | 原理 | 适用场景 |
|---|---|---|
| 多进程(multiprocessing) | 每个进程有独立的解释器和 GIL,进程间并行执行 | CPU 密集型任务(如数据计算) |
| C 扩展(Cython/C) | 在 C 代码中手动释放 GIL(如 Py_BEGIN_ALLOW_THREADS),让 C 代码并行 |
性能瓶颈在 C 扩展的计算任务 |
| 异步编程(asyncio) | 单线程内通过「协程」切换任务,避免 GIL 争夺(本质是单线程并发) | I/O 密集型任务(如爬虫、Web 服务) |
| 使用 PyPy | PyPy 虽有 GIL,但通过 JIT 即时编译大幅提升执行速度(比 CPython 快 5-10 倍) | 纯 Python 代码的 CPU 密集型任务 |
什么是Cpython?
一、CPython 是什么?(定义)
CPython 是 Python 语言的官方默认解释器实现,由 C 语言编写,因此得名。我们平时从 python.org 下载安装的 Python 就是 CPython。
二、CPython 的核心特点(面试常考)
-
GIL 的存在CPython 有全局解释器锁(GIL),同一时刻仅允许一个线程执行 Python 字节码,这是它与其他解释器的核心区别(GIL 的影响可参考之前的回答)。
-
执行流程先将
.py源代码编译为字节码(Bytecode,.pyc 文件),再由 CPython 虚拟机(PVM)逐行解释执行字节码。 -
C 扩展支持可直接调用 C/C++ 编写的扩展模块(如 NumPy、Pandas 底层均依赖 C 扩展),这是 CPython 生态丰富、性能可优化的关键原因。
-
跨平台支持 Windows、Linux、macOS 等主流操作系统,兼容性最好。
三、CPython 的地位
- 是使用最广泛的 Python 解释器,市场占比超 90%;
- 绝大多数第三方库(如 Django、Flask、TensorFlow)优先支持 CPython;
- Python 语言的新特性(如类型提示、模式匹配)会率先在 CPython 中实现。
四、与其他常见解释器的对比(加分项)
| 解释器 | 实现语言 | 核心特点 | 适用场景 |
|---|---|---|---|
| CPython | C | 官方默认、有 GIL、支持 C 扩展 | 绝大多数日常开发、生产环境 |
| PyPy | Python | JIT 即时编译、速度快(比 CPython 快 5-10 倍)、C 扩展支持有限 | 纯 Python 代码的 CPU 密集型任务 |
| Jython | Java | 运行在 JVM 上、可直接调用 Java 代码 | 与 Java 生态集成的场景 |
| IronPython | C# | 运行在 .NET 上、可直接调用 .NET 代码 | 与 .NET 生态集成的场景 |
总结
CPython 是 Python 的官方默认实现,用 C 语言编写,有 GIL,支持 C 扩展,是目前使用最广泛、生态最完善的 Python 解释器。
迭代器与生成器
一、迭代器(Iterator)
1. 定义
迭代器是 ** 实现了「迭代器协议」** 的对象,核心是能通过 next() 函数逐个获取元素,直到抛出 StopIteration 异常。
2. 迭代器协议(核心考点)
迭代器必须实现两个特殊方法:
__iter__():返回迭代器自身(把可迭代对象转成迭代器,为了兼容for循环);__next__():返回下一个元素,没有更多元素时抛出StopIteration异常。
3. 简单例子
# 列表是「可迭代对象」(Iterable),但不是迭代器
my_list = [1, 2, 3]
# 用 iter() 把可迭代对象转成迭代器
my_iter = iter(my_list)
# 用 next() 逐个获取元素
print(next(my_iter)) # 输出 1
print(next(my_iter)) # 输出 2
print(next(my_iter)) # 输出 3
print(next(my_iter)) # 抛出 StopIteration 异常
4. 迭代器的特点
- 惰性计算:不一次性生成所有元素,只在调用
next()时才计算下一个,省内存(适合处理大数据集); - 单向遍历:只能向前逐个获取,不能后退或重置。
二、生成器(Generator)
1. 定义
生成器是特殊的迭代器,无需显式实现 __iter__ 和 __next__,用更简洁的语法就能创建,核心是 yield 关键字。
2. 两种创建方式(核心考点)
方式一:生成器函数(用 yield)
普通函数用 return 返回值,生成器函数用 yield「暂停」函数并返回值,下次调用 next() 时从暂停处继续执行。
经典例子:斐波那契数列
def fib(n):
a, b = 0, 1
for _ in range(n):
yield b # 暂停并返回 b
a, b = b, a + b
# 调用生成器函数,返回生成器对象(不会立即执行)
f = fib(5)
print(next(f)) # 输出 1(执行到第一个 yield)
print(next(f)) # 输出 1(从暂停处继续,执行到第二个 yield)
print(next(f)) # 输出 2
print(next(f)) # 输出 3
print(next(f)) # 输出 5
方式二:生成器表达式(类似列表推导式)
把列表推导式的 [] 换成 (),就得到生成器表达式,比生成器函数更简洁。
# 列表推导式:一次性生成所有元素,占内存
list_comp = [i*2 for i in range(5)]
print(list_comp) # 输出 [0, 2, 4, 6, 8]
# 生成器表达式:惰性计算,省内存
gen_comp = (i*2 for i in range(5))
print(next(gen_comp)) # 输出 0
print(next(gen_comp)) # 输出 2
3. 生成器的特点
- 完全继承迭代器的「惰性计算、省内存」特点;
- 代码比自定义迭代器简洁得多;
yield会保存函数的执行状态(变量值、执行位置),下次next()直接恢复。
三、迭代器 vs 生成器(面试常对比)
| 维度 | 迭代器 | 生成器 |
|---|---|---|
| 实现方式 | 需显式写 __iter__ 和 __next__ |
用 yield 函数或 () 表达式 |
| 代码复杂度 | 较高(需手动维护状态) | 极低(语法简洁) |
| 核心关键字 | __iter__、__next__ |
yield |
| 关系 | 生成器是特殊的迭代器 | 属于迭代器的子集 |
总结
- 迭代器是实现了
__iter__和__next__的对象,核心是惰性计算、省内存; - 生成器是简化版的迭代器,用
yield或生成器表达式创建,代码更简洁; - 两者都适合处理大数据集或无限序列(如斐波那契数列),避免内存溢出。
装饰器
一、装饰器是什么?(定义)
装饰器是 Python 的一种语法糖,核心作用是在不修改原函数 / 类代码的前提下,为其扩展额外功能(如计时、日志、权限验证、缓存等),完美符合「开闭原则」(对扩展开放,对修改关闭)。
二、装饰器的本质原理(核心考点)
装饰器的本质是「高阶函数 + 闭包」,依赖 Python 中「函数是一等公民」的特性:
- 高阶函数:接收函数作为参数,或返回函数的函数;
- 闭包:内层函数引用外层函数的变量,且外层函数返回内层函数。
三、基本语法与实现
1. 不带参数的装饰器
原始写法(不用语法糖)
# 定义装饰器(高阶函数)
def my_decorator(func):
# 定义包装函数(闭包)
def wrapper(*args, **kwargs):
# 扩展功能:原函数执行前
print("原函数执行前")
# 调用原函数,接收参数和返回值
result = func(*args, **kwargs)
# 扩展功能:原函数执行后
print("原函数执行后")
return result
return wrapper
# 被装饰的函数
def say_hello(name):
print(f"Hello, {name}")
return "done"
# 使用装饰器:将原函数传给装饰器,返回包装函数
say_hello = my_decorator(say_hello)
say_hello("Tom")
语法糖写法(@)
用 @装饰器名 放在原函数定义上方,等价于上面的 say_hello = my_decorator(say_hello):
def my_decorator(func):
def wrapper(*args, **kwargs):
print("原函数执行前")
result = func(*args, **kwargs)
print("原函数执行后")
return result
return wrapper
@my_decorator # 语法糖
def say_hello(name):
print(f"Hello, {name}")
return "done"
say_hello("Tom")
2. 保留原函数属性:functools.wraps
装饰器会让原函数的 __name__、__doc__ 等属性被包装函数覆盖,需用 functools.wraps 保留:
import functools
def my_decorator(func):
@functools.wraps(func) # 保留原函数属性
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@my_decorator
def say_hello(name):
"""这是一个打招呼的函数"""
print(f"Hello, {name}")
print(say_hello.__name__) # 输出 say_hello(不加 wraps 会输出 wrapper)
四、常见面试手写例子
1. 计时装饰器
import time
import functools
def timer(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} 执行耗时:{end - start:.2f}秒")
return result
return wrapper
@timer
def test():
time.sleep(1)
print("test 函数执行完毕")
test()
2. 日志装饰器
import functools
def log(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"调用函数:{func.__name__},参数:args={args}, kwargs={kwargs}")
result = func(*args, **kwargs)
print(f"函数 {func.__name__} 执行完毕,返回值:{result}")
return result
return wrapper
@log
def add(a, b):
return a + b
add(1, 2)
五、进阶用法(加分项)
1. 带参数的装饰器
需三层函数:最外层接收装饰器参数,中间层接收原函数,最内层是包装函数:
import functools
def log_with_level(level): # 第一层:接收装饰器参数
def decorator(func): # 第二层:接收原函数
@functools.wraps(func)
def wrapper(*args, **kwargs): # 第三层:包装函数
print(f"[{level}] 调用函数:{func.__name__}")
return func(*args, **kwargs)
return wrapper
return decorator
@log_with_level(level="INFO") # 传入参数
def say_hello(name):
print(f"Hello, {name}")
say_hello("Tom")
2. 类装饰器
通过类的 __call__ 方法实现,可保持状态(如计数):
import functools
class Counter:
def __init__(self, func):
self.func = func
self.count = 0 # 保持状态:调用次数
def __call__(self, *args, **kwargs):
self.count += 1
print(f"{self.func.__name__} 已调用 {self.count} 次")
return self.func(*args, **kwargs)
@Counter
def say_hello(name):
print(f"Hello, {name}")
say_hello("Tom")
say_hello("Jerry")
总结
- 装饰器是语法糖,本质是「高阶函数 + 闭包」;
- 作用是不修改原代码扩展功能,符合开闭原则;
- 需掌握
functools.wraps、带参数的装饰器、类装饰器等细节。
闭包和高阶函数
一、高阶函数(Higher-order Function)
1. 定义
满足以下任意一个条件的函数,就是高阶函数:
- 接收一个或多个函数作为参数;
- 返回值是一个函数。
2. 核心依赖
Python 中「函数是一等公民」(First-class Citizen),即函数可以像变量一样:
- 赋值给变量;
- 作为参数传递;
- 作为返回值返回。
3. 常见例子
例子 1:接收函数作为参数(如内置函数 map、filter)
# 自定义高阶函数:接收 func 作为参数
def apply_func(func, data):
return [func(x) for x in data]
# 被传递的函数
def square(x):
return x * x
# 使用
result = apply_func(square, [1, 2, 3])
print(result) # 输出 [1, 4, 9]
例子 2:返回值是函数
def create_multiplier(n):
# 定义内层函数
def multiplier(x):
return x * n
# 返回内层函数
return multiplier
# 使用:create_multiplier(2) 返回 multiplier 函数
double = create_multiplier(2)
print(double(5)) # 输出 10(x=5, n=2)
二、闭包(Closure)
1. 定义
闭包是一种特殊的函数,需同时满足三个条件:
- 存在内层函数嵌套在外层函数中;
- 内层函数引用了外层函数的变量(非全局变量);
- 外层函数返回了内层函数。
此时,内层函数会「记住」外层函数的变量状态,即使外层函数执行完毕,内层函数仍能访问外层变量。
2. 经典例子:累加器
def make_accumulator():
# 外层变量:sum_total
sum_total = 0
# 内层函数:引用外层变量 sum_total
def accumulator(num):
nonlocal sum_total # 声明使用外层变量(Python3 需加,Python2 用可变对象如列表)
sum_total += num
return sum_total
# 外层返回内层函数
return accumulator
# 使用
acc = make_accumulator()
print(acc(1)) # 输出 1(sum_total=1)
print(acc(2)) # 输出 3(sum_total=1+2=3)
print(acc(3)) # 输出 6(sum_total=3+3=6)
关键点:
make_accumulator执行完毕后,sum_total变量不会被销毁,而是被闭包accumulator「捕获」并保留状态。
三、高阶函数 vs 闭包 vs 装饰器的关系(核心考点)
- 高阶函数是「函数操作函数」的基础;
- 闭包是「保留外层变量状态」的特殊函数;
- 装饰器 = 高阶函数 + 闭包(装饰器通过高阶函数接收原函数,通过闭包包装原函数并保留状态)。
总结
- 高阶函数:接收函数为参数,或返回函数;
- 闭包:内层函数引用外层变量,外层返回内层,可保留状态;
- 两者结合是实现装饰器的核心原理。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)