python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Python条件判断嵌套

Python基础指南之条件判断嵌套的写法及优化技巧

作者:星河耀银海

本文介绍了Python中条件判断嵌套的写法及优化技巧,文章内提出了三种优化方案,每种方法都通过对比问题代码和优化代码进行说明,帮助开发者写出更清晰、更易维护的条件判断代码

一、开篇:嵌套是蜜糖也是砒霜

条件嵌套——把if放在if里面——这是所有编程语言都支持的结构,也是让代码变得复杂、难读、难维护的"元凶"之一。

先看一段让你头皮发麻的代码:

# 这样的代码你见过吗?
def process_order(order):
    if order is not None:
        if order.status == "paid":
            if order.user is not None:
                if order.user.is_verified:
                    if order.items:
                        if all(item.stock > 0 for item in order.items):
                            # 终于到了实际逻辑!
                            ship_order(order)
                        else:
                            print("部分商品缺货")
                    else:
                        print("订单为空")
                else:
                    print("用户未验证")
            else:
                print("用户不存在")
        else:
            print("订单未支付")
    else:
        print("订单不存在")

这就是著名的"厄运金字塔"(Pyramid of Doom)——代码越来越向右倾斜,可读性直线下降。

今天这篇文章,我们就来学习条件嵌套的正确使用方式,以及如何利用多条件组合和重构技巧,把"嵌套地狱"变成"清晰逻辑"。

二、条件嵌套的基本结构

2.1 什么是条件嵌套

条件嵌套就是在一个if/elif/else的代码块中再放入另一个if语句:

# 条件嵌套的基本形式
if outer_condition:
    # 外层条件为真时执行的代码
    if inner_condition:
        # 内外层条件都为真时执行
        print("两个条件都满足")
    else:
        # 外层为真但内层为假
        print("只有外层条件满足")
else:
    # 外层条件为假
    print("外层条件不满足")

2.2 嵌套中的缩进陷阱

# 缩进决定了哪个if属于哪个else
# Python的规则:else与最近的未配对的if匹配

x = 10
y = 5

# 情况1:明确的嵌套
if x > 0:
    if y > 0:
        print("x和y都大于0")
    else:
        print("x>0但y<=0")   # 这个else属于内层if

# 情况2:使用括号或空行无助于改变归属关系
if x > 0:
    if y > 0:
        print("x和y都大于0")
else:                       # 这个else属于哪个if?
    print("x<=0")           # 答案:属于最外层的if(因为内层if有缩进)

# ⚠️ 容易混淆的情况
if x > 0:
    if y > 0:
        print("都大于0")
    else:
        print("y<=0")       # else属于内层的 if y > 0

# 如果想让else属于外层if,需要填充内层
if x > 0:
    if y > 0:
        print("都大于0")
    # 内层没有else,所以这个位置为空
else:
    print("x<=0")           # 现在else属于外层if

三、嵌套地狱与解救之道

3.1 技法一:提前返回(Guard Clauses)

这是最重要也最有效的技法:

# ❌ 嵌套地狱
def validate_and_process(user, data):
    if user is not None:
        if user.is_active:
            if user.has_permission("write"):
                if data is not None:
                    if len(data) > 0:
                        # 实际处理逻辑
                        result = process(user, data)
                        return result
                    else:
                        return "数据为空"
                else:
                    return "数据不存在"
            else:
                return "无权限"
        else:
            return "用户未激活"
    else:
        return "用户不存在"

# ✅ 提前返回(Guard Clauses)
def validate_and_process_v2(user, data):
    if user is None:
        return "用户不存在"
    if not user.is_active:
        return "用户未激活"
    if not user.has_permission("write"):
        return "无权限"
    if data is None:
        return "数据不存在"
    if len(data) == 0:
        return "数据为空"
    
    # 所有异常情况已处理完毕
    # 这里是正常逻辑,缩进层级为0!
    return process(user, data)

3.2 技法二:条件合并

当嵌套的多个if之间是AND关系时:

# ❌ 不必要的嵌套
def check_access(user, resource):
    if user.is_authenticated:
        if user.is_active:
            if user.has_role("admin"):
                if resource.is_available:
                    return True
    return False

# ✅ 条件合并
def check_access_v2(user, resource):
    if (user.is_authenticated 
        and user.is_active 
        and user.has_role("admin") 
        and resource.is_available):
        return True
    return False

# ✅ 更进一步的简化
def check_access_v3(user, resource):
    return (user.is_authenticated 
            and user.is_active 
            and user.has_role("admin") 
            and resource.is_available)

