从零到一:深入理解并高效运用 Python 函数

 0. 为什么先学函数

在 Python 的学习曲线里,函数是第一个真正意义上的“抽象”工具。语法 if/for 只是流程控制,而函数让我们把“做什么”与“怎么做”分离。掌握了函数,你就能:
• 把 100 行脚本拆成 10 个可测试、可组合的小部件;
• 通过参数化让同一段逻辑服务多种场景;
• 用装饰器、生成器等高级特性写出优雅 DSL;
• 在阅读源码时快速定位作者意图:函数名就是注释。

1.函数的本质:从“代码片段”到“可复用抽象”

计算机科学大师 Harold Abelson 说:“程序设计语言并不仅仅是告诉计算机做什么,更是组织抽象的手段。”
在 Python 中,函数是一类对象,它具备: 

  • identity(唯一标识,id(func))

  • state(defaultsclosuredict

  • behavior(call
    因此,函数比“可复用的代码片段”多出两层含义:
    (1) 它是数据,可存列表、当键值、动态生成;
    (2) 它是契约,通过签名(signature)对外承诺输入与输出。

 2. 语法速写:定义、参数、返回值

 最简形式:

def add(a, b=1):
    """Return sum of a and b."""
    return a + b

拆解:

  • def 关键字创建函数对象,绑定到当前作用域名字 add;

  • 参数分两类:位置参数 a,默认参数 b;

  • 函数体在编译时变成代码对象 code object;

  • return 把结果压栈并终止函数,无 return 隐式 return None。

 3. 参数传递全景图

3.1 位置与关键字
位置参数按顺序赋值;关键字参数通过名字赋值,可增加可读性: 

def tag(name, /, content, *, cls=None, **attrs):
    ...

/ 前为仅位置,* 后为仅关键字(Python 3.8+)。

3.2 可变参数

def mean(first, *rest, ignore_nan=False):
    data = (first,) + rest
    if ignore_nan:
        data = [x for x in data if x == x]  # 过滤 NaN
    return sum(data) / len(data)

*args 打包成 tuple,**kwargs 打包成 dict。

3.3 解包调用

args = (1, 2, 3)
config = {'ignore_nan': True}
mean(*args, **config)

 3.4 只读默认参数陷阱
默认参数在 def 语句执行时求值一次:

def append(x, lst=[]):   # 错误示范
    lst.append(x)
    return lst

 修复:

def append(x, lst=None):
    lst = [] if lst is None else lst
    lst.append(x)
    return lst

 4. 作用域与生命周期

4.1 LEGB 规则
Local → Enclosing → Global → Built-in。 

x = 1
def outer():
    x = 2
    def inner():
        nonlocal x  # 指向 outer 作用域
        x += 1
        return x
    return inner

 4.2 闭包
函数对象保存了定义时作用域的引用,形成闭包。典型应用:

def make_power(n):
    def power(x):
        return x ** n
    return power

square = make_power(2)
cube   = make_power(3)

 4.3 延迟绑定陷阱
在循环中创建闭包:

funcs = [lambda: i for i in range(3)]
print([f() for f in funcs])  # [2, 2, 2]

 修复:

funcs = [lambda i=i: i for i in range(3)]

 5. 一等公民:把函数当数据用

5.1 高阶函数
filter、sorted、functools.reduce 都接受函数作为参数: 

from functools import reduce
reduce(lambda acc, x: acc + [x] if x % 2 else acc, range(10), [])

 5.2 函数注册表

registry = {}
def register(name):
    def wrapper(fn):
        registry[name] = fn
        return fn
    return wrapper

@register('add')
def _(a, b):
    return a + b

 5.3 动态派发

def dispatch(op):
    return registry[op]

 6. 装饰器:不修改源码的“外挂”

6.1 无参装饰器 

from functools import wraps
import time

def timethis(fn):
    @wraps(fn)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = fn(*args, **kwargs)
        print(f"{fn.__name__} took {time.perf_counter() - start:.4f}s")
        return result
    return wrapper

 6.2 带参装饰器

def repeat(n):
    def decorator(fn):
        @wraps(fn)
        def wrapper(*args, **kwargs):
            for _ in range(n):
                result = fn(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(3)
def hello():
    print("Hello")

 6.3 类装饰器

def singleton(cls):
    instances = {}
    def getinstance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    return getinstance

 7. 生成器:用函数实现“状态机”

7.1 惰性迭代 

def fib():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

 7.2 send & throw

def coro():
    print("Start")
    x = yield
    print("Got", x)
    yield x + 1

 7.3 yield from 委托

def chain(*iters):
    for it in iters:
        yield from it

8. 常见误区与最佳实践

  • 避免过度嵌套:>3 层考虑拆函数或类;

  • 函数长度:一屏(PEP8 建议 79 列 * 50 行)以内;

  • 早返回减少缩进;

  • 用 docstring 描述“做什么”而非“怎么做”;

  • 不要用可变对象为默认参数;

  • 限制 *args/**kwargs 使用场景,避免签名过于宽泛。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值