在日常开发中,我们常常会写出像 “金字塔” 一样的代码——多层 if 嵌套、循环套循环。这种代码不仅难以阅读,更容易在修改时引入 Bug。代码扁平化(Flattening) 就是一系列旨在减少嵌套层级、让主逻辑清晰浮现的技巧。代码扁平化通常还会引入一些副作用(Side effect),例如让代码的上下文长度变长。不过总的来说依旧是利大于弊的。

代码扁平化不是炫技,而是一种以可读性和可维护性为中心的工程实践。核心心法是:“让快乐路径(Happy Path)走直线,让异常情况早退出”

本文将通过一个具体场景,一步步展示如何运用不同的扁平化策略,将一段深嵌套的代码重构为简洁、健壮、易维护的版本。


场景设置

假设我们有一组用户数据,每个用户包含姓名和订单列表,每个订单又包含 ID 和商品列表。我们的目标是:找到第一个购买了 “apple” 的有效订单,并打印出用户姓名和订单 ID。初始数据如下:

users = [
    {"name": "Alice", "orders": [{"id": 101, "items": ["apple", "banana"]}]},
    {"name": "Bob",   "orders": [{"id": 102, "items": ["orange"]}, {"id": 103, "items": ["apple"]}]},
    {"name": "Carro", "orders": []},
]

这是原始的深嵌套代码。形成了典型的 4 层嵌套,写法最直观但最不优雅。

def find_first_apple_order(users):
    for user in users:                              # 第1层:遍历用户
        if user.get("name"):                        # 第2层:检查用户名存在且非空
            for order in user.get("orders", []):    # 第3层:遍历该用户的订单
                for item in order.get("items", []): # 第4层:遍历订单中的每个商品
                    if item == "apple":
                        print(f"{user['name']} bought an apple in order #{order['id']}")

问题分析

  • 可读性差:主逻辑(找 apple)被埋在第四层。
  • 健壮性弱:如果 order 中没有 "id" 字段,会直接抛出 KeyError
  • 扩展困难:如果要增加新的校验条件(如订单必须已支付),嵌套会更深。

守卫语句(Guard Clauses) + 提前返回

守卫语句的核心思想是:在函数或循环开头快速排除非法情况,让主逻辑保持在外层

def find_first_apple_order_exp2(users):
    for user in users:
        # 守卫1:用户数据必须包含有效的 name
        if not user.get("name"):
            print("Warning: Skipping user with missing/empty 'name'")
            continue
        
        # 守卫2:用户必须有 orders 列表
        orders = user.get("orders")
        if not isinstance(orders, list):
            print("Warning: Skipping user with invalid 'orders'")
            continue

        for order in orders:
            # 守卫3:订单必须包含 items 列表
            items = order.get("items")
            if not isinstance(items, list):
                print("Warning: Skipping order with invalid 'items'")
                continue
            
            # 守卫4:订单必须有 id
            if "id" not in order:
                print("Warning: Skipping order with missing 'id'")
                continue

            # 主逻辑:检查是否包含 apple
            if "apple" in items:
                print(f"{user['name']} bought an apple in order #{order['id']}")

优化点

  1. 使用 continue 在循环中实现“提前退出”,避免了嵌套。
  2. 主逻辑(if "apple" in items)清晰地出现在最外层。
  3. 增加了类型检查(isinstance),提升了健壮性。

守卫模式 + 提前返回有一个显著优点,这种风格能用很规整的格式处理每一种错误场景,错误信息更具体,便于调试。


提取函数(Extract Function)

当某段逻辑足够独立时,将其提取成函数是降低复杂度的利器。

def is_valid_order_with_apple(order):
    """判断一个订单是否有效且包含 'apple'"""
    if not isinstance(order.get("items"), list):
        return False, None
    if "id" not in order:
        return False, None
    if "apple" in order["items"]:
        return True, order["id"]
    return False, None

