从零开始彻底掌握Python函数,让你的代码效率提升10倍!

从零开始彻底掌握Python函数,让你的代码效率提升10倍! 引言为什么函数是Python编程的灵魂在编程的世界里有一句话广为流传“Don‘t Repeat Yourself”不要重复自己。这不仅仅是编程界的金科玉律更是衡量代码质量的试金石。还记得你第一次写Python代码时为了打印几个星星图案不得不反复编写相同的循环代码吗今天我们就来深入探讨Python中最核心、最实用的概念——函数。掌握函数你的编程水平将迈上一个全新的台阶一、函数是什么为什么要用函数1.1 函数的本质想象一下你正在厨房做一道复杂的菜肴。如果每次做菜都要从切菜、调味到烹饪全部重新开始那将是多么低效的事情。聪明的厨师会将常用的步骤封装成“标准流程”需要时直接执行即可。Python函数正是如此它是一段带名字的代码块专门用于完成特定的任务并且可以重复使用。1.2 一个真实的痛点场景假设我们需要在控制台打印一个2行3列的星形图案pythonrow 2 while row 0: print(* * 3) row - 1如果程序需要在多个地方打印这个图案按照传统方式你可能会复制粘贴这段代码。这就产生了代码冗余——同样的逻辑散落在程序的各个角落一旦需要修改比如改成3行4列你就需要在所有地方逐一修改既耗时又容易出错。这就是函数诞生的理由二、函数的定义与调用2.1 函数的基本结构Python定义函数使用def关键字基本语法如下pythondef 函数名(参数列表): 函数说明文档可选 函数体 return 返回值可选让我们用这个语法改造上面的打印星形案例pythondef printStar(): 打印2行3列的星形图案 row 2 while row 0: print(* * 3) row - 1 # 调用函数 printStar() print(- * 20) printStar() # 再次调用代码复用输出结果text*** *** -------------------- *** ***看到了吗同样的功能现在只需要调用一次函数名就能完成代码变得简洁而优雅。2.2 函数名的命名规范函数名应该遵循以下规范动词开头函数执行的是动作所以应该用动词命名如printStar、getUserInfo小写字母开头第一个单词全部小写多单词用下划线分隔或驼峰命名Python社区推荐使用下划线分隔如print_star三、函数的参数——让函数变得灵活3.1 参数的必要性回到刚才的例子如果我们现在想打印1行4列的星形图案怎么办重新写一个函数吗那又产生了冗余聪明的做法让函数接收参数由调用者决定打印的行数和列数。pythondef printStar(row, col): 打印指定行数列数的星形图案 while row 0: print(* * col) row - 1 # 调用时传入不同的参数 printStar(2, 3) # 打印2行3列 print(- * 20) printStar(1, 4) # 打印1行4列3.2 形参与实参这里需要明确两个重要概念形参形式参数定义函数时括号内的参数如row和col它们只是占位符不占用实际内存实参实际参数调用函数时传递的具体值如2, 3和1, 4当函数被调用时实参的值会传递给形参函数内部就可以使用这些值执行操作。3.3 Python参数传递的底层原理这是很多初学者容易混淆的地方。Python中的参数传递究竟是值传递还是引用传递答案是既不是纯粹的值传递也不是纯粹的引用传递而是“传对象引用”。Python中变量实际上是对象的引用可以理解为贴在对象上的标签。当我们传递参数时传递的是引用的副本。不可变对象数字、字符串、元组pythondef changeInt(a): print(函数内改变前a的地址:, id(a)) a 10 print(函数内改变后a的地址:, id(a)) b 2 print(函数外b的地址:, id(b)) changeInt(b) print(函数外b的值:, b) # 输出2并未改变输出结果text函数外b的地址: 140711474555352 函数内改变前a的地址: 140711474555352 函数内改变后a的地址: 140711474555608 函数外b的值: 2分析a 10实际上是让a指向了一个新的整数对象10而b仍然指向原来的2。因此外部变量不会被修改。可变对象列表、字典、集合pythondef changeList(myList): myList[1] 50 print(函数内列表:, myList) print(函数内列表地址:, id(myList)) mlist [1, 2, 3] print(函数外列表地址:, id(mlist)) changeList(mlist) print(函数外列表:, mlist) # 输出[1, 50, 3]已被修改输出结果text函数外列表地址: 1546427570560 函数内列表: [1, 50, 3] 函数内列表地址: 1546427570560 函数外列表: [1, 50, 3]分析myList[1] 50修改的是列表内部的元素并没有改变列表对象的引用所以外部列表也被修改了。关键结论在函数内修改参数时如果是可变对象可以直接修改如果是不可变对象需要重新赋值但这不会影响外部变量。3.4 四种参数类型1必须参数位置参数调用函数时必须按照定义顺序传入相同数量的参数pythondef func(a, b, c): print(a, b, c) func(1, 2, 3) # 正确1传给a2传给b3传给c # func(1, 2) # 错误缺少参数2关键字参数调用时指定参数名可以无视顺序pythondef printInfo(name, age): print(f姓名:{name}, 年龄:{age}) # 以下两种调用方式等效 printInfo(name张三, age18) printInfo(age18, name张三)3默认值参数为参数提供默认值调用时可省略pythondef printInfo(name, age20): print(f姓名:{name}, 年龄:{age}) printInfo(张三) # 年龄使用默认值20 printInfo(李四, 30) # 年龄使用传入的30 printInfo(age40, name王五) # 关键字参数重要规则带默认值的参数必须放在不带默认值参数的后面否则会报语法错误。python# 错误示例 def wrong(name张三, age): # SyntaxError pass # 正确示例 def correct(age, name张三): pass4不定长参数当不确定需要传入多少个参数时使用单星号*args接收任意多个位置参数打包成元组pythondef printInfo(num, *vartuple): print(固定参数:, num) print(不定长参数:, vartuple) printInfo(70, 60, 50) # 输出 # 固定参数: 70 # 不定长参数: (60, 50) # 如果不定长参数后面还有参数必须用关键字传参 def printInfo2(num1, *vartuple, num): print(num1, vartuple, num) printInfo2(10, 20, num40) # 正确双星号**kwargs接收任意多个关键字参数打包成字典pythondef printInfo(num, **vardict): print(固定参数:, num) print(关键字参数:, vardict) printInfo(10, key120, key230) # 输出 # 固定参数: 10 # 关键字参数: {key1: 20, key2: 30}3.5 解包传参如果已经有一个列表或字典想将其作为参数传入可以使用*和**解包pythondef func(a, b, c): return a b c # 解包列表 tuple11 (1, 2, 3) print(func(*tuple11)) # 输出6 # 解包字典字典的键必须和参数名一致 dict1 {a: 1, b: 2, c: 3} print(func(**dict1)) # 输出63.6 强制参数传递方式Python 3.8引入了更精确的参数传递控制/前的参数必须使用位置传参*后的参数必须使用关键字传参pythondef f(a, b, /, c, d, *, e, f): print(a, b, c, d, e, f) f(1, 2, 3, d4, e5, f6) # 正确 # f(1, 2, c3, d4, e5, f6) # 也正确 # f(1, b2, 3, d4, e5, f6) # 错误b必须在/之前位置传参四、函数的返回值4.1 返回值的作用函数执行完成后有时需要将结果返回给调用者这就是返回值的作用。pythondef add(num1, num2): 求两个数的和 return num1 num2 result add(10, 20) print(两个数的和为:, result) # 输出304.2 return的多种形式带表达式的return返回计算结果不带表达式的return返回None没有return语句函数执行完也会返回None返回多个值多个值会被打包成元组pythondef f(a, b, c): return a, b, c, [a, b, c] print(f(1, 2, 3)) # 输出(1, 2, 3, [1, 2, 3])重要return语句会立即结束函数执行return后的代码不会执行。五、变量作用域5.1 作用域的概念Python有4种作用域按查找顺序为L (Local)→E (Enclosing)→G (Global)→B (Built-in)pythona int(2.9) # 内建作用域 b 0 # 全局作用域 def outer(): c 1 # 嵌套作用域 def inner(): d 2 # 局部作用域 print(d, c, b, a) # 从内到外查找变量 return inner in_func outer() in_func() # 输出2 1 0 25.2 全局变量与局部变量pythonsum 0 # 全局变量 def add(num1, num2): sum num1 num2 # 局部变量 print(函数内局部变量:, sum, id(sum)) return sum add(10, 20) print(函数外全局变量:, sum, id(sum)) # 输出 # 函数内局部变量: 30 140711474555608 # 函数外全局变量: 0 140711474555352核心原则在函数内部修改全局变量默认会被当作局部变量处理。5.3 global关键字如果想在函数内修改全局变量必须使用global声明pythonvar1 100 def function_a(): global var1 var1 200 print(函数内var1:, var1) print(var1) # 100 function_a() # 200 print(var1) # 200特殊情况可变类型的全局变量不声明global也可以修改其内部元素pythonlist1 [1, 2, 3] def function_a(): list1[0] -1000 # 修改列表元素 print(list1:, list1) print(list1) # [1, 2, 3] function_a() # [-1000, 2, 3] print(list1) # [-1000, 2, 3]5.4 nonlocal关键字用于修改嵌套作用域中的变量非全局pythondef function_outer(): var1 1 print(外层var1:, var1) # 1 def function_inner(): nonlocal var1 var1 200 function_inner() print(修改后var1:, var1) # 200 function_outer()六、递归函数6.1 什么是递归递归是一种编程思想函数调用自身将一个大问题分解为若干个小问题直到达到一个可立即解决的简单情况。6.2 阶乘的递归实现pythondef get_factorial(n): 计算n的阶乘 return n * get_factorial(n - 1) if n 1 else 1 print(get_factorial(5)) # 输出1206.3 递归的执行流程当调用get_factorial(5)时执行流程如下textget_factorial(5) 5 * get_factorial(4) get_factorial(4) 4 * get_factorial(3) get_factorial(3) 3 * get_factorial(2) get_factorial(2) 2 * get_factorial(1) get_factorial(1) 1然后层层返回textget_factorial(2) 2 * 1 2 get_factorial(3) 3 * 2 6 get_factorial(4) 4 * 6 24 get_factorial(5) 5 * 24 120递归的两个关键要素递归规律找出问题分解的规律结束条件必须有明确的终止条件否则会无限递归导致栈溢出七、匿名函数Lambda7.1 Lambda语法Lambda是Python中定义匿名函数的方式语法简洁pythonlambda 参数列表: 表达式等价于pythondef 函数名(参数列表): return 表达式7.2 基本使用python# 普通函数 def add(a, b): return a b # Lambda表达式 add_lambda lambda a, b: a b print(add(1, 2)) # 3 print(add_lambda(1, 2)) # 37.3 Lambda与高阶函数Lambda经常与内置高阶函数配合使用1) sorted() - 排序pythonstudents [ {name: 张三, age: 36}, {name: 李四, age: 14}, {name: 王五, age: 27} ] # 按年龄排序 sorted_students sorted(students, keylambda x: x[age]) print(sorted_students) # 输出[{name: 李四, age: 14}, {name: 王五, age: 27}, {name: 张三, age: 36}]2) map() - 映射python# 对列表每个元素平方 result map(lambda x: x * x, [0, 1, 3, 7, 9]) print(list(result)) # [0, 1, 9, 49, 81]3) filter() - 过滤python# 过滤出非负数 result filter(lambda x: x 0, [-0, -1, -3, 7, 9]) print(list(result)) # [0, 7, 9]4) reduce() - 累积pythonfrom functools import reduce # 计算1*2*3*4*5 result reduce(lambda x, y: x * y, [1, 2, 3, 4, 5]) print(result) # 120八、函数的最佳实践8.1 编写函数说明文档好的函数应该包含清晰的文档字符串pythondef adult(age18): 根据年龄判断是否成年 参数: age: 年龄默认18岁 返回: 字符串成年或未成年 return 未成年 if age 18 else 成年 # 查看函数文档 help(adult)8.2 防止函数修改列表如果不想让函数修改原始列表可以传递深拷贝pythonimport copy def multiply2(var1): var1[3].append(400) print(函数内处理后:, var1) list1 [1, 2, 3, [100, 200, 300]] print(函数外处理前:, list1) multiply2(copy.deepcopy(list1)) print(函数外处理后:, list1) # 原始列表未被修改8.3 函数注释Type HintsPython 3.x支持函数注释提高代码可读性pythondef dog(name: str, age: (1, 99), species: 狗狗的品种) - tuple: 带注释的函数 return (name, age, species) print(dog.__annotations__) # 输出{name: class str, age: (1, 99), species: 狗狗的品种, return: class tuple}九、总结通过本文的深入学习我们系统地掌握了Python函数的核心知识核心要点回顾函数的意义代码复用、提高可维护性、降低复杂度定义与调用使用def定义函数函数先定义后调用函数名应使用动词开头参数系统必须参数、关键字参数、默认值参数、不定长参数理解形参和实参的区别掌握Python的参数传递机制传对象引用返回值return结束函数并返回值可以返回多个值打包成元组无return或空return返回None变量作用域局部变量、全局变量、嵌套作用域使用global修改全局变量使用nonlocal修改嵌套变量递归函数调用自身必须包含结束条件适用于分治思想的问题Lambda表达式简洁的匿名函数常用于高阶函数map、filter、reduce、sorted最佳实践编写清晰的文档字符串合理使用类型注释注意可变对象的副作用进阶建议掌握函数之后你的Python编程能力将进入一个新的阶段。建议你多练习将日常的重复代码封装成函数深入理解作用域这是避免bug的关键善用高阶函数让代码更简洁优雅编写单元测试确保函数在各种情况下都能正确运行函数是Python程序的基本组成单元就像建筑的砖块。打好基础才能构建出稳固而优雅的程序架构。希望这篇文章能帮助你真正理解并灵活运用Python函数让你的编程之路越走越远如果你觉得这篇文章对你有帮助欢迎点赞、收藏、转发让更多人受益