3.3 技法三:提取函数

当条件逻辑复杂时,提取为命名良好的函数:

# ❌ 复杂的内联条件
def handle_request(request):
    if request.method == "POST":
        if request.path.startswith("/api/"):
            if "Authorization" in request.headers:
                token = request.headers["Authorization"]
                if token.startswith("Bearer "):
                    actual_token = token[7:]
                    if is_valid_token(actual_token):
                        user = get_user_from_token(actual_token)
                        if user.is_active and not user.is_banned:
                            # 实际处理
                            return process_api_request(request, user)
    return error_response("Unauthorized", 401)

# ✅ 提取函数:每个函数只做一件事
def authenticate_request(request):
    """验证请求并返回用户,失败返回None"""
    if request.method != "POST":
        return None
    if not request.path.startswith("/api/"):
        return None
    
    auth_header = request.headers.get("Authorization", "")
    if not auth_header.startswith("Bearer "):
        return None
    
    token = auth_header[7:]
    if not is_valid_token(token):
        return None
    
    return get_user_from_token(token)

def is_user_authorized(user):
    """检查用户是否有权访问"""
    return user is not None and user.is_active and not user.is_banned

def handle_request_v2(request):
    user = authenticate_request(request)
    if not is_user_authorized(user):
        return error_response("Unauthorized", 401)
    
    return process_api_request(request, user)

3.4 技法四:使用字典或查找表

# ❌ 嵌套的条件检查
def get_shipping_cost(method, weight, distance, is_express):
    if method == "standard":
        if weight < 1:
            if distance < 100:
                return 5
            elif distance < 500:
                return 10
            else:
                return 15
        elif weight < 5:
            if distance < 100:
                return 10
            elif distance < 500:
                return 20
            else:
                return 30
        else:
            return 50
    elif method == "express":
        # 类似的多层嵌套...
        pass

# ✅ 查找表 + 函数
def get_shipping_cost_v2(method, weight, distance):
    # 基础费用
    base_cost = _get_base_cost(weight, distance)
    
    # 方法倍率
    method_multiplier = {
        "standard": 1.0,
        "express": 1.8,
        "overnight": 3.0,
    }
    
    return base_cost * method_multiplier.get(method, 1.0)

def _get_base_cost(weight, distance):
    """根据重量和距离计算基础费用"""
    # 重量等级
    if weight < 1:
        weight_grade = 0
    elif weight < 5:
        weight_grade = 1
    elif weight < 20:
        weight_grade = 2
    else:
        weight_grade = 3
    
    # 距离等级
    if distance < 100:
        dist_grade = 0
    elif distance < 500:
        dist_grade = 1
    else:
        dist_grade = 2
    
    # 费用矩阵
    cost_matrix = [
        [5,  10, 15],   # weight < 1
        [10, 20, 30],   # weight < 5
        [20, 35, 50],   # weight < 20
        [30, 50, 80],   # weight >= 20
    ]
    
    return cost_matrix[weight_grade][dist_grade]

四、多条件组合的高级写法

4.1 使用元组和列表组合条件

# 检查多个值的有效性
def validate_coordinates(x, y, z):
    """检查三维坐标是否在有效范围内"""
    # 所有坐标必须满足范围要求
    if all(0 <= v <= 100 for v in (x, y, z)):
        return True, "坐标有效"
    else:
        invalid = [name for name, v in [("x", x), ("y", y), ("z", z)] 
                   if not (0 <= v <= 100)]
        return False, f"无效的坐标: {', '.join(invalid)}"

# 检查用户信息完整性
def is_profile_complete(profile):
    required_fields = ["name", "email", "phone", "address"]
    optional_fields = ["age", "gender", "city"]
    
    # 所有必填字段都存在且非空
    has_required = all(profile.get(f) for f in required_fields)
    # 至少有一个可选字段
    has_optional = any(profile.get(f) for f in optional_fields)
    
    return has_required and has_optional

4.2 使用集合进行条件匹配

# ❌ 多个or条件
def is_special_status(status):
    return (status == "vip" or status == "admin" or 
            status == "moderator" or status == "partner")

# ✅ 使用集合
SPECIAL_STATUSES = frozenset({"vip", "admin", "moderator", "partner"})
def is_special_status_v2(status):
    return status in SPECIAL_STATUSES

# 集合支持子集关系判断
def has_all_permissions(user_perms, required_perms):
    """用户是否拥有所有必需的权限"""
    return required_perms.issubset(user_perms)
    # 或: return required_perms <= user_perms

