多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

Python中函数的创建及调用

Python中函数的创建及调用 前言函数在 Python 里有两种诞生方式用def起名创建或用lambda匿名创建。创建之后它就是一个普通的对象——可以赋值、可以放进列表、可以当参数传给别的函数、也可以被函数返回。调用则更丰富按位置调、按关键字调、用*/**把序列或字典展开后调。很多人卡在「只会def和f()」这一步一旦看到sorted(data, keylambda kv: kv[1])或者回调式的写法就发懵。其实这些写法没有新语法只是把「函数是对象」这一件事用透了。本文把「创建」和「调用」两条线分开讲清楚再合起来给一个可运行的例子。版本提醒Python 2.7 已于 2020 年 1 月 1 日停止维护。Python 2 里print是语句print xlambda里塞不下它Python 3 里print是函数print(x)而且因为函数调用属于表达式Python 3 的lambda体里反而可以调用print。本文全按 Python 3 写。一、用 def 创建函数def创建的函数有名字、可以有多条语句、可以有文档字符串# 适用于 Python 3.8def average(numbers):返回序列的平均值空序列返回 None。if not numbers:return Nonereturn sum(numbers) / len(numbers)print(average([1, 2, 3, 4])) # 2.5print(average([])) # None创建函数的动作在执行到def那一行时发生函数体此时不执行。真正执行发生在调用时。内层也可以再def形成嵌套函数用来做辅助或工厂# 适用于 Python 3.8def make_power(exp):返回一个把输入取 exp 次幂的函数。def power(x):return x ** expreturn powersquare make_power(2)cube make_power(3)print(square(5)) # 25print(cube(3)) # 27make_power返回的就是「创建出来的函数对象」。二、用 lambda 创建匿名函数lambda用来创建没有名字的小函数语法是lambda 参数列表: 表达式。它只能是一个表达式不能写语句块# 适用于 Python 3.8add lambda x, y: x yprint(add(2, 3)) # 5print((lambda x: x * 2)(10)) # 20 ← 定义后立即调用lambda的价值通常在于「作为参数内联传入」不必为它专门起名字# 适用于 Python 3.8pairs [(b, 2), (a, 3), (c, 1)]by_second sorted(pairs, keylambda kv: kv[1])print(by_second) # [(c, 1), (b, 2), (a, 3)]by_first sorted(pairs, keylambda kv: kv[0])print(by_first) # [(a, 3), (b, 2), (c, 1)]lambda的局限要清楚不能有多个表达式、不能有赋值语句、不能有return表达式的结果本身就是返回值。复杂逻辑还是用def给函数起个名字可读性和可调试性都更好。三、创建后的函数是对象函数是对象所以下面这些操作都合法# 适用于 Python 3.8def double(x):return x * 2f double # ① 赋值给变量print(f(4)) # 8funcs [double, abs] # ② 放进容器print([g(-3) for g in funcs]) # [-6, 3]def apply(func, value): # ③ 当参数传入return func(value)print(apply(double, 5)) # 10print(apply(len, hello)) # 5把函数当参数传、或当返回值返回的函数叫高阶函数higher-order function。内置的map、filter、sorted的key都是这个用法。四、调用的形式与时机调用时决定「哪个实参给哪个形参」有四种写法。# 适用于 Python 3.8def describe(name, age, city北京):return f{name}{age} 岁来自 {city}# ① 全位置print(describe(小明, 18))# ② 位置 关键字print(describe(小明, 18, city上海))# ③ 全关键字顺序随便print(describe(age18, 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.8def 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) # 成功 42run(False, on_success, on_failure) # 失败 出错了要区分func与func()前者是「把函数本身传出去」后者是「立刻调用并传出结果」。这是回调类代码最常见的错。实战一个用函数对象做成的小工具下面把创建、高阶函数、关键字调用、解包都串起来可直接运行。# 适用于 Python 3.8def 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接收参数并原样透传体现调用侧解包与定义侧收集的配合。常见坑点把func和func()弄混❌button.on_click(handler())—— 立刻调用了handler把返回值当回调。 ✅button.on_click(handler)—— 传函数本身。位置参数写在关键字参数后面❌describe(name小明, 18)——SyntaxError。 ✅describe(小明, age18)位置参数永远在前。lambda里想写语句❌lambda x: print(x); x 1——lambda体只能是单个表达式。 ✅ 复杂逻辑用def起名只做映射/取键时才用lambda。循环里创建 lambda 导致延迟绑定❌fns [lambda: i for i in range(3)]—— 全部返回最后一个i。 ✅fns [lambda ii: i for i in range(3)]用默认参数把当前值绑定。调用时参数个数不匹配❌ 少传必需参数或把函数当对象传给str拼接报TypeError。 ✅ 对照签名传参不确定时先看help(func)显示的签名。*解包把字符串当成可变位置参数❌describe(*小明)—— 字符串被拆成一个个字符参数错位。 ✅ 把要展开的东西放进元组或列表describe(*[小明, 18])。嵌套函数返回时忘了返回内层函数❌def make(): def inner(): ...但结尾没写return inner—— 得到None。 ✅ 结尾写return inner注意不带括号。用 Python 2 的写法❌print x、把lambda当成能放语句的工具Python 2 里print是语句更写不进去。 ✅print(x)把print当函数才能放进需要函数的场合如list(map(print, items))。总结环节写法要点创建具名def name(...):多条语句、文档字符串创建匿名lambda 参数: 表达式单个表达式适合内联创建工厂函数内def后return返回的是函数对象调用位置f(1, 2)顺序对应形参调用关键字f(a1, b2)必须在位置参数之后调用解包f(*args, **kwargs)展开序列 / 字典「函数是对象所以能被创建、被传递、被延迟调用」——这句话是理解 Python 函数的钥匙。把def与lambda两种创建方式、以及位置 / 关键字 / 解包三种调用方式记熟再分清f与f()的区别回调、高阶函数这些概念自然就通了。
返回列表