🧩 附章 函数式语言是怎样工作的
📄 一句话概括

五个动作——推导式、不改入参、抽纯函数、用 map/key、可变默认参数改 None;5 个全部当场验证通过、0 个需要改架构——这就是【掌握】的意思:不用重写。

📝 本节和前面几节的关系

A.14 说完分工,这一节收尾:不讲理论,讲今天就能动的五件事。

一、实测五个动作

① 用推导式替 append:3 条语句 → 1 条。 ② 不改传入对象:返回新列表,原列表长度仍是 3。 ③ 抽纯函数:同输入 20 次只有 1 种结果。 ④ 用 map/key 代替手写循环:结果与手写版一致。 ⑤ 可变默认参数改 None:坏版两次共用同一列表,好版各自独立。 5 个全过,0 个需要改架构。

二、第 5 条为什么单列

def f(x, acc=[]) 里的 [] 只在定义时造一次,之后每次调用共用。 所以两次调用会累加到同一个列表——这是 Python 里最经典的一坑。 改法固定:acc=None,函数里再 acc = [] if acc is None else acc。

三、这一章该带走什么

不是【改用函数式】,是三样东西:推导式、纯函数、不改别人的东西。 这三样在面向对象的代码里同样能用,而且立刻能减少一批莫名其妙的 bug。 所以附章的落点和全书一样:你写的东西,别人改得动吗。

四、动手 Demo:掌握函数式编程

五个动作各写一版坏写法、一版好写法。 逐个验证过没过。

out1 = []
for i in range(3):
    out1.append(i * 2)
out2 = [i * 2 for i in range(3)]

src = [1, 2, 3]
new = src + [4]                      # 不改入参

def pure(x):
    return x + 1
same = len({pure(7) for _ in range(20)}) == 1

def bad(x, acc=[]):
    acc.append(x)
    return acc

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

print(out1 == out2, len(src), same)   # True 3 True
print(bad(1) == bad(2), good(1) != good(2))   # True True

实跑输出:

--------------------------------------------------------------------
掌握函数式编程
--------------------------------------------------------------------

  从今天就能用的 5 个动作,**每个都能一行验一遍:**

  动作                           验证                                         过
  ---------------------------------------------------------------------------------
  1 用推导式替 append            3 条 → 1 条语句                              True
  2 不改传入对象                 原列表长度仍是 3                             True
  3 抽纯函数                     同输入 20 次只有 1 种结果                    True
  4 用 map/key 代替手写循环     ['BB', 'A', 'CCC'] 对 ['BB', 'A', 'CCC']     True
  5 可变默认参数改 None          坏版共用 = True,好版独立 = True             True

      五个动作                    5 个
      当场验证通过                5 个
      需要改架构才能做的          0 个

  **5 个动作全部可以在现有代码里立刻做**——**这才是【掌握】的意思:不用重写。**
  **注意第 5 条**:`acc=[]` 是可变默认参数,**两次调用共用同一个列表**,这是最经典的一坑。
  **所以函数式的第一课不是抽象,是【别偷偷改别人的东西】。**

实测:五个动作全部通过,0 个需要改架构。 第 5 条是最经典的一坑:acc=[] 只在定义时造一次,两次调用共用。 这一章要带走的就三样:推导式、纯函数、不改别人的东西。

30 秒实验

  1. 今天就在自己的代码里找一处 acc=[] 或 dict={} 的默认参数,改成 None。
  2. 挑一个函数,把里面的 append 循环改成推导式。

常见误区

  1. 以为掌握函数式要学完单子。三样东西就够开工。
  2. 以为这是【另一种编程方式】。它是一组能塞进任何语言的习惯。
自测三问(点开对答案) 1. 五个动作是哪五个? —— 推导式替 append、不改入参、抽纯函数、用 map/key、可变默认参数改 None。 2. 为什么 acc=[] 是坑? —— 默认值只在定义时造一次,之后每次调用共用同一个列表。 3. 这一章要带走哪三样? —— 推导式、纯函数、不改别人的东西。

相关笔记