user_permissions = {"read", "write", "delete", "admin"}
print(has_all_permissions(user_permissions, {"read", "write"}))       # True
print(has_all_permissions(user_permissions, {"read", "export"}))     # False

def has_any_permission(user_perms, allowed_perms):
    """用户是否拥有任意一个允许的权限"""
    return bool(user_perms & allowed_perms)
    # 或: return not user_perms.isdisjoint(allowed_perms)

4.3 使用函数式编程组合条件

# 条件函数的组合
def compose_conditions(*conditions):
    """组合多个条件函数:全部满足"""
    def composed(*args, **kwargs):
        return all(cond(*args, **kwargs) for cond in conditions)
    return composed

def any_condition(*conditions):
    """组合多个条件函数:任一满足"""
    def composed(*args, **kwargs):
        return any(cond(*args, **kwargs) for cond in conditions)
    return composed

# 使用
def is_long(s):
    return len(s) > 5

def has_number(s):
    return any(c.isdigit() for c in s)

def has_upper(s):
    return any(c.isupper() for c in s)

# 组合条件
is_strong_password = compose_conditions(is_long, has_number, has_upper)

passwords = ["abc", "abcdef", "abc123", "ABC123", "Abc123!"]
for pwd in passwords:
    result = "✅" if is_strong_password(pwd) else "❌"
    print(f"{result} {pwd}")

五、嵌套的合理使用场景

并非所有嵌套都是坏的,有些场景嵌套是自然且合理的:

5.1 场景一:分类-子分类

# 这种嵌套是合理的——大类下再细分
def classify_animal(animal_type, features):
    if animal_type == "mammal":
        if features.get("flies"):
            return "蝙蝠"
        elif features.get("aquatic"):
            return "鲸鱼"
        elif features.get("has_pouch"):
            return "袋鼠"
        else:
            return "常见哺乳动物"
    elif animal_type == "bird":
        if features.get("cannot_fly"):
            return "鸵鸟"
        elif features.get("aquatic"):
            return "企鹅"
        else:
            return "常见鸟类"
    else:
        return "其他动物"

5.2 场景二:验证后再处理

# 先验证合法性,再按类型处理
def process_file(filename):
    # 第一层:验证
    if not filename:
        return "文件名不能为空"
    
    ext = filename.rsplit('.', 1)[-1].lower() if '.' in filename else ''
    
    if ext not in ('txt', 'csv', 'json', 'xml'):
        return f"不支持的文件格式: {ext}"
    
    # 第二层:按类型处理(已验证合法,嵌套自然)
    if ext == 'txt':
        return process_text_file(filename)
    elif ext == 'csv':
        return process_csv_file(filename)
    elif ext == 'json':
        return process_json_file(filename)
    elif ext == 'xml':
        return process_xml_file(filename)

六、实战案例

6.1 用户注册验证器

class RegistrationValidator:
    """用户注册验证器 —— 展示如何用组合替代嵌套"""
    
    def __init__(self):
        self.rules = []
    
    def add_rule(self, rule_func, error_message):
        """添加验证规则"""
        self.rules.append((rule_func, error_message))
        return self  # 支持链式调用
    
    def validate(self, data):
        """验证数据,返回所有错误"""
        errors = []
        for rule_func, error_msg in self.rules:
            if not rule_func(data):
                errors.append(error_msg)
        return errors
    
    def is_valid(self, data):
        return len(self.validate(data)) == 0

# 定义规则函数
def username_rule(data):
    name = data.get("username", "")
    return 3 <= len(name) <= 20 and name.isalnum()

def password_rule(data):
    pwd = data.get("password", "")
    return len(pwd) >= 8

def password_match_rule(data):
    return data.get("password") == data.get("confirm_password")

def email_rule(data):
    email = data.get("email", "")
    return "@" in email and "." in email.split("@")[-1]

def age_rule(data):
    age = data.get("age", 0)
    return 0 <= age <= 150

# 构建验证器
validator = (RegistrationValidator()
    .add_rule(username_rule, "用户名需3-20个字符,仅限字母数字")
    .add_rule(password_rule, "密码至少8个字符")
    .add_rule(password_match_rule, "两次密码不一致")
    .add_rule(email_rule, "邮箱格式不正确")
    .add_rule(age_rule, "年龄超出有效范围")
)

# 测试
test_data = {
    "username": "ab",           # 太短
    "password": "123",          # 太短
    "confirm_password": "1234", # 不匹配
    "email": "invalid",         # 格式错误
    "age": 200,                 # 超范围
}

errors = validator.validate(test_data)
if errors:
    print("❌ 验证失败:")
    for i, error in enumerate(errors, 1):
        print(f"  {i}. {error}")
