Python面试手册AI版——Part2
Python面试手册AI版——Part2(函数、闭包、装饰器)
·
Part 2: 函数 & 闭包 & 装饰器
一、函数基础
1.1 函数参数类型(必须精通!)
"""
Python 函数参数有 5 种类型:
1. 位置参数 (Positional)
2. 默认参数 (Default)
3. 可变位置参数 (*args)
4. 可变关键字参数 (**kwargs)
5. 仅关键字参数 (Keyword-only)
6. 仅位置参数 (Positional-only, Python 3.8+)
"""
# ============ 完整示例 ============
def example(
pos1, pos2, # 位置参数(必填)
/, # 之前的只能位置传参 (Python 3.8+)
default1="a", # 默认参数
default2="b",
*args, # 可变位置参数
kw_only1, # 仅关键字参数(*args 之后的)
kw_only2="default",
**kwargs # 可变关键字参数
):
print(f"pos1={pos1}, pos2={pos2}")
print(f"default1={default1}, default2={default2}")
print(f"args={args}")
print(f"kw_only1={kw_only1}, kw_only2={kw_only2}")
print(f"kwargs={kwargs}")
# 调用
example(1, 2, "x", "y", 3, 4, 5, kw_only1="must", extra="value")
# 输出:
# pos1=1, pos2=2
# default1=x, default2=y
# args=(3, 4, 5)
# kw_only1=must, kw_only2=default
# kwargs={'extra': 'value'}
参数顺序规则
"""
参数定义顺序(必须遵守):
位置参数 → / → 默认参数 → *args → 仅关键字参数 → **kwargs
或简化版(不使用 /):
位置参数 → 默认参数 → *args → 仅关键字参数 → **kwargs
"""
# ============ 常见错误 ============
# def wrong(name="default", age): # SyntaxError: 默认参数后不能有位置参数
# pass
# ============ 仅关键字参数的两种写法 ============
# 方式1:在 *args 之后
def func1(*args, keyword_only):
pass
# 方式2:使用单独的 * (不接收可变参数时)
def func2(a, b, *, keyword_only):
pass
func2(1, 2, keyword_only=3) # ✅
# func2(1, 2, 3) # ❌ TypeError
/ 和 * 的含义
"""
/ : 之前的参数只能通过位置传递
* : 之后的参数只能通过关键字传递
"""
# Python 3.8+ 的仅位置参数
def greet(name, /, greeting="Hello"):
return f"{greeting}, {name}!"
greet("Alice") # ✅ "Hello, Alice!"
greet("Alice", "Hi") # ✅ "Hi, Alice!"
greet("Alice", greeting="Hi") # ✅
# greet(name="Alice") # ❌ TypeError: name 是仅位置参数
# 实际应用:len() 函数
# def len(obj, /): # obj 不能用关键字传递
# ...
1.2 参数传递机制(高频考点!)
"""
Python 参数传递是 "传对象引用"(Pass by Object Reference)
- 不是传值(Pass by Value)
- 也不是传引用(Pass by Reference)
关键理解:
- 函数内部的参数名是原对象的新引用
- 对可变对象的 "原地修改" 会影响外部
- 对参数的 "重新赋值" 不会影响外部
"""
# ============ 示例1:不可变对象 ============
def modify_int(x):
print(f"函数内修改前: id={id(x)}")
x = x + 1 # 创建新对象,x 指向新对象
print(f"函数内修改后: id={id(x)}")
return x
a = 10
print(f"调用前: a={a}, id={id(a)}")
modify_int(a)
print(f"调用后: a={a}") # a 仍然是 10
# ============ 示例2:可变对象 - 原地修改 ============
def modify_list_inplace(lst):
lst.append(4) # 原地修改,影响外部
lst[0] = 100 # 原地修改,影响外部
my_list = [1, 2, 3]
modify_list_inplace(my_list)
print(my_list) # [100, 2, 3, 4] ← 被修改了!
# ============ 示例3:可变对象 - 重新赋值 ============
def reassign_list(lst):
lst = [7, 8, 9] # 重新赋值,不影响外部
print(f"函数内: {lst}")
my_list = [1, 2, 3]
reassign_list(my_list)
print(f"函数外: {my_list}") # [1, 2, 3] ← 未被修改
# ============ 示例4:常见陷阱 ============
def tricky(lst):
lst = lst + [4] # + 创建新列表,不影响外部
# lst += [4] # += 是原地修改(调用 __iadd__),会影响外部!
my_list = [1, 2, 3]
tricky(my_list)
print(my_list) # [1, 2, 3]
def tricky2(lst):
lst += [4] # 原地修改
my_list = [1, 2, 3]
tricky2(my_list)
print(my_list) # [1, 2, 3, 4] ← 被修改了!
面试标准答案:
Python 的参数传递既不是传值也不是传引用,而是 传对象引用。
函数参数是实参的一个别名,指向同一个对象。
如果对参数进行原地修改(如list.append()),会影响原对象;
如果对参数重新赋值,则只是让局部变量指向新对象,不影响原对象。
1.3 *args 和 **kwargs 解包
# ============ 定义时:收集参数 ============
def func(*args, **kwargs):
print(f"args = {args}") # tuple
print(f"kwargs = {kwargs}") # dict
func(1, 2, 3, a=4, b=5)
# args = (1, 2, 3)
# kwargs = {'a': 4, 'b': 5}
# ============ 调用时:解包参数 ============
def greet(name, age, city):
print(f"{name}, {age} years old, from {city}")
# 列表/元组解包
args = ["Alice", 30, "Beijing"]
greet(*args) # 等价于 greet("Alice", 30, "Beijing")
# 字典解包
kwargs = {"name": "Bob", "age": 25, "city": "Shanghai"}
greet(**kwargs) # 等价于 greet(name="Bob", age=25, city="Shanghai")
# 组合使用
greet(*["Alice", 30], **{"city": "Beijing"})
# ============ 实际应用:装饰器转发参数 ============
def decorator(func):
def wrapper(*args, **kwargs):
print("Before")
result = func(*args, **kwargs) # 转发所有参数
print("After")
return result
return wrapper
1.4 函数注解与类型提示
from typing import (
List, Dict, Tuple, Set, Optional, Union,
Callable, Any, TypeVar, Generic
)
# ============ 基础类型注解 ============
def greet(name: str, age: int = 0) -> str:
"""
参数类型和返回值类型注解
注意:Python 运行时不强制检查类型!
"""
return f"Hello, {name}! You are {age} years old."
# ============ 容器类型 ============
# Python 3.9+ 可以直接用 list, dict 等
def process_items(items: list[int]) -> dict[str, int]:
return {str(i): i for i in items}
# Python 3.8 及之前需要从 typing 导入
def process_items_old(items: List[int]) -> Dict[str, int]:
return {str(i): i for i in items}
# ============ Optional 和 Union ============
# Optional[X] 等价于 Union[X, None]
def find_user(user_id: int) -> Optional[dict]:
"""可能返回 dict,也可能返回 None"""
if user_id > 0:
return {"id": user_id, "name": "Alice"}
return None
# Union:多种类型
def process(value: Union[int, str]) -> str:
return str(value)
# Python 3.10+ 可以用 | 替代 Union
def process_new(value: int | str) -> str:
return str(value)
# ============ Callable(函数类型) ============
def apply_func(func: Callable[[int, int], int], a: int, b: int) -> int:
"""
Callable[[参数类型列表], 返回类型]
"""
return func(a, b)
apply_func(lambda x, y: x + y, 1, 2) # 3
# ============ TypeVar(泛型) ============
T = TypeVar('T')
def first(items: list[T]) -> T:
"""返回类型与列表元素类型一致"""
return items[0]
first([1, 2, 3]) # 返回 int
first(["a", "b"]) # 返回 str
# ============ 访问注解信息 ============
print(greet.__annotations__)
# {'name': <class 'str'>, 'age': <class 'int'>, 'return': <class 'str'>}
二、作用域与命名空间(LEGB 规则)
2.1 LEGB 规则详解
"""
Python 查找变量的顺序(LEGB):
L - Local : 函数内部局部作用域
E - Enclosing : 外层嵌套函数的作用域(闭包)
G - Global : 模块全局作用域
B - Built-in : Python 内置作用域
查找顺序:L → E → G → B
"""
# ============ 示例 ============
x = "global" # G
def outer():
x = "enclosing" # E
def inner():
x = "local" # L
print(x) # 输出: local
inner()
outer()
# ============ 内置作用域 ============
# print, len, range 等都在 Built-in 作用域
# 可以被覆盖(但不推荐)
# list = [1, 2, 3] # ❌ 覆盖了内置的 list
2.2 global 和 nonlocal 关键字
# ============ global:修改全局变量 ============
counter = 0
def increment():
global counter # 声明使用全局变量
counter += 1
increment()
print(counter) # 1
# 不使用 global 会报错
def wrong_increment():
# counter += 1 # UnboundLocalError: 赋值使其成为局部变量
pass
# ============ nonlocal:修改外层函数变量 ============
def outer():
count = 0
def inner():
nonlocal count # 声明使用外层变量
count += 1
return count
return inner
counter = outer()
print(counter()) # 1
print(counter()) # 2
print(counter()) # 3
# ============ 常见错误 ============
x = 10
def foo():
# print(x) # ❌ UnboundLocalError
x = 20 # 这行使 x 成为局部变量,但上面的 print 在赋值前使用
# 正确做法
def foo_correct():
global x
print(x)
x = 20
三、闭包(Closure)
3.1 什么是闭包?
"""
闭包 = 函数 + 它引用的外层变量
形成闭包的条件:
1. 有嵌套函数
2. 内层函数引用了外层函数的变量
3. 外层函数返回内层函数
"""
# ============ 闭包示例 ============
def make_multiplier(n: int) -> Callable[[int], int]:
"""工厂函数:创建乘法器"""
def multiplier(x: int) -> int:
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__) # (<cell at 0x...: int object at 0x...>,)
print(double.__closure__[0].cell_contents) # 2
# ============ 闭包的本质 ============
# 闭包让函数"记住"了创建时的环境
# n 的生命周期被延长了,不会随 make_multiplier 返回而销毁
3.2 闭包的经典陷阱(必考!)
# ============ 陷阱:循环变量捕获 ============
def create_functions():
functions = []
for i in range(3):
def f():
return i # 捕获的是变量 i,不是值
functions.append(f)
return functions
funcs = create_functions()
print(funcs[0]()) # 期望 0,实际 2
print(funcs[1]()) # 期望 1,实际 2
print(funcs[2]()) # 2
# 原因:闭包捕获的是变量的引用,而非值
# 循环结束后 i = 2,所有函数都引用同一个 i
# ============ 解决方案1:默认参数(推荐) ============
def create_functions_fixed():
functions = []
for i in range(3):
def f(x=i): # 默认参数在定义时求值
return x
functions.append(f)
return functions
# ============ 解决方案2:再包一层 ============
def create_functions_fixed2():
functions = []
for i in range(3):
def make_f(n):
def f():
return n
return f
functions.append(make_f(i))
return functions
# ============ 解决方案3:使用 functools.partial ============
from functools import partial
def create_functions_fixed3():
def f(x):
return x
return [partial(f, i) for i in range(3)]
# ============ 解决方案4:lambda + 默认参数 ============
functions = [lambda x=i: x for i in range(3)]
3.3 闭包的实际应用
# ============ 应用1:计数器 ============
def make_counter(start: int = 0) -> Callable[[], int]:
count = start
def counter():
nonlocal count
count += 1
return count
return counter
counter = make_counter()
print(counter(), counter(), counter()) # 1 2 3
# ============ 应用2:缓存/记忆化 ============
def make_cache() -> Callable:
cache = {}
def cached_func(func: Callable) -> Callable:
def wrapper(*args):
if args not in cache:
cache[args] = func(*args)
return cache[args]
return wrapper
return cached_func
# ============ 应用3:配置工厂 ============
def make_logger(prefix: str, level: str = "INFO"):
def log(message: str):
print(f"[{level}] {prefix}: {message}")
return log
api_logger = make_logger("API", "DEBUG")
db_logger = make_logger("Database", "ERROR")
api_logger("Request received") # [DEBUG] API: Request received
db_logger("Connection failed") # [ERROR] Database: Connection failed
四、装饰器(Decorator)
装饰器是面试的 重中之重,几乎每场面试都会问!
4.1 装饰器的本质
"""
装饰器本质上是:
1. 一个函数(或类)
2. 接收一个函数作为参数
3. 返回一个新函数(通常是包装后的函数)
语法糖:@decorator 等价于 func = decorator(func)
"""
# ============ 最简单的装饰器 ============
def my_decorator(func):
def wrapper(*args, **kwargs):
print("Before function call")
result = func(*args, **kwargs)
print("After function call")
return result
return wrapper
@my_decorator
def say_hello(name):
print(f"Hello, {name}!")
return "Done"
# 等价于:say_hello = my_decorator(say_hello)
say_hello("Alice")
# 输出:
# Before function call
# Hello, Alice!
# After function call
4.2 保留原函数元信息(functools.wraps)
from functools import wraps
# ============ 问题:装饰器会丢失原函数信息 ============
def bad_decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@bad_decorator
def my_func():
"""这是 my_func 的文档"""
pass
print(my_func.__name__) # wrapper ← 应该是 my_func
print(my_func.__doc__) # None ← 应该是文档字符串
# ============ 解决:使用 @wraps ============
def good_decorator(func):
@wraps(func) # 保留原函数的元信息
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@good_decorator
def my_func():
"""这是 my_func 的文档"""
pass
print(my_func.__name__) # my_func ✅
print(my_func.__doc__) # 这是 my_func 的文档 ✅
print(my_func.__wrapped__) # 可以访问原函数
4.3 带参数的装饰器
from functools import wraps
from typing import Callable, Any
import time
import logging
logger = logging.getLogger(__name__)
# ============ 三层嵌套结构 ============
def repeat(times: int) -> Callable:
"""装饰器工厂:返回真正的装饰器"""
def decorator(func: Callable) -> Callable:
@wraps(func)
def wrapper(*args, **kwargs) -> Any:
for _ in range(times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(times=3)
def say_hello(name: str):
print(f"Hello, {name}!")
# 等价于:say_hello = repeat(times=3)(say_hello)
say_hello("Alice")
# 输出:
# Hello, Alice!
# Hello, Alice!
# Hello, Alice!
# ============ 实用示例:计时装饰器 ============
def timer(precision: int = 3) -> Callable:
"""测量函数执行时间"""
def decorator(func: Callable) -> Callable:
@wraps(func)
def wrapper(*args, **kwargs) -> Any:
start = time.perf_counter()
result = func(*args, **kwargs)
elapsed = time.perf_counter() - start
logger.info(f"{func.__name__} took {elapsed:.{precision}f}s")
return result
return wrapper
return decorator
@timer(precision=4)
def slow_function():
time.sleep(0.1)
return "done"
# ============ 实用示例:重试装饰器 ============
def retry(
max_attempts: int = 3,
exceptions: tuple = (Exception,),
delay: float = 1.0
) -> Callable:
"""失败时自动重试"""
def decorator(func: Callable) -> Callable:
@wraps(func)
def wrapper(*args, **kwargs) -> Any:
last_exception = None
for attempt in range(1, max_attempts + 1):
try:
return func(*args, **kwargs)
except exceptions as e:
last_exception = e
logger.warning(
f"{func.__name__} attempt {attempt}/{max_attempts} failed: {e}"
)
if attempt < max_attempts:
time.sleep(delay)
raise last_exception
return wrapper
return decorator
@retry(max_attempts=3, exceptions=(ConnectionError, TimeoutError))
def fetch_data(url: str) -> dict:
# 可能失败的网络请求
pass
4.4 类装饰器
from functools import wraps
from typing import Callable, Any
import time
# ============ 方式1:类作为装饰器 ============
class Timer:
"""使用 __call__ 使类实例可调用"""
def __init__(self, func: Callable):
wraps(func)(self) # 保留原函数信息
self.func = func
self.call_count = 0
def __call__(self, *args, **kwargs) -> Any:
self.call_count += 1
start = time.perf_counter()
result = self.func(*args, **kwargs)
elapsed = time.perf_counter() - start
print(f"{self.func.__name__} (call #{self.call_count}) took {elapsed:.4f}s")
return result
@Timer
def slow_func():
time.sleep(0.1)
slow_func() # slow_func (call #1) took 0.1001s
slow_func() # slow_func (call #2) took 0.1002s
print(slow_func.call_count) # 2
# ============ 方式2:带参数的类装饰器 ============
class Retry:
def __init__(self, max_attempts: int = 3, delay: float = 1.0):
self.max_attempts = max_attempts
self.delay = delay
def __call__(self, func: Callable) -> Callable:
@wraps(func)
def wrapper(*args, **kwargs) -> Any:
for attempt in range(self.max_attempts):
try:
return func(*args, **kwargs)
except Exception as e:
if attempt == self.max_attempts - 1:
raise
time.sleep(self.delay)
return wrapper
@Retry(max_attempts=5, delay=2.0)
def unstable_operation():
pass
# ============ 方式3:用装饰器装饰类 ============
def singleton(cls):
"""单例装饰器"""
instances = {}
@wraps(cls)
def get_instance(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return get_instance
@singleton
class Database:
def __init__(self):
print("Connecting to database...")
db1 = Database() # Connecting to database...
db2 = Database() # 不会再打印
print(db1 is db2) # True
4.5 装饰器叠加顺序
def decorator_a(func):
print("Applying A")
def wrapper(*args, **kwargs):
print("A before")
result = func(*args, **kwargs)
print("A after")
return result
return wrapper
def decorator_b(func):
print("Applying B")
def wrapper(*args, **kwargs):
print("B before")
result = func(*args, **kwargs)
print("B after")
return result
return wrapper
@decorator_a
@decorator_b
def my_func():
print("Function")
# 等价于:my_func = decorator_a(decorator_b(my_func))
# 装饰时(从下往上):
# Applying B
# Applying A
# 调用时(从上往下):
my_func()
# A before
# B before
# Function
# B after
# A after
记忆口诀:装饰从下往上,执行从上往下(洋葱模型)
4.6 常用内置装饰器
# ============ @property(属性装饰器) ============
class Circle:
def __init__(self, radius: float):
self._radius = radius
@property
def radius(self) -> float:
"""getter"""
return self._radius
@radius.setter
def radius(self, value: float):
"""setter"""
if value < 0:
raise ValueError("Radius must be positive")
self._radius = value
@property
def area(self) -> float:
"""只读属性(没有 setter)"""
return 3.14159 * self._radius ** 2
c = Circle(5)
print(c.radius) # 5(调用 getter)
c.radius = 10 # 调用 setter
print(c.area) # 314.159
# c.area = 100 # AttributeError: can't set attribute
# ============ @staticmethod 和 @classmethod ============
class MyClass:
class_var = "I'm a class variable"
def __init__(self, value):
self.value = value
def instance_method(self):
"""实例方法:第一个参数是 self"""
return f"instance: {self.value}"
@classmethod
def class_method(cls):
"""类方法:第一个参数是 cls(类本身)"""
return f"class: {cls.class_var}"
@staticmethod
def static_method(x, y):
"""静态方法:没有隐式参数,跟普通函数一样"""
return x + y
obj = MyClass("hello")
print(obj.instance_method()) # instance: hello
print(MyClass.class_method()) # class: I'm a class variable
print(obj.class_method()) # 也可以通过实例调用
print(MyClass.static_method(1, 2)) # 3
# ============ @functools.lru_cache(缓存装饰器) ============
from functools import lru_cache
@lru_cache(maxsize=128)
def fibonacci(n: int) -> int:
"""带缓存的递归斐波那契"""
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(100)) # 瞬间返回,不会重复计算
print(fibonacci.cache_info()) # 查看缓存统计
fibonacci.cache_clear() # 清除缓存
# Python 3.9+ 可以使用 @cache(无大小限制)
from functools import cache
@cache
def factorial(n: int) -> int:
return n * factorial(n - 1) if n else 1
4.7 装饰器实战模板
from functools import wraps
from typing import Callable, Any, Optional, TypeVar, ParamSpec
import time
import logging
logger = logging.getLogger(__name__)
P = ParamSpec('P')
T = TypeVar('T')
# ============ 通用装饰器模板 ============
def decorator_template(func: Callable[P, T]) -> Callable[P, T]:
"""
标准装饰器模板
"""
@wraps(func)
def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
# 前置逻辑
try:
result = func(*args, **kwargs)
# 后置逻辑
return result
except Exception as e:
# 异常处理
raise
finally:
# 清理逻辑
pass
return wrapper
# ============ 带参数的装饰器模板 ============
def parametrized_decorator(
param1: str = "default",
param2: int = 0
) -> Callable[[Callable[P, T]], Callable[P, T]]:
"""
带参数的装饰器模板
"""
def decorator(func: Callable[P, T]) -> Callable[P, T]:
@wraps(func)
def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
logger.info(f"Params: {param1}, {param2}")
return func(*args, **kwargs)
return wrapper
return decorator
# ============ 可选参数的装饰器 ============
def flexible_decorator(
func: Optional[Callable[P, T]] = None,
*,
param: str = "default"
) -> Callable:
"""
同时支持 @decorator 和 @decorator() 两种写法
"""
def decorator(f: Callable[P, T]) -> Callable[P, T]:
@wraps(f)
def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
print(f"param: {param}")
return f(*args, **kwargs)
return wrapper
if func is not None:
# @decorator 形式(无括号)
return decorator(func)
# @decorator() 或 @decorator(param="xxx") 形式
return decorator
# 两种写法都可以:
@flexible_decorator
def func1():
pass
@flexible_decorator()
def func2():
pass
@flexible_decorator(param="custom")
def func3():
pass
五、生成器与迭代器
5.1 迭代器协议
"""
可迭代对象 (Iterable):实现了 __iter__() 方法
迭代器 (Iterator):实现了 __iter__() 和 __next__() 方法
关系:
- 可迭代对象的 __iter__() 返回迭代器
- 迭代器的 __iter__() 返回自身
- 迭代器的 __next__() 返回下一个元素,没有元素时抛出 StopIteration
"""
# ============ 手动实现迭代器 ============
class CountDown:
def __init__(self, start: int):
self.current = start
def __iter__(self):
return self # 返回自身(迭代器)
def __next__(self):
if self.current <= 0:
raise StopIteration
self.current -= 1
return self.current + 1
# 使用
for num in CountDown(5):
print(num) # 5 4 3 2 1
# ============ iter() 和 next() 内置函数 ============
lst = [1, 2, 3]
iterator = iter(lst) # 获取迭代器
print(next(iterator)) # 1
print(next(iterator)) # 2
print(next(iterator)) # 3
# print(next(iterator)) # StopIteration
# next() 可以指定默认值
print(next(iterator, "finished")) # finished
5.2 生成器函数(yield)
"""
生成器是一种特殊的迭代器,使用 yield 关键字定义
优点:
1. 惰性求值,节省内存
2. 代码更简洁
3. 可以表示无限序列
"""
# ============ 基础生成器 ============
def count_up_to(n: int):
"""生成 1 到 n 的数字"""
i = 1
while i <= n:
yield i # 暂停并返回值
i += 1
gen = count_up_to(3)
print(type(gen)) # <class 'generator'>
print(next(gen)) # 1
print(next(gen)) # 2
print(next(gen)) # 3
# 用于循环
for num in count_up_to(5):
print(num)
# ============ 生成器 vs 列表 的内存对比 ============
import sys
# 列表:立即创建所有元素
list_comp = [x ** 2 for x in range(1000000)]
print(sys.getsizeof(list_comp)) # ~8 MB
# 生成器:惰性求值
gen_exp = (x ** 2 for x in range(1000000))
print(sys.getsizeof(gen_exp)) # ~200 bytes ✅
# ============ yield from(委托生成器) ============
def chain(*iterables):
"""连接多个可迭代对象"""
for it in iterables:
yield from it # 等价于: for item in it: yield item
list(chain([1, 2], [3, 4], [5, 6])) # [1, 2, 3, 4, 5, 6]
# ============ 生成器表达式 ============
# 列表推导式
squares_list = [x ** 2 for x in range(10)] # 返回 list
# 生成器表达式
squares_gen = (x ** 2 for x in range(10)) # 返回 generator
# ============ 实用示例:读取大文件 ============
def read_large_file(file_path: str, chunk_size: int = 1024):
"""逐块读取大文件,避免内存溢出"""
with open(file_path, 'r') as f:
while True:
chunk = f.read(chunk_size)
if not chunk:
break
yield chunk
for chunk in read_large_file("huge_file.txt"):
process(chunk)
5.3 生成器高级用法(send、throw、close)
# ============ send():向生成器发送值 ============
def accumulator():
"""累加器:接收外部发送的值"""
total = 0
while True:
value = yield total # yield 返回当前值,并接收 send 的值
if value is not None:
total += value
acc = accumulator()
print(next(acc)) # 0 (启动生成器)
print(acc.send(10)) # 10
print(acc.send(20)) # 30
print(acc.send(5)) # 35
# ============ throw():向生成器抛出异常 ============
def careful_generator():
try:
yield 1
yield 2
yield 3
except ValueError:
yield "caught ValueError"
gen = careful_generator()
print(next(gen)) # 1
print(gen.throw(ValueError)) # caught ValueError
# ============ close():关闭生成器 ============
def generator_with_cleanup():
try:
yield 1
yield 2
finally:
print("Cleanup executed")
gen = generator_with_cleanup()
print(next(gen)) # 1
gen.close() # Cleanup executed
六、Lambda 表达式
# ============ 基础语法 ============
# lambda 参数: 表达式
add = lambda x, y: x + y
print(add(1, 2)) # 3
# 等价于:
def add(x, y):
return x + y
# ============ 常见用途 ============
# 1. 排序 key
students = [("Alice", 85), ("Bob", 92), ("Charlie", 78)]
students.sort(key=lambda x: x[1], reverse=True)
# [('Bob', 92), ('Alice', 85), ('Charlie', 78)]
# 2. filter
numbers = [1, 2, 3, 4, 5, 6]
evens = list(filter(lambda x: x % 2 == 0, numbers)) # [2, 4, 6]
# 3. map
squares = list(map(lambda x: x ** 2, numbers)) # [1, 4, 9, 16, 25, 36]
# 4. reduce
from functools import reduce
total = reduce(lambda x, y: x + y, numbers) # 21
# ============ lambda 的限制 ============
# 1. 只能是单个表达式,不能有语句(如 if-else 语句、赋值等)
# 2. 不能有 return、yield
# 3. 可读性差,复杂逻辑应使用普通函数
# 条件表达式可以用
max_val = lambda x, y: x if x > y else y
# ============ 不推荐的用法 ============
# 给 lambda 命名(应该用 def)
# f = lambda x: x ** 2 # ❌
def f(x): return x ** 2 # ✅
# 过于复杂的 lambda
# ❌ 难以阅读
complex_lambda = lambda x: (x ** 2 if x > 0 else -x ** 2) + (1 if x % 2 == 0 else 0)
📝 Part 2 总结
面试高频考点
| 知识点 | 面试频率 | 难度 | 必须掌握程度 |
|---|---|---|---|
| 参数类型(*args, **kwargs) | ⭐⭐⭐⭐⭐ | ⭐⭐ | 手写代码 |
| 参数传递机制 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | 能解释清楚 |
| LEGB 作用域规则 | ⭐⭐⭐⭐ | ⭐⭐ | 能解释清楚 |
| 闭包原理 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | 手写代码 |
| 闭包陷阱(循环变量) | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | 必须掌握 |
| 装饰器原理 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | 手写代码 |
| 带参数的装饰器 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | 手写代码 |
| functools.wraps | ⭐⭐⭐⭐ | ⭐⭐ | 知道为什么用 |
| 生成器 yield | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | 手写代码 |
| 迭代器协议 | ⭐⭐⭐ | ⭐⭐⭐ | 能解释清楚 |
面试常见问题
- 什么是闭包?请手写一个闭包示例
- 装饰器的原理是什么?@语法糖等价于什么?
- 请手写一个带参数的装饰器
- Python 的参数传递是传值还是传引用?
- yield 和 return 的区别是什么?
- 什么情况下用生成器而不是列表?
- *args 和 **kwargs 是什么意思?
- global 和 nonlocal 的区别?
更多推荐


所有评论(0)