news 2026/10/23 5:17:13

Python中函数的创建及调用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python中函数的创建及调用

前言


函数在 Python 里有两种诞生方式:用def起名创建,或用lambda匿名创建。创建之后,它就是一个普通的对象——可以赋值、可以放进列表、可以当参数传给别的函数、也可以被函数返回。调用则更丰富:按位置调、按关键字调、用*/**把序列或字典展开后调。


很多人卡在「只会def和f()」这一步,一旦看到sorted(data, key=lambda kv: kv[1])或者回调式的写法就发懵。其实这些写法没有新语法,只是把「函数是对象」这一件事用透了。本文把「创建」和「调用」两条线分开讲清楚,再合起来给一个可运行的例子。


版本提醒:Python 2.7 已于 2020 年 1 月 1 日停止维护。Python 2 里print是语句(print x),lambda里塞不下它;Python 3 里print是函数(print(x)),而且因为函数调用属于表达式,Python 3 的lambda体里反而可以调用print。本文全按 Python 3 写。


一、用 def 创建函数


def创建的函数有名字、可以有多条语句、可以有文档字符串:


# 适用于 Python 3.8+
def average(numbers):
"""返回序列的平均值;空序列返回 None。"""
if not numbers:
return None
return sum(numbers) / len(numbers)

print(average([1, 2, 3, 4])) # 2.5
print(average([])) # None

创建函数的动作在执行到def那一行时发生,函数体此时不执行。真正执行发生在调用时。


内层也可以再def,形成嵌套函数,用来做辅助或工厂:


# 适用于 Python 3.8+
def make_power(exp):
"""返回一个把输入取 exp 次幂的函数。"""
def power(x):
return x ** exp
return power

square = make_power(2)
cube = make_power(3)
print(square(5)) # 25
print(cube(3)) # 27

make_power返回的就是「创建出来的函数对象」。


二、用 lambda 创建匿名函数


lambda用来创建没有名字的小函数,语法是lambda 参数列表: 表达式。它只能是一个表达式,不能写语句块:


# 适用于 Python 3.8+
add = lambda x, y: x + y
print(add(2, 3)) # 5

print((lambda x: x * 2)(10)) # 20 ← 定义后立即调用

lambda的价值通常在于「作为参数内联传入」,不必为它专门起名字:


# 适用于 Python 3.8+
pairs = [("b", 2), ("a", 3), ("c", 1)]
by_second = sorted(pairs, key=lambda kv: kv[1])
print(by_second) # [('c', 1), ('b', 2), ('a', 3)]
by_first = sorted(pairs, key=lambda kv: kv[0])
print(by_first) # [('a', 3), ('b', 2), ('c', 1)]

lambda的局限要清楚:不能有多个表达式、不能有赋值语句、不能有return(表达式的结果本身就是返回值)。复杂逻辑还是用def,给函数起个名字,可读性和可调试性都更好。


三、创建后的函数是对象


函数是对象,所以下面这些操作都合法:


# 适用于 Python 3.8+
def double(x):
return x * 2

f = double # ① 赋值给变量
print(f(4)) # 8

funcs = [double, abs] # ② 放进容器
print([g(-3) for g in funcs]) # [-6, 3]

def apply(func, value): # ③ 当参数传入
return func(value)

print(apply(double, 5)) # 10
print(apply(len, "hello")) # 5

把函数当参数传、或当返回值返回的函数,叫高阶函数(higher-order function)。内置的map、filter、sorted的key都是这个用法。


四、调用的形式与时机


调用时决定「哪个实参给哪个形参」,有四种写法。


# 适用于 Python 3.8+
def describe(name, age, city="北京"):
return f"{name},{age} 岁,来自 {city}"

# ① 全位置
print(describe("小明", 18))
# ② 位置 + 关键字
print(describe("小明", 18, city="上海"))
# ③ 全关键字(顺序随便)
print(describe(age=18, name="小明", city="广州"))
# ④ 用 * 和 ** 解包
args = ("小红", 20)
kwargs = {"city": "深圳"}
print(describe(*args, **kwargs))

规则只有一条要记牢:位置参数必须在关键字参数之前。describe(name="小明", 18)会直接SyntaxError。


调用时可用的参数数量必须和签名匹配:少了必需参数报TypeError: missing required positional argument,多了报TypeError: takes N positional arguments but M were given。


写f()就是立即调用;把f(不带括号)传出去,则在别人决定的时候调用,这叫延迟调用或回调(callback)。


# 适用于 Python 3.8+
def on_success(value):
print("成功:", value)

def on_failure(msg):
print("失败:", msg)

def run(ok, on_ok, on_err):
if ok:
on_ok(42) # 在这里才调用
else:
on_err("出错了")

run(True, on_success, on_failure) # 成功: 42
run(False, on_success, on_failure) # 失败: 出错了

要区分func与func():前者是「把函数本身传出去」,后者是「立刻调用并传出结果」。这是回调类代码最常见的错。


实战:一个用函数对象做成的小工具


下面把创建、高阶函数、关键字调用、解包都串起来,可直接运行。


# 适用于 Python 3.8+
def build_operators():
"""创建一组运算函数并放进字典。"""
return {
"add": lambda a, b: a + b,
"sub": lambda a, b: a - b,
"mul": lambda a, b: a * b,
"max": lambda a, b: max(a, b),
}

def apply_all(operators, a, b):
"""对同一组输入逐个调用运算符函数,返回结果字典。"""
return {name: fn(a, b) for name, fn in operators.items()}

def repeat_call(func, times, *args, **kwargs):
"""把 func 用同样的参数调用 times 次,返回结果列表。"""
return [func(*args, **kwargs) for _ in range(times)]

if __name__ == "__main__":
ops = build_operators()
print(apply_all(ops, 6, 3))
# {'add': 9, 'sub': 3, 'mul': 18, 'max': 6}

print(ops["add"](10, 5)) # 15 ← 从字典取函数再调用
print(repeat_call(ops["mul"], 3, 2, 5)) # [10, 10, 10]

要点:



  • build_operators里用lambda创建了四个匿名函数,装进字典,键是名字、值是函数对象。

  • apply_all遍历字典的.items(),拿到每个函数并调用——这是「函数是对象」的直接应用。

  • repeat_call用*args, **kwargs接收参数并原样透传,体现调用侧解包与定义侧收集的配合。


常见坑点



  1. 把func和func()弄混


❌button.on_click(handler())—— 立刻调用了handler,把返回值当回调。 ✅button.on_click(handler)—— 传函数本身。



  1. 位置参数写在关键字参数后面


❌describe(name="小明", 18)——SyntaxError。 ✅describe("小明", age=18),位置参数永远在前。



  1. lambda里想写语句


❌lambda x: print(x); x + 1——lambda体只能是单个表达式。 ✅ 复杂逻辑用def起名;只做映射/取键时才用lambda。



  1. 循环里创建 lambda 导致延迟绑定


❌fns = [lambda: i for i in range(3)]—— 全部返回最后一个i。 ✅fns = [lambda i=i: i for i in range(3)],用默认参数把当前值绑定。



  1. 调用时参数个数不匹配


❌ 少传必需参数或把函数当对象传给str拼接,报TypeError。 ✅ 对照签名传参;不确定时先看help(func)显示的签名。



  1. *解包把字符串当成可变位置参数


❌describe(*"小明")—— 字符串被拆成一个个字符,参数错位。 ✅ 把要展开的东西放进元组或列表:describe(*["小明", 18])。



  1. 嵌套函数返回时忘了返回内层函数


❌def make(): def inner(): ...但结尾没写return inner—— 得到None。 ✅ 结尾写return inner(注意不带括号)。



  1. 用 Python 2 的写法


❌print x、把lambda当成能放语句的工具(Python 2 里print是语句,更写不进去)。 ✅print(x);把print当函数才能放进需要函数的场合,如list(map(print, items))。


总结




环节写法要点



创建(具名)def name(...):多条语句、文档字符串

创建(匿名)lambda 参数: 表达式单个表达式,适合内联

创建(工厂)函数内def后return返回的是函数对象

调用(位置)f(1, 2)顺序对应形参

调用(关键字)f(a=1, b=2)必须在位置参数之后

调用(解包)f(*args, **kwargs)展开序列 / 字典



「函数是对象,所以能被创建、被传递、被延迟调用」——这句话是理解 Python 函数的钥匙。把def与lambda两种创建方式、以及位置 / 关键字 / 解包三种调用方式记熟,再分清f与f()的区别,回调、高阶函数这些概念自然就通了。




版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/18 2:05:58

COMSOL声学模型超声无损检测仿真:核心思路与完整建模

从事超声无损检测仿真这几年,我越来越觉得COMSOL是最适合把这个领域“讲清楚”的工具。声学模型看着门槛不高,但真要拿它复现一次超声波探伤过程、算出A扫描信号,中间涉及物理场耦合、网格、时间步长和边界吸收的讲究特别多。今天我就把COMSO…

作者头像 李华
网站建设 2026/10/18 20:58:06

PostgresML 自托管完全指南:从 APT 安装到 GPU 加速与 pgvector 实战

后端人工智能机器学习RAG向量数据库 【免费下载链接】postgresml Postgres with GPUs for ML/AI apps. 项目地址: https://gitcode.com/gh_mirrors/po/postgresml 点击查看 免费下载 PostgresML 本质上是 PostgreSQL 的一个扩展,因此自托管 PostgresML …

作者头像 李华
网站建设 2026/10/18 20:58:08

OpenEuler Shell脚本起步:核心语法与调试排错实战

刚接触 OpenEuler 的 Shell 编程时,很多人觉得脚本不过就是把几条 Linux 命令按顺序写进一个文件,然后执行一下,仅此而已。这个理解不算错,但真正动手之后你会发现,一个看起来再简单的起步脚本,里面也藏着解…

作者头像 李华
网站建设 2026/10/18 20:58:07

前缀和与哈希表:LeetCode 560题和为K的子数组最优解

LeetCode Hot 100 里的 560 题“和为 K 的子数组”,我前前后后刷了三遍,每次都有新的理解。这道题在面试和算法学习中出现的频率极高,看起来只是“数一数有多少个连续子数组的和等于 k”,可真正把它讲透的人不多。很多解法上来就丢…

作者头像 李华
网站建设 2026/10/18 23:08:23

提示词进阶实战:从拆解任务到驾驭多模态生成

不聊虚的,直接聊怎么用。这篇是提示词系列第四篇,前几篇把角色设定、上下文管理、输出约束这些基础过了一遍,这篇集中讲进阶场景:复杂任务拆解、编程场景提示词、多模态生成提示词,还有最常见的报错现场。核心就一句话…

作者头像 李华
网站建设 2026/10/18 23:08:24

Makefile 变量打印全攻略:调试构建系统必会的技巧与避坑指南

我先讲一个自己踩过的坑。有一回调试一块嵌入式板卡,交叉编译老是找不到头文件,我在网上翻了好几圈帖子,最后才发现其实是 Makefile 里的头文件路径变量被环境变量静默覆盖了。当时要是早点把变量打出来,大概十分钟就能定位问题&a…

作者头像 李华