Python从入门到实战(九):函数对象与高阶函数

目录

一、重新认识函数

[1. 为什么函数也是对象](#1. 为什么函数也是对象)

[2. 函数添加自定义属性](#2. 函数添加自定义属性)

[3. 函数赋值给变量](#3. 函数赋值给变量)

[4. 函数作为参数](#4. 函数作为参数)

[5. 函数作为返回值](#5. 函数作为返回值)

二、函数参数进阶

[1. 多返回值](#1. 多返回值)

[2. 参数打包](#2. 参数打包)

[2.1 位置参数打包](#2.1 位置参数打包)

[2.2 关键字参数打包](#2.2 关键字参数打包)

[3. 参数解包](#3. 参数解包)

[4. 参数传递机制](#4. 参数传递机制)

三、高阶函数

[1. 为什么需要高阶函数](#1. 为什么需要高阶函数)

[2. 高阶函数案例](#2. 高阶函数案例)

[四、Lambda 表达式](#四、Lambda 表达式)

[1. Lambda 语法](#1. Lambda 语法)

[2. Lambda 应用场景](#2. Lambda 应用场景)

[3. Lambda 注意事项](#3. Lambda 注意事项)

[五、Python 常用高阶函数](#五、Python 常用高阶函数)

[1. map()](#1. map())

[2. filter()](#2. filter())

[3. sorted()](#3. sorted())

[4. reduce()](#4. reduce())

六、条件表达式

[1. 为什么需要条件表达式](#1. 为什么需要条件表达式)

[2. 语法结构](#2. 语法结构)

[3. 应用场景示例](#3. 应用场景示例)

[4. 什么时候不该使用](#4. 什么时候不该使用)

七、列表推导式

[1. 为什么需要推导式](#1. 为什么需要推导式)

[2. 基本语法](#2. 基本语法)

[3. 条件推导式](#3. 条件推导式)

[4. 多层推导式](#4. 多层推导式)

总结


一、重新认识函数

在基础语法阶段,我们通常将函数视为一段封装好的、用于执行特定任务的代码块。我们定义它,然后通过小括号调用它。这是一种典型的面向过程的思维方式

然而,要真正解锁 Python 的高级特性(如闭包、装饰器、函数式编程等),我们必须打破这种传统的认知。在 Python 的哲学里,函数不仅是代码块,更是活生生的对象


1. 为什么函数也是对象

在 Python 的世界遵循一切皆对象的核心法则。整数、字符串、列表是对象,我们自定义的类和实例是对象,连函数本身也是对象

使用 def 关键字定义一个函数时,Python 解释器在底层实际上做了两件事:

  1. 在堆内存中创建了一个类型为 function 的对象实体,里面封装了函数的执行代码、命名空间等数据

  2. 在当前的命名空间中创建了一个变量(即你的函数名),并将这个变量作为指针,指向刚刚创建的函数对象

我们可以通过内置函数 type() 和 id() 来直接证实这一物理事实:

python 复制代码
def greet(name):
    return f"Hello, {name}!"

# 1. 验证函数的类型
print(type(greet))  # 输出:<class 'function'>

# 2. 验证函数的内存地址
print(id(greet))    # 输出:一串内存地址数字

既然函数是 function 类的对象,那么它就天然具备了对象的所有特性:可以被赋值给变量、可以作为容器元素的成员、拥有自己的属性、作为参数传递,也可以作为返回值返回


2. 函数添加自定义属性

既然函数是对象,它就可以像普通对象一样,在运行期间动态地挂载自定义属性。这一特性在编写某些需要记忆状态的特殊函数时非常有用(例如实现轻量级的计数器,而无需动用全局变量或类)

python 复制代码
def regular_service():
    print("正在执行核心业务逻辑...")
    # 动态累加函数自身的属性
    regular_service.call_count += 1

# 在函数对象上初始化一个自定义属性
regular_service.call_count = 0

# 连续调用
regular_service()
regular_service()

# 外部直接读取函数对象的属性
print(f"该函数一共被调用了 {regular_service.call_count} 次。")  # 输出:2

3. 函数赋值给变量

在 Python 中,函数名本质上就是一个普通的变量名。我们可以将一个函数名赋值给另一个新变量,这仅仅意味着让新变量也指向同一个函数对象

python 复制代码
def cal_tax(income):
    return income * 0.2

# 1. 将函数对象赋值给另一个变量(注意:不要加小括号,加小括号表示调用函数)
tax_p = cal_tax

# 2. 验证两者的内存地址完全一致
print(id(cal_tax) == id(tax_p))  # 输出:True

# 3. 通过新变量调用函数
result = tax_p(5000)
print(f"应缴税额:{result}")  # 输出:1000.0

4. 函数作为参数

函数能够作为参数传递给另一个函数,是现代软件工程解耦的核心手段之一。它允许我们将执行逻辑像普通数据一样在程序中自由传递

python 复制代码
def stars(text):
    return f"*** {text} ***"

def brackets(text):
    return f"[[ {text} ]]"

# 渲染函数:接收一个文本和一个"格式化函数对象"作为参数
def render_engine(raw_text, formatter_func):
    # 面向行为编程:直接调用传入的函数对象
    processed_text = formatter_func(raw_text)
    print(f"渲染:{processed_text}")

# 传入不同的行为对象,得到不同的输出结果
render_engine("Core Data", stars)     # 输出:*** Core Data ***
render_engine("Core Data", brackets)  # 输出:[[ Core Data ]]

5. 函数作为返回值

一个函数不仅可以接收数据和行为,还可以在其内部动态生成并返回一个全新的函数对象

python 复制代码
def get_multiplier(factor):
    """根据传入的因子,动态生成并返回一个定制的乘法函数"""
    
    # 在内部定义一个局部函数
    def multiplier(number):
        return number * factor
        
    # 将这个内部函数对象作为成果返回出去
    return multiplier

# 生成一个"翻三倍"的专属函数
triple_func = get_multiplier(3)
print(triple_func(10))  # 输出:30

# 生成一个"翻十倍"的专属函数
decaple_func = get_multiplier(10)
print(decaple_func(10))  # 输出:100

二、函数参数进阶

Python 为函数提供了灵活多变的参数机制,使其能够在系统内高效运作


1. 多返回值

在很多传统的编程语言中,一个函数通常只能返回一个数据。如果需要返回多个数据,往往必须将其打包成一个复杂的结构体或对象。而 Python 允许非常直观的多返回值写法:

python 复制代码
def analyze(scores):
    highest = max(scores)
    lowest = min(scores)
    average = sum(scores) / len(scores)
    
    # 返回了三个数值
    return highest, lowest, average

result = analyze([88, 95, 70, 64, 100])
print(type(result))  # 输出:<class 'tuple'>
print(result)        # 输出:(100, 64, 83.4)

底层机制:

Python 在底层根本不支持真正的多返回值 。上述代码中,return highest, lowest, average 被隐式地打包成了一个单一的元组(Tuple)对象

也就是说,函数自始至终只返回了一个对象。而外部能够通过 max_v, min_v, avg_v = analyze(...) 进行接收,靠的是 Python 自动触发的元组拆包机制


2. 参数打包

在工程设计中,我们经常无法预知一个函数未来会接收到多少个参数。为了应对这种不确定性,Python 提供了参数打包机制

2.1 位置参数打包

当你在函数的形参(Parameter)前加上一个星号 * 时,这意味着该形参将接收所有未被显式捕获的位置参数,并在底层将其自动打包成一个元组

python 复制代码
def make_pizza(size, *toppings):
    print(f"正在制作一个 {size} 英寸的披萨。")
    print(f"添加的配料:{toppings},其类型是:{type(toppings)}")

# 调用时,多出来的配料参数被自动收拢到一个元组中
make_pizza(12, "香肠", "蘑菇", "青椒")
# 输出:
# 正在制作一个 12 英寸的披萨。
# 添加的配料元组:('香肠', '蘑菇', '青椒'),其类型是:<class 'tuple'>

2.2 关键字参数打包

当你在形参前加上两个星号时,这意味着该形参将接收所有未被显式捕获的关键字参数,并在底层将其自动打包成一个字典

python 复制代码
def build_profile(first, last, **user_info):
    profile = {"first_name": first, "last_name": last}
    # 直接将打包好的字典合并进来
    profile.update(user_info)
    return profile

user_data = build_profile("Albert", "Einstein", field="Physics", Nobel=True)
print(user_data)
# 输出:{'first_name': 'Albert', 'last_name': 'Einstein', 'field': 'Physics', 'Nobel': True}

3. 参数解包

参数解包是打包的逆向操作。当你手中已经拥有一个容器数据(如列表、元组或字典),而你需要将它们逐一传递给一个普通函数时,可以使用解包操作

  • * 作用于列表/元组时,会将其拆散为独立的位置参数

  • 作用于字典时,会将其拆散为独立的关键字参数对

python 复制代码
def create_sandbox(ip, port, debug_mode):
    print(f"服务器配置:IP={ip}, Port={port}, Debug={debug_mode}")

# 1. 列表位置参数解包
network_config = ["192.168.1.100", 8080, True]
create_sandbox(*network_config)  # 等价于 create_sandbox("192.168.1.100", 8080, True)

# 2. 字典关键字参数解包
kv_config = {"ip": "10.0.0.1", "port": 443, "debug_mode": False}
create_sandbox(**kv_config)      
# 等价于 create_sandbox(ip="10.0.0.1", port=443, debug_mode=False)

4. 参数传递机制

Python 对于函数参数的定义是赋值传递对象引用传递

其核心逻辑为:向函数传递一个实参时,函数内部的形参变量仅仅是拷贝了该实参的内存地址指针(即两者共享同一个堆内存对象) 。至于函数内部的操作是否会影响到函数外部,完全取决于传入对象的可变性

python 复制代码
def modify_data(num_param, list_param):
    num_param += 10          # 操作不可变对象(int)
    list_param.append(999)   # 操作可变对象(list)
    print(f"函数内部:num={num_param}, lst={list_param}")

outer_num = 5
outer_lst = [1, 2]

modify_data(outer_num, outer_lst)
# 输出:函数内部:num=15, lst=[1, 2, 999]

print(f"函数外部:outer_num={outer_num}, outer_lst={outer_lst}")
# 输出:函数外部:outer_num=5, outer_lst=[1, 2, 999]
  • 针对不可变对象:传入函数时,形参 num_param 指向 5。当执行 num_param += 10 时,由于整数不可变,Python 只好在堆内存中新开辟空间计算出 15,并将 num_param 指向这个新位置。此时外部的 outer_num 依然指向原来的 5,不受任何影响

  • 针对可变对象:传入函数时,list_param 共享了列表的物理地址。执行 list_param.append(999) 时,由于列表支持就地修改,Python 直接在原物理空间追加了元素。这就导致函数外部的 outer_lst 同步发生了改变

永远不要使用可变类型(如空列表、空字典)作为函数的默认参数(如 def func(data=\[\]))。由于默认参数在函数加载时有且仅被初始化一次,所有的调用都将共享这一个同一个物理列表,这极易引发灾难性的逻辑污染

三、高阶函数

高阶函数的定义非常简单。一个函数只要满足以下两个条件中的任意一个,它就被称为高阶函数:

  • 接受一个或多个函数作为参数传入

  • 将一个函数作为执行结果返回


1. 为什么需要高阶函数

我们的代码往往将控制流与具体算法死死地捆绑在一起

例如,你需要开发一个包含多个核心业务的方法链,架构师要求:在执行每一个业务方法前,必须记录一条安全审计日志,并测量该业务的执行耗时

如果没有高阶函数,你只能在每个独立的业务函数内部,重复地复制、粘贴日志记录与耗时测量的代码。这不仅造成了严重的冗余,也使得核心业务代码变得极难阅读。

高阶函数的工程价值在于:它允许我们将基础设施逻辑(如日志、安全校验、耗时统计、异常重试)抽象并封装在高阶函数内部,而将具体的业务逻辑作为函数对象投递进来。 从而实现横切关注点的完美分离


2. 高阶函数案例

下面通过一个 "业务耗时与审计监测网关" 案例,展示高阶函数如何对逻辑进行解密和组合

python 复制代码
import time

# 1. 业务逻辑层(不掺杂任何监控代码)
def fetch_user_data(user_id):
    """模拟从数据库拉取数据的耗时操作"""
    time.sleep(0.3)  # 模拟网络延迟
    return {"user_id": user_id, "role": "VIP", "status": "Active"}

def generate_report(data_dict):
    """模拟复杂的报表渲染计算"""
    time.sleep(0.5)  # 模拟CPU密集型计算
    return f"【报表资产】身份验证成功,用户角色为 {data_dict['role']}"

# 2. 基础设施层(接受函数作为参数)
def execution_monitor(target_func, *args, **kwargs):
    """
    高阶监视器。
    负责统一接管控制流,拦截目标函数的执行,注入审计与计时逻辑。
    """
    print(f"[审计系统] :检测到正在尝试调用函数 -> '{target_func.__name__}'")
    
    start_time = time.time()
    
    # 多态地执行传入的行为对象,并利用参数解包投递业务数据
    result = target_func(*args, **kwargs)
    
    end_time = time.time()
    elapsed = round((end_time - start_time) * 1000, 2)
    
    print(f"[审计系统] 函数 '{target_func.__name__}' 物理耗时: {elapsed} 毫秒。\n")
    
    # 将业务函数的正果安全交付出去
    return result

运行验证:

python 复制代码
# 传统的调用方式需要将监控代码塞进函数内。
# 现在,我们直接将业务行为与数据一并投递给高阶监视器:

# 监控业务一:拉取数据
user_info = execution_monitor(fetch_user_data, user_id=9527)

# 监控业务二:生成报表
report_text = execution_monitor(generate_report, user_info)

print(f"最终流水线输出结果:{report_text}")

结果输出:

通过这个案例可以清晰地看到,fetch_user_data 和 generate_report 不需要知道任何关于审计或计时器的存在。高阶函数 execution_monitor 承担了逻辑排列的职责

四、Lambda 表达式

如果很多辅助性的逻辑非常简单,如果每用一次都去声明一个函数,不仅会写出大量只有一两行代码的一次性垃圾函数,还会严重污染系统的命名空间

为了解决这种高频、轻量级行为的就地投递问题,Python 引入了 Lambda 表达式 ,也就是通常所说的匿名函数


1. Lambda 语法

Lambda 表达式本质上是一种用于定义轻量级函数的语法糖。它没有函数名,也没有普通的函数声明结构。其标准的语法非常精简:

  • Lambda 关键字:用于声明这是一个匿名函数

  • arguments (参数列表) :函数的输入参数,支持多个参数,用逗号 , 隔开。语法规则与普通函数的参数列表完全一致,但不需要加括号

  • 冒号:分隔符

  • expression (单一表达式) :函数的执行逻辑本体。核心限制:只能有一条表达式,不能包含复杂的控制语句。计算结果会自动作为函数的返回值返回,不能写 return 关键字

def 声明与 lambda 表达式

python 复制代码
# 1. 传统 def 声明
def add(x, y):
    return x + y

# 2. 等价的 lambda 表达式
add_lambda = lambda x, y: x + y

print(add(5, 3))         # 输出:8
print(add_lambda(5, 3))  # 输出:8
print(type(add_lambda))  # 输出:<class 'function'> -> 证明它在底层依然是一个纯粹的函数对象

2. Lambda 应用场景

Lambda 表达式真正用法,是与高阶函数结合,作为就地临时消费的行为参数直接嵌入到调用中

最经典的场景之一就是自定义排序逻辑。内置的 sorted() 函数或列表的 .sort() 方法是一个标准的高阶函数,它接收一个 key 参数,该参数要求传入一个函数对象,用来决定排序的依据:

python 复制代码
# 一组存储员工数据的字典列表
employees = [
    {"name": "Alice", "age": 30, "salary": 9000},
    {"name": "Bob", "age": 25, "salary": 12000},
    {"name": "Charlie", "age": 35, "salary": 8000}
]

# 业务需求:按照员工的薪资由低到高进行精准排序
# 利用 lambda 表达式一行流搞定,直接提取出薪资字段作为排序键
sorted_by_salary = sorted(employees, key=lambda emp: emp["salary"])

print(sorted_by_salary)
# 输出:[
#   {'name': 'Charlie', 'age': 35, 'salary': 8000}, 
#   {'name': 'Alice', 'age': 30, 'salary': 9000}, 
#   {'name': 'Bob', 'age': 25, 'salary': 12000}
# ]

如果没有 Lambda 表达式,你必须在外部额外定义一个多余函数,代码的紧凑型和直观度会大打折扣


3. Lambda 注意事项

Lambda 表达式虽然简洁优雅,但在实际开发中需要谨慎把握使用尺度,否则非常容易导致代码可读性下降

保持单一表达式

Lambda 在设计之初就是为了处理一眼就能看懂的超轻量级算法。如果一行内堆叠了多层复杂的条件判断,应当立刻重构为标准的函数

python 复制代码
# 严重的错误示范:在 lambda 中通过嵌套三元表达式塞入复杂逻辑
complex_func = lambda x: "A" if x > 90 else ("B" if x > 80 else ("C" if x > 60 else "D"))

# 正确示范:写 def 函数
def evaluate_grade(score):
    if score > 90: return "A"
    if score > 80: return "B"
    if score > 60: return "C"
    return "D"

PEP 8 规范

Python 官方的 PEP 8 代码规范指出:不要显式地将一个 Lambda 表达式赋值给一个变量

如果你需要给一个逻辑命名以供复用,那就应该使用 def 关键字。强行给 Lambda 表达式命名,不仅丧失了匿名的灵活性,还会降低调试时的体验

python 复制代码
# 不推荐:强行给匿名函数命名
square = lambda x: x ** 2

#  推荐:直接使用 def 声明
def square(x):
    return x ** 2

在程序崩溃抛出异常时,使用 def 声明的函数会明确指出是哪个函数名发生了错误;而如果使用命名 Lambda,异常栈只会提示 <lambda> 报错,这会给大规模系统中的 Bug 定位带来极大的困扰

五、Python 常用高阶函数

Python 内置了四个强大的高阶函数:map()、filter()、sorted() 和 reduce()。熟练运用这些工具,可以让你以简洁高效方式优雅地处理大规模数据,实现清洗、过滤和聚合操作


1. map()

map(function, iterable) 是一种数据转换方法。它接受一个转换函数和一个可迭代对象,自动遍历该对象的所有元素,将函数依次应用到每个元素上,最终生成一个新的转换结果序列

python 复制代码
# 将一组用户注册邮箱统一转换为小写
raw_emails = [" Alice@Gmail.com ", "BOB@outlook.COM ", " charlie@QQ.COM"]

# 使用 lambda 快速清洗字符串去除空格并转为小写
clean_emails_iter = map(lambda email: email.strip().lower(), raw_emails)

# 转换为列表输出
print(list(clean_emails_iter))
# 输出:['alice@gmail.com', 'bob@outlook.com', 'charlie@qq.com']

2. filter()

filter(function, iterable) 用于对数据进行筛选过滤。它接受一个返回布尔值的判定函数和一个可迭代对象,保留所有判定为 True 的元素,过滤掉判定为 False 的元素

python 复制代码
# 从一组商品库存数据中,筛选出所有库存低于10的商品数量
stock_levels = [150, 4, 88, 0, 12, 9, 200]

danger_stocks = filter(lambda stock: stock < 10, stock_levels)

print(list(danger_stocks))  # 输出:[4, 0, 9]

3. sorted()

sorted(iterable, key=None, reverse=False)的核心在于高阶参数 key。通过向 key 注入一个转换函数,我们能够打破传统的 "按数值大小排序" 的局限,实现按任意自定义规则进行多维排序

python 复制代码
# 一组待处理的航线数据
flights = ["PEK-SIN", "HND-LAX", "PVG-LHR"]

# 终点站是字符串切片后的后三位 [-3:]
sorted_flights = sorted(flights, key=lambda flight: flight[-3:])

print(sorted_flights)
# 输出:['HND-LAX', 'PVG-LHR', 'PEK-SIN']  (按 LAX, LHR, SIN 字母表升序排序)

4. reduce()

reduce(function, iterable, initializer) 是唯一被移出内置全局命名空间的函数。因为 Python 创始人 Guido 认为它过于隐晦,所以在 Python 3 中将其收拢到了 functools 模块内

reduce 的功能是对可迭代对象进行累积计算。其计算过程如下:首先将前两个元素传入函数并计算结果;然后将该结果作为新参数,与下一个元素再次传入函数。如此迭代处理所有元素,最终返回一个累积计算值

python 复制代码
from functools import reduce

# 业务需求:计算 1 到 5 的阶乘结果(即 1*2*3*4*5)
numbers = [1, 2, 3, 4, 5]

# lambda x, y 中,x 承载的是上一次的累积成果,y 是本次新数据
factorial_result = reduce(lambda x, y: x * y, numbers)

print(factorial_result)  # 输出:120

六、条件表达式

Lambda 表达式的内部只能包含单一的表达式,不能塞入如 if-else 这样的控制语句。然而,在实际开发中,根据某个条件为变量赋予不同的值是最常见的业务需求

为了打破多行分支的代码冗余,Python 引入了条件表达式 ,在其他编程语言中,它通常被称为三元运算符


1. 为什么需要条件表达式

假设我们需要根据用户的权限等级,返回其可享受的折扣率:

python 复制代码
# 传统的命令式写法
def get_discount_legacy(is_vip):
    if is_vip:
        discount = 0.8
    else:
        discount = 1.0
    return discount

这段代码虽然清晰,但为了一个简单的二选一逻辑,大张旗鼓地动用了 5 行代码,并在多个分支中重复对 discount 变量进行赋值。这不仅显得臃肿,也打断了声明式代码的流畅度。

条件表达式则允许我们以表达式的形式,将这一判定逻辑浓缩进一行代码中


2. 语法结构

  • condition(判定条件):一个返回布尔值的表达式

  • value_if_true:当前面的 condition 判定为 True 时,表达式返回的值

  • value_if_false:当前面的 condition 判定为 False 时,表达式返回的值

短路求值

Python 在执行条件表达式时会首先计算中间的 condition。如果为真,则只去计算并返回 value_if_true,右侧的 value_if_false 会被完全忽略;反之亦然。这种机制确保了不会触发多余的计算开销


3. 应用场景示例

变量的高效赋值

python 复制代码
status_code = 200

# 一行流完成状态文本的生成
message = "请求成功" if status_code == 200 else "服务器异常"
print(message)  # 输出:请求成功

Lambda 表达式中的使用

这是条件表达式最核心的使用。有了它,匿名函数就具备了处理二选一逻辑的能力:

python 复制代码
# 业务需求:给一组数据进行边界安全限制,若数字大于 100 则截断为 100,否则保持原样
clip_signal = lambda x: 100 if x > 100 else x

print(clip_signal(150)) # 输出:100
print(clip_signal(45))  # 输出:45

在返回语句中使用

python 复制代码
def check_permission(user_role):
    # 直接在 return 时进行契约判定
    return "Access Granted" if user_role in ("Admin", "Root") else "Access Denied"

print(check_permission("Guest"))  # 输出:Access Denied

4. 什么时候不该使用

条件表达式是一把锋利的双刃剑,它的使用原则只有一个:可读性

严禁嵌套

当业务出现了三选一或更多分支时,有些开发者会写出如下嵌套的条件表达式:

python 复制代码
score = 85

# 恶劣的示范:嵌套三元
grade = "A" if score >= 90 else ("B" if score >= 80 else ("C" if score >= 60 else "D"))

这种代码严重违背了 Python 设计原则。面对多分支,应当回归到标准的 if-elif-else 结构中

严禁混淆语句与表达式

条件表达式的初衷是为了 "计算并返回一个值",而不是为了 "执行一段动作"

python 复制代码
#  反面模式:在条件表达式里执行 print (极度不推荐)
print("VIP") if is_vip else print("Normal")

七、列表推导式

列表推导式堪称 Python 最具特色的语法特性之一。它将传统的循环遍历、条件筛选、数据转换高度浓缩在方括号内部。它不仅能让代码变得更加精简,其底层在 CPython 解释器中还经过了专门的底层优化,执行效率通常显著高于手写的 for 循环


1. 为什么需要推导式

在传统命令式编程中,创建新列表往往需要遵循 "声明空列表→构建循环→处理逻辑→手动追加" 这一冗长流程:

python 复制代码
# 传统的命令式循环
squares = []
for x in range(1, 6):
    squares.append(x ** 2)

这段代码关注的是 "怎么做(How)" 的步骤。而列表推导式则让我们直接切换到声明式的思维轨道,直接定义 "我要一个什么样的数据集(What)"


2. 基本语法

列表推导式最基本的语法如下:

  • iterable:数据源(如列表、元组、字符串、range 等)

  • for item in:循环驱动,负责遍历数据源中的元素

  • expression(核心表达式):针对每一个拨出来的元素,计算出一个结果,并将其插入新的列表中

ruby 复制代码
# 列表推导式一行流实现数字平方
squares = [x ** 2 for x in range(1, 6)]
print(squares)  # 输出:[1, 4, 9, 16, 25]

3. 条件推导式

推导式真正强大的地方在于它可以与条件判定融合。根据 if 关键字所在的位置不同,它展现出两种不同的语义:

if 放在尾部

当 if 在整条推导式的最右侧时,它扮演的是filter() 的拦截角色。只有满足条件的元素,才会被放行投递到最左侧的表达式中

python 复制代码
# 从一组设备温度中,只提取出所有"超标温度"(大于 40)
temperatures = [23, 45, 12, 60, 38, 92]

hot_signals = [t for t in temperatures if t > 40]
print(hot_signals)  # 输出:[45, 60, 92]

if-else 放在首部

在需要遍历过程中实现二选一状态转换时,可将条件表达式作为推导式最左侧的处理环节

python 复制代码
# 对一批日志状态码进行分类标记(200为"OK",其余一律为"ERR")
status_codes = [200, 404, 200, 500]

labels = ["OK" if code == 200 else "ERR" for code in status_codes]
print(labels)  # 输出:['OK', 'ERR', 'OK', 'ERR']

4. 多层推导式

列表推导式甚至支持嵌套多个 for 循环,这在处理二维列表(矩阵)时非常利落。其嵌套书写顺序与多层 for 循环的顺序完全一致

python 复制代码
# 将一个二维矩阵扁平化为一维列表
matrix = [
    [1, 2, 3],
    [4, 5, 6]
]

# 拆解逻辑:for row in matrix(外层循环)-> for val in row(内层循环)
flattened = [val for row in matrix for val in row]
print(flattened)  # 输出:[1, 2, 3, 4, 5, 6]

多层推导式最多只能接受 2 层循环嵌套。如果超过 2 层,代码的可读性会发生灾难性的崩塌,此时必须立刻回归到传统的多行 for 循环中

总结

本章从 "函数也是对象" 这一核心思想出发,介绍了函数赋值、函数作为参数和返回值、高阶函数、Lambda 表达式、列表推导式以及 map()、filter()、sorted()、reduce() 等常用函数式编程工具。通过这些内容,我们不仅掌握了更加简洁、灵活的代码编写方式,也进一步理解了 Python 区别于许多传统编程语言的重要特性

下一篇文章我们将继续深入函数进阶内容,学习作用域、闭包与装饰器。这些特性广泛应用于日志记录、权限控制、性能统计等实际开发场景,也是理解 Python 高级编程思想的重要基础

相关推荐
技术民工之路1 小时前
Python Socket编程零基础实战教程(TCP/UDP通信完整版)
python·tcp/ip·udp
戮漠summer1 小时前
Missing Semester 计算机教育中缺失的一课 Lecture 01 Shell
开发语言·后端·scala
jinyishu_1 小时前
C++ string使用方法
开发语言·c++
EW Frontier1 小时前
三级跳突破864维动作空间——QMIX-Hierarchical多无人机协同通信方法全解析【附python代码】
开发语言·python·无人机·强化学习·通信资源分配
名字还没想好☜1 小时前
Next.js 中间件实战:鉴权、重定向与 A/B 分流
开发语言·前端·javascript·中间件·react·next.js
乐观勇敢坚强的老彭2 小时前
C++浮点数使用注意事项
开发语言·c++
wuyk5552 小时前
38.什么是C语言?从语法到工程落地的工程师视角解读
c语言·开发语言·stm32·单片机·mcu·51单片机
库克克2 小时前
【C++ 】内联函数
java·开发语言·c++
zh_xuan2 小时前
java 虚拟线程
java·开发语言·虚拟线程