else:
    print("✅ 验证通过")

6.2 电商促销规则引擎

from datetime import datetime, timedelta

class PromotionEngine:
    """促销规则引擎 —— 复杂条件的组合与嵌套"""
    
    def __init__(self):
        self.promotions = []
    
    def add_promotion(self, name, condition_func, calculate_func, priority=0):
        self.promotions.append({
            "name": name,
            "condition": condition_func,
            "calculate": calculate_func,
            "priority": priority,
        })
        # 按优先级排序(优先级高的先匹配)
        self.promotions.sort(key=lambda p: -p["priority"])
    
    def get_best_promotion(self, cart, user):
        """获取最佳促销方案"""
        for promo in self.promotions:
            if promo["condition"](cart, user):
                discount = promo["calculate"](cart, user)
                return {
                    "name": promo["name"],
                    "discount": discount,
                    "final_amount": cart["total"] - discount,
                }
        # 没有任何促销
        return {"name": "无促销", "discount": 0, "final_amount": cart["total"]}

# 定义促销条件
def is_new_user(cart, user):
    return user.get("days_since_register", 365) <= 30

def is_vip(cart, user):
    return user.get("level") == "vip"

def is_big_spender(cart, user):
    return cart["total"] >= 1000

def is_holiday_season(cart, user):
    now = datetime.now()
    # 11月-1月为节假日季节
    return now.month in (11, 12, 1)

def has_specific_items(cart, user):
    """购物车包含特定品类商品"""
    target_categories = {"电子产品", "图书"}
    cart_categories = {item["category"] for item in cart.get("items", [])}
    return bool(cart_categories & target_categories)

# 构建促销引擎
engine = PromotionEngine()

# 添加促销规则
engine.add_promotion(
    name="VIP大额满减",
    condition=lambda c, u: is_vip(c, u) and is_big_spender(c, u),
    calculate=lambda c, u: min(c["total"] * 0.2, 500),  # 8折,最高减500
    priority=10,
)

engine.add_promotion(
    name="新人首单优惠",
    condition=lambda c, u: is_new_user(c, u) and c["total"] >= 100,
    calculate=lambda c, u: 50,  # 减50元
    priority=9,
)

engine.add_promotion(
    name="节假日大促",
    condition=lambda c, u: is_holiday_season(c, u) and c["total"] >= 200,
    calculate=lambda c, u: c["total"] * 0.15,  # 85折
    priority=8,
)

engine.add_promotion(
    name="满减优惠",
    condition=lambda c, u: c["total"] >= 300,
    calculate=lambda c, u: 30 if c["total"] < 500 else (60 if c["total"] < 1000 else 100),
    priority=5,
)

# 测试
cart = {
    "total": 1200,
    "items": [
        {"name": "笔记本电脑", "category": "电子产品", "price": 1000},
        {"name": "Python编程书", "category": "图书", "price": 200},
    ]
}

users = [
    {"name": "新VIP用户", "level": "vip", "days_since_register": 15},
    {"name": "普通老用户", "level": "normal", "days_since_register": 365},
    {"name": "VIP老用户", "level": "vip", "days_since_register": 500},
]

for user in users:
    result = engine.get_best_promotion(cart, user)
    print(f"\n用户: {user['name']}")
    print(f"  购物车总额: ¥{cart['total']}")
    print(f"  匹配促销: {result['name']}")
    print(f"  优惠金额: ¥{result['discount']}")
    print(f"  最终支付: ¥{result['final_amount']}")

七、本章小结

本文我们学习了条件判断嵌套与多条件组合的所有关键知识:

嵌套的基本概念:if中放if,但要注意else的匹配规则(就近原则)和缩进的一致性。

解救嵌套四大技法

多条件组合技巧:元组/列表配合all()/any()、集合的成员检查和子集判断、函数式条件组合。

嵌套的合理场景:分类-子分类结构、验证后再处理的流程——这些场景的嵌套是自然且清晰的。

实战案例:用户注册验证器(规则模式)、促销规则引擎(条件函数+优先级排序)——展示了真实项目中复杂条件的优雅处理方式。

一个检验代码质量的好问题是:"一个新加入团队的成员能在5分钟内理解这段条件的逻辑吗?"如果答案是否定的,就该重构了。⌨️ 下一篇文章,我们将学习三元表达式——如何用一行代码优雅地替代简单的if-else!

以上就是Python基础指南之条件判断嵌套的写法及优化技巧的详细内容,更多关于Python条件判断嵌套的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文