Python 核心语言机制
1. 对象模型
1.1 一切皆对象
Python 中,函数、类、模块、None、整数字面量都是对象,都有:
- 身份(
id()):对象在内存中的唯一标识 - 类型(
type()):决定对象支持哪些操作 - 值:对象存储的数据
print(type(42)) # <class 'int'>
print(type(int)) # <class 'type'>
print(type(type)) # <class 'type'>(type 是所有类的元类,且 type(type) is type)
def greet(): pass
print(type(greet)) # <class 'function'>
print(isinstance(greet, object)) # True,函数也是对象
这意味着函数可以赋值给变量、作为参数传递、存入列表——这是高阶函数和装饰器的基础。
1.2 引用与绑定
Python 变量不"存储值",而是存储对象的引用。赋值是让变量名指向对象,不是复制数据。
a = [1, 2, 3]
b = a # b 和 a 指向同一个列表对象
b.append(4)
print(a) # [1, 2, 3, 4]
print(a is b) # True,同一个对象
函数参数传递是"传对象引用"(Pass by Object Reference):
def modify(lst, num):
lst.append(99) # 修改列表内容,影响外部
num += 1 # 重新绑定局部变量,不影响外部
my_list = [1, 2]
my_num = 10
modify(my_list, my_num)
print(my_list) # [1, 2, 99]
print(my_num) # 10
1.3 is 与 ==、对象驻留
==:比较值(调用__eq__)is:比较身份(id()是否相同)
对象驻留(interning):CPython 对小整数(-5 ~ 256)和编译期可确定的短字符串做缓存,is 比较可能意外为 True:
a = 256
b = 256
print(a is b) # True(缓存范围内)
c = 257
d = 257
print(c is d) # False(超出缓存范围,但在交互式环境可能为 True)
s1 = "hello"
s2 = "hello"
print(s1 is s2) # True(字符串驻留)
# 规则:只用 is 判断 None、True、False 和单例
print(x is None) # ✅
print(x == None) # ❌ 不推荐
1.4 可变与不可变
| 不可变(Immutable) | 可变(Mutable) |
|---|---|
int, float, str | list, dict, set |
tuple, frozenset | bytearray |
bytes | 自定义类(默认) |
不可变对象的"修改"实际是创建新对象:
s = "hello"
print(id(s))
s += " world"
print(id(s)) # id 变了,s 指向新对象
经典陷阱:可变默认参数
# ❌ 错误:默认参数在函数定义时创建,所有调用共享
def append_to(item, lst=[]):
lst.append(item)
return lst
print(append_to(1)) # [1]
print(append_to(2)) # [1, 2] ← 不是 [2]!
# ✅ 正确:用 None 作哨兵
def append_to(item, lst=None):
if lst is None:
lst = []
lst.append(item)
return lst
1.5 浅拷贝与深拷贝
import copy
original = [[1, 2], [3, 4]]
shallow = copy.copy(original) # 新容器,内部元素共享
deep = copy.deepcopy(original) # 完全独立的副本
shallow[0].append(99)
print(original[0]) # [1, 2, 99] ← 浅拷贝,内层对象共享
deep[1].append(99)
print(original[1]) # [3, 4] ← 深拷贝,完全独立
选择原则:
- 只需独立容器,不需独立元素 → 浅拷贝(性能更好)
- 需要完全独立的副本 → 深拷贝(递归复制所有嵌套对象)
2. 函数与作用域
2.1 LEGB 规则
Python 按 L → E → G → B 的顺序查找名称:
| 层级 | 全称 | 说明 |
|---|---|---|
| L | Local | 当前函数内部 |
| E | Enclosing | 外层函数(闭包) |
| G | Global | 模块顶层 |
| B | Built-in | Python 内置命名空间 |
x = "global"
def outer():
x = "enclosing"
def inner():
x = "local"
print(x) # local
inner()
print(x) # enclosing
outer()
print(x) # global
global 和 nonlocal 关键字:
counter = 0
def increment():
global counter # 声明修改全局变量
counter += 1
def make_counter():
count = 0
def inc():
nonlocal count # 声明修改外层函数的变量
count += 1
return count
return inc
原则:少用
global,多用闭包或类封装状态。
2.2 高阶函数
接收函数作为参数,或返回函数的函数:
nums = [1, -3, 2, -1, 4]
# map / filter / sorted
squares = list(map(lambda x: x**2, nums))
positives = list(filter(lambda x: x > 0, nums))
by_abs = sorted(nums, key=abs)
# functools 工具
from functools import reduce, partial
total = reduce(lambda acc, x: acc + x, nums) # 求和
double = partial(pow, exp=2) # 固定部分参数
print(double(base=3)) # 9
2.3 闭包
函数记住了它被定义时的词法作用域,即使该作用域已执行完毕:
def make_multiplier(n):
def multiplier(x):
return x * n # n 来自外层,被"捕获"
return multiplier
double = make_multiplier(2)
triple = make_multiplier(3)
print(double(5)) # 10
print(triple(5)) # 15
# 查看捕获的自由变量
print(double.__closure__[0].cell_contents) # 2
经典陷阱:循环中的闭包后期绑定(Late Binding)
闭包捕获的是变量名,不是变量的值。循环结束后,所有闭包看到的 i 都是最终值:
# ❌ 错误:所有函数都返回 4
funcs = [lambda: i for i in range(5)]
print([f() for f in funcs]) # [4, 4, 4, 4, 4]
# ✅ 修复方案一:用默认参数捕获当前值
funcs = [lambda i=i: i for i in range(5)]
print([f() for f in funcs]) # [0, 1, 2, 3, 4]
# ✅ 修复方案二:用 functools.partial
from functools import partial
funcs = [partial(lambda i: i, i) for i in range(5)]
2.4 装饰器
装饰器是一个接收函数并返回新函数的高阶函数,本质是语法糖:
@decorator
def func(): ...
# 等价于
func = decorator(func)
基础装饰器(函数形式):
import functools
import time
def timer(func):
@functools.wraps(func) # 保留原函数的 __name__、__doc__ 等元信息
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
elapsed = time.perf_counter() - start
print(f"{func.__name__} 耗时 {elapsed:.4f}s")
return result
return wrapper
@timer
def slow_function():
time.sleep(0.1)
带参数的装饰器(三层嵌套):
def retry(max_times=3, exceptions=(Exception,)):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(max_times):
try:
return func(*args, **kwargs)
except exceptions as e:
if attempt == max_times - 1:
raise
print(f"第 {attempt + 1} 次失败,重试... ({e})")
return wrapper
return decorator
@retry(max_times=3, exceptions=(ConnectionError,))
def fetch_data(url):
...
类装饰器(用 __call__ 实现,适合需要保存状态的场景):
class CallCounter:
def __init__(self, func):
functools.update_wrapper(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)
@CallCounter
def greet(name):
return f"Hello, {name}!"
greet("Alice") # greet 被调用了 1 次
greet("Bob") # greet 被调用了 2 次
print(greet.count) # 2
装饰器叠加顺序:
@decorator_a
@decorator_b
@decorator_c
def func(): ...
# 等价于:func = decorator_a(decorator_b(decorator_c(func)))
# 执行时由外向内:a 的 wrapper → b 的 wrapper → c 的 wrapper → 原函数
functools.lru_cache 缓存装饰器:
from functools import lru_cache, cache
@lru_cache(maxsize=128) # 缓存最近 128 次结果
def fib(n):
if n < 2: return n
return fib(n - 1) + fib(n - 2)
@cache # Python 3.9+,等价于 lru_cache(maxsize=None)
def fib_unlimited(n):
if n < 2: return n
return fib_unlimited(n - 1) + fib_unlimited(n - 2)
print(fib(40)) # 瞬间完成(无缓存需递归 2^40 次)
print(fib.cache_info()) # CacheInfo(hits=38, misses=41, maxsize=128, currsize=41)
2.5 参数机制
Python 函数参数的五种形式:
def func(
pos_only1, pos_only2, # 位置参数
/, # / 之前只能位置传参(Python 3.8+)
normal, # 普通参数(位置或关键字均可)
*args, # 可变位置参数(收集为 tuple)
kw_only, # 关键字参数(* 之后必须用关键字传参)
**kwargs # 可变关键字参数(收集为 dict)
):
pass
def log(level, *args, sep=" ", **kwargs):
print(f"[{level}]", *args, sep=sep)
if kwargs:
print("extra:", kwargs)
log("INFO", "server", "started")
log("DEBUG", "a", "b", sep="-", timestamp="12:00")
# 解包传参
args = (1, 2, 3)
kwargs = {"name": "Alice"}
some_func(*args, **kwargs)
3. 迭代与生成
3.1 可迭代对象与迭代器
可迭代对象(Iterable):实现了 __iter__() 的对象,可以被 for 循环遍历。
迭代器(Iterator):同时实现了 __iter__() 和 __next__() 的对象,持有迭代状态。
class MyRange:
def __init__(self, n):
self.n = n
self.current = 0
def __iter__(self):
return self # 迭代器返回自身
def __next__(self):
if self.current >= self.n:
raise StopIteration
val = self.current
self.current += 1
return val
for i in MyRange(3):
print(i) # 0 1 2
可迭代对象 vs 迭代器的区别:
lst = [1, 2, 3] # 可迭代对象,但不是迭代器
it = iter(lst) # 从可迭代对象获取迭代器
print(next(it)) # 1
print(next(it)) # 2
# 可迭代对象可以多次遍历,迭代器是一次性的
for x in lst: print(x) # 正常
for x in it: print(x) # 只有 3(前两个已被消耗)
迭代器按需计算下一个值,适合处理大数据流或无限序列。
3.2 生成器
生成器是编写迭代器最简洁的方式,使用 yield 关键字:
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
gen = fibonacci()
print([next(gen) for _ in range(8)]) # [0, 1, 1, 2, 3, 5, 8, 13]
工作原理:
- 调用生成器函数 → 返回生成器对象(不执行函数体)
- 调用
next()→ 从上次yield处继续执行 - 遇到
yield→ 暂停并返回值 - 函数执行完毕 → 抛出
StopIteration
生成器表达式(比列表推导式节省内存):
# 列表推导:立即计算,占用全部内存
squares_list = [x**2 for x in range(1_000_000)]
# 生成器表达式:惰性计算,只占极少内存
squares_gen = (x**2 for x in range(1_000_000))
# 可以直接传入接受可迭代的函数
total = sum(x**2 for x in range(1_000_000)) # 括号可省略
双向通信:send() 和 throw():
def accumulator():
total = 0
while True:
value = yield total # yield 既返回值又接收 send 的值
if value is None:
break
total += value
gen = accumulator()
next(gen) # 启动生成器(必须先 next 或 send(None))
print(gen.send(10)) # 10
print(gen.send(20)) # 30
print(gen.send(5)) # 35
3.3 yield from
yield from 将迭代委托给另一个可迭代对象,等价于逐一 yield,但更高效,且会透传 send() 和 throw():
# 没有 yield from:需要手动循环
def chain_manual(*iterables):
for it in iterables:
for item in it:
yield item
# 使用 yield from:简洁高效
def chain(*iterables):
for it in iterables:
yield from it
list(chain([1, 2], [3, 4], [5])) # [1, 2, 3, 4, 5]
递归展平嵌套结构:
def flatten(nested):
for item in nested:
if isinstance(item, list):
yield from flatten(item) # 递归委托
else:
yield item
list(flatten([1, [2, [3, 4]], 5])) # [1, 2, 3, 4, 5]
3.4 itertools — 迭代器工具箱
itertools 提供了一组高效的迭代器工具,全部惰性求值:
import itertools
# --- 无限迭代器 ---
counter = itertools.count(start=1, step=2) # 1, 3, 5, 7, ...
cycler = itertools.cycle([1, 2, 3]) # 1, 2, 3, 1, 2, 3, ...
repeater = itertools.repeat("x", times=3) # 'x', 'x', 'x'
# --- 有限迭代器 ---
# chain:连接多个可迭代对象
list(itertools.chain([1, 2], [3, 4], [5])) # [1, 2, 3, 4, 5]
# islice:截取片段
list(itertools.islice(range(100), 5, 10)) # [5, 6, 7, 8, 9]
# takewhile / dropwhile:按条件截取
list(itertools.takewhile(lambda x: x < 5, [1, 3, 6, 2])) # [1, 3]
list(itertools.dropwhile(lambda x: x < 5, [1, 3, 6, 2])) # [6, 2]
# groupby:分组(需先排序)
data = [("a", 1), ("a", 2), ("b", 3)]
for key, group in itertools.groupby(data, key=lambda x: x[0]):
print(key, list(group))
# --- 组合迭代器 ---
list(itertools.product([1, 2], ["a", "b"]))
# [(1,'a'), (1,'b'), (2,'a'), (2,'b')]
list(itertools.combinations([1, 2, 3], 2))
# [(1,2), (1,3), (2,3)]
list(itertools.permutations([1, 2, 3], 2))
# [(1,2), (1,3), (2,1), (2,3), (3,1), (3,2)]
4. 上下文与协议
4.1 上下文管理器
协议:实现 __enter__ 和 __exit__ 方法,配合 with 语句使用。
class DatabaseConnection:
def __enter__(self):
self.conn = connect_to_db()
return self.conn
def __exit__(self, exc_type, exc_val, exc_tb):
self.conn.close()
return False # False = 不压制异常;True = 压制(吞掉异常)
with DatabaseConnection() as conn:
conn.query("SELECT 1")
# 离开 with 块时自动调用 __exit__,即使发生异常
__exit__ 参数说明:若 with 块内发生异常,exc_type/exc_val/exc_tb 分别为异常类型、值和 traceback;正常退出时三者均为 None。
contextlib.contextmanager:用生成器写上下文管理器:
from contextlib import contextmanager
@contextmanager
def managed_resource():
print("进入")
resource = acquire_resource()
try:
yield resource # yield 的值赋给 as 变量
finally:
release_resource(resource) # 无论是否异常都执行
print("退出")
with managed_resource() as r:
use(r)
contextlib 实用工具:
from contextlib import suppress, ExitStack, nullcontext
# suppress:忽略指定异常
with suppress(FileNotFoundError):
os.remove("maybe_exists.txt") # 文件不存在时静默跳过
# ExitStack:动态管理多个上下文
with ExitStack() as stack:
files = [stack.enter_context(open(f)) for f in file_list]
# 离开时按 LIFO 顺序关闭所有文件
# nullcontext:占位用,无操作的上下文管理器(Python 3.7+)
def process(conn=None):
ctx = nullcontext(conn) if conn else DatabaseConnection()
with ctx as c:
c.query("SELECT 1")
4.2 描述符协议
见 元编程 → 「描述符」章节。
4.3 属性查找机制
描述符协议的完整属性查找链见 元编程 → 「描述符」。
Python 访问 obj.attr 时,按如下优先级查找(从高到低):
1. 数据描述符(类或父类中定义了 __set__ 的描述符)
2. 实例 __dict__
3. 非数据描述符(只有 __get__)及普通类属性
4. __getattr__(属性不存在时的最后兜底)
class Descriptor:
def __get__(self, obj, objtype=None):
print("__get__ 被调用")
return 42
def __set__(self, obj, value):
print("__set__ 被调用")
class MyClass:
attr = Descriptor() # 数据描述符(有 __set__)
obj = MyClass()
obj.__dict__["attr"] = 999 # 直接写入实例字典
print(obj.attr) # __get__ 被调用 → 42(数据描述符优先于实例 __dict__)
自定义属性访问钩子:
class FlexibleConfig:
def __init__(self, **kwargs):
self._data = kwargs
def __getattr__(self, name):
# 只在正常查找失败后才调用
if name in self._data:
return self._data[name]
raise AttributeError(f"没有属性 {name!r}")
def __setattr__(self, name, value):
if name.startswith("_"):
super().__setattr__(name, value) # 正常存入实例 __dict__
else:
self._data[name] = value
cfg = FlexibleConfig(host="localhost", port=5432)
print(cfg.host) # localhost
cfg.timeout = 30
print(cfg.timeout) # 30
注意:
__getattribute__是每次属性访问都触发的底层钩子(包括成功的查找),覆盖它要极其小心,通常__getattr__就足够了。
4.4 类创建流程
当 Python 解释器遇到 class 语句时,依次执行:
1. 确定元类(metaclass)
→ 检查 metaclass= 参数 → 检查父类的元类 → 默认 type
2. 准备类命名空间(namespace)
→ 调用 metaclass.__prepare__(name, bases)
3. 执行类体
→ 在命名空间中执行类体代码,收集属性和方法
4. 创建类对象
→ 调用 metaclass(name, bases, namespace)
5. 应用类装饰器(如果有)
用 type 动态创建类:
# class Animal: 等价于 type("Animal", (object,), {...})
def speak(self):
return f"{self.name} says woof"
Dog = type("Dog", (object,), {
"sound": "woof",
"speak": speak,
})
rex = Dog()
rex.name = "Rex"
print(rex.speak()) # Rex says woof
元类的使用场景(元类的详细使用见 元编程 → 「元类」,此处只补充一个自动注册的例子):
class PluginMeta(type):
registry = {}
def __new__(mcs, name, bases, namespace):
cls = super().__new__(mcs, name, bases, namespace)
if bases: # 跳过基类自身
mcs.registry[name] = cls
return cls
class Plugin(metaclass=PluginMeta):
pass
class UpperPlugin(Plugin):
def process(self, text): return text.upper()
class ReversePlugin(Plugin):
def process(self, text): return text[::-1]
print(PluginMeta.registry)
# {'UpperPlugin': <class 'UpperPlugin'>, 'ReversePlugin': <class 'ReversePlugin'>}