def find_first_apple_order_exp3(users):
    for user in users:
        # 用户级守卫
        if not user.get("name") or not isinstance(user.get("orders"), list):
            continue

        for order in user["orders"]:
            is_valid, order_id = is_valid_order_with_apple(order)
            if is_valid:
                print(f"{user['name']} bought an apple in order #{order_id}")

优化点

  • 将订单校验和业务逻辑封装在 is_valid_order_with_apple 中,职责单一。
  • 主函数 find_first_apple_order_exp3 变得极其简洁,只关注“遍历用户 -> 遍历订单 -> 找到就返回”。
  • 便于单元测试:可以单独测试 is_valid_order_with_apple

函数式风格(使用 any + 生成器)

如果我们只关心 “是否存在”,而不需处理所有匹配项,可以用更声明式的方式。

def find_first_apple_order_exp4(users):
    for user in users:
        if not user.get("name") or not isinstance(user.get("orders"), list):
            continue
        
        # 使用生成器表达式查找第一个匹配的订单ID
        apple_order_ids = (
            order["id"]
            for order in user["orders"]
            if isinstance(order.get("items"), list)
               and "id" in order
               and "apple" in order["items"]
        )
        
        # 安全地获取第一个匹配项,无匹配则返回 `None`。
        first_id = next(apple_order_ids, None)
        if first_id is not None:
            print(f"{user['name']} bought an apple in order #{first_id}")

用生成器表达式集中描述 “什么是有效的含 apple 订单”。

⚠注意:这种方式适合“找一个”的场景。如果要处理所有匹配项,还是用显式循环更清晰。


组合使用(守卫 + 提取函数 + 清晰主干)

综合以上优点,得到一个生产环境友好的版本:

def _has_valid_apple_order(orders):
    """内部工具函数:检查订单列表中是否有有效的含 apple 订单"""
    for order in orders:
        if (isinstance(order.get("items"), list) 
            and "id" in order 
            and "apple" in order["items"]):
            return order["id"]  # 返回第一个匹配的ID
    return None

def find_first_apple_order(users):
    """
    在用户列表中查找第一个购买了 'apple' 的有效订单。
    成功找到返回 True,否则返回 False。
    """
    for user in users:
        # 快速守卫:跳过无效用户
        if not user.get("name") or not isinstance(user.get("orders"), list):
            continue
        
        order_id = _has_valid_apple_order(user["orders"])
        if order_id is not None:
            print(f"{user['name']} bought an apple in order #{order_id}")

# 测试
if __name__ == "__main__":
    users = [
        {"name": "Alice", "orders": [{"id": 101, "items": ["apple", "banana"]}]},
        {"name": "Bob",   "orders": [{"id": 102, "items": ["orange"]}, {"id": 103, "items": ["apple"]}]},
        {"name": "Carro", "orders": []},
    ]
    find_first_apple_order(users)

经过努力,我们成功的将主函数压缩到了 8 行,逻辑一目了然。在代码编写的全过程都进行了错误前置,在函数前部快速判断函数错误退出条件,不干扰后面主干代码的逻辑。还对深层次函数逻辑进行了函数封装。整个代码易于测试和拓展。


其他小技巧

条件合并

如果对于 user["name"]user["orders"] 存在性判断之后的处理逻辑相同,那么还能对这两个 if 判断进行合并。

if age < 15:
    print("Too youny.")
    return None
if ge > 18:
    print("Too old.")
    return None
go_to_high_school(user)

# 条件合并
if not age < 15 and age > 18:
    go_to_high_school(user)

德·摩根定律

使用德·摩根定律简化逻辑。将复杂的 not (A and B) 转为 not A or not B,让否定更清晰。

if not (user.age >= 18 and user.has_id):
    deny_access()

# 德·摩根定律
if user.age < 18 or not user.has_id:
    deny_access()
    return

其实这也是一种守卫风格。


字典映射

if action == "add":
    result = a + b
elif action == "sub":
    result = a - b
elif action == "mul":
    result = a * b

# 字典映射
ops = {
    "add": lambda a, b: a + b,
    "sub": lambda a, b: a - b,
    "mul": lambda a, b: a * b,
}
result = ops.get(action, lambda a, b: 0)(a, b)

仅适用于逻辑简单、副作用少的场景。


多态

if-elif-else 太多时,考虑用类和方法分发。

def get_sound(animal):
    if animal == "dog":
        return "Woof"
    elif animal == "cat":
        return "Meow"
    elif animal == "cow":
        return "Moo"
    return "..."

下面是多态实现(震撼实现,长度翻倍)

from abc import ABC, abstractmethod

# 1. 抽象基类
class Animal(ABC):
    @abstractmethod
    def sound(self):
        pass

# 2. 具体动物类
class Dog(Animal):
    def sound(self): return "Woof"

class Cat(Animal):
    def sound(self): return "Meow"

class Cow(Animal):
    def sound(self): return "Moo"

# 3. 注册所有类型(代替 if-elif)
_ANIMALS = {
    "dog": Dog,
    "cat": Cat,
    "cow": Cow,
}

# 4. 统一入口函数(接口和原来完全一样!)
def get_sound(animal: str) -> str:
    cls = _ANIMALS.get(animal)
    if cls is None:
        return "..."
    return cls().sound()  # 创建实例并调用方法

# 使用(和原来一模一样!)
print(get_sound("dog"))  # Woof
print(get_sound("cat"))  # Meow
print(get_sound("bird")) # ...

⚠同时也可以看出,用 OOP 编程是多么的重。


总结

技巧 适用场景 关键词
守卫语句 函数/循环入口校验 if ...: return/continue
提前返回 多条件分支 消除 else 嵌套
提取函数 任何 >3 行的逻辑块 单一职责
合并条件 简单布尔判断 and / or
德·摩根定律 复杂否定逻辑 not (A and B) → not A or not B
字典映射 简单分发 ops = {...}
多态 类型驱动行为 面向对象

下次当你发现自己在写第三层 if 时,请停下来想一想:能不能用守卫语句把它提到外面?能不能抽成一个函数? 你的同事(以及未来的你)会感谢你的!

⚠附:所有扁平化技巧都应确保不改变原有逻辑和副作用。例如,日志打印、状态修改等操作的位置可能需要调整,以保证行为一致。

作业:上面的代码只会在每一个 userorders 中返回第一个匹配上的 item-id。如何修改代码,来处理 orders 中存在多个 id 可供返回的场景?


示例

## 修改前
# 头重脚轻,整体代码复杂度分配不均匀
def error_handler(_error_counter, _error_info, silence_mode) -> bool:
    """错误处理函数"""
    # 复杂逻辑排在前面了
    if _error_counter >= cfg.maxCount:
        logger.wr(f"连续错误次数达到上限 {cfg.maxCount},程序出现严重错误,需要立刻排查。{str(_error_info)}", logtype='error')
        # 深层嵌套
        if silence_mode:
            return False
        bot.send_msg(f"⚠️连续错误次数达到上限 {cfg.maxCount}\n⚠️程序出现严重错误,需要立刻排查\n⚠️{str(_error_info)}")
        return False
    # 简单逻辑排在后面了
    else:
        return True

## 修改后
def error_handler(_error_counter, _error_info, silence_mode) -> bool:
    """错误处理函数"""
    # 未超过最大错误次数,返回 True
    if _error_counter < cfg.maxCount:
        return True
    # 超过最大错误次数,触发告警
    if _error_counter >= cfg.maxCount:
        logger.wr(f"连续错误次数达到上限 {cfg.maxCount},程序出现严重错误,需要立刻排查。{str(_error_info)}", logtype='error')
    if silence_mode:
        bot.send_msg(f"⚠️连续错误次数达到上限 {cfg.maxCount}\n⚠️程序出现严重错误,需要立刻排查\n⚠️{str(_error_info)}")
    return False