首页 / 资讯中心 / 文章详情

Python函数编程核心:作用域、递归与算法实战解析

Python函数编程核心:作用域、递归与算法实战解析 ★ FEATURED ARTICLE
简介本资源为《Python编程实践》第8章练习题及解答的PDF文档作者陈波、刘慧君面向正在学习Python基础语法与函数式编程的初学者及高校学生帮助其通过配套习题巩固课堂知识、检验掌握程度。压缩包内仅含1个PDF文件大小约224KB轻量便携适合在电脑或移动设备上随时查阅。内容覆盖函数编程、递归、循环、字符串处理、数字转换、回文素数等多个主题具体包括整数各位数字之和、递归求和、十进制转十六进制/八进制/二进制、不特定多参数乘积、前100个回文素数打印以及折半查找等典型题目。每道题均给出答案与思路解析并附示例代码便于读者对照理解函数参数传递、递归调用、取模运算与素数筛选等关键知识点。目前已有1935人学习下载可作为课后自测与期末复习的实用参考。1. 变量作用域到底是怎么回事从一个“同名不同命”的困惑说起很多人第一次学 Python 函数时都遇到过这样的怪事函数里面写了一个x 77函数外面也有一个x 1调用完函数再打印外面的x它居然还是 1。明明函数里改了呀这就像你在自己家冰箱里放了一瓶可乐邻居家冰箱里也有一瓶你打开邻居家冰箱把可乐换成了雪碧回到自己家打开冰箱——可乐还在。两瓶可乐同名但住在不同的“作用域”里互不干扰。这个“同名不同命”的现象就是 Python 变量作用域最直观的体现。搞懂它你才能明白为什么有些变量函数里能用、外面不能用为什么有些函数改完值外面却“看不见”。而这份《Python编程实践》第 8 章的练习题及解答恰好把作用域、递归、进制转换、回文素数、折半查找这些函数编程的核心概念串成了一条线。它不是干巴巴地讲语法而是用一道道题逼你把“函数到底怎么跑”想清楚。接下来我们就把这些概念一个个拆开用大白话讲透。2. 函数编程的底层逻辑为什么变量会“认生”2.1 局部变量与全局变量函数里的“优先权”规则先看一个典型场景。假设有这样一个程序x 1 y 1 def func(): x 77 z y * 2 print(xy, x z) func() print(x, x) print(y, y)运行结果是xy 79然后x 1y 1。为什么函数里的x变成了 77外面的x还是 1因为 Python 在函数内部遇到赋值语句x 77时会认为x是一个局部变量它只在函数执行期间存在函数一结束就消失了。而y在函数里没有被赋值Python 就会去全局作用域找找到了外面的y 1所以z 1 * 2 2最终x z 77 2 79。通俗地说函数就像一个房间里面可以放自己的东西局部变量也可以借用外面的东西全局变量但如果你在房间里放了一个和外面同名的东西房间里的人就只认自己那个外面的完全不受影响。这就是“局部优先”原则。注意如果你在函数里只是想读取全局变量不赋值那没问题一旦赋值Python 就默认你要创建一个新的局部变量除非你用global声明。2.2 递归函数自己调用自己的“套娃”艺术第 8 章里有一道题要求计算f(i) 1/2 2/3 3/4 … i/(i1)还有一道题用递归求1 到 9 的奇数和。递归听起来玄乎其实就是函数自己调用自己把一个大问题拆成一个小一号的同类问题直到拆到最简单的情况直接给出答案。比如求奇数和def func(x): if x 1: return 1 return x func(x - 2) print(func(9))执行过程是这样的func(9)发现 9 大于 1于是返回9 func(7)func(7)又返回7 func(5)……一路拆到func(1)它直接返回 1。然后一层层往回加1 3 5 7 9 25。这就像你有一摞盘子每次拿掉两个直到只剩一个再把拿掉的盘子数加起来。递归的关键在于终止条件。没有if x 1: return 1这一句函数就会无限调用自己直到程序崩溃。所以写递归时第一件事就是想清楚什么时候停下来2.3 不定参数一个函数怎么吃下“不特定多个数”第 8 章还有一道题要求“计算一个或不特定多个数的乘积”。Python 用*temp这种写法来接收任意数量的参数def caculate(*temp): ans 1 for x in temp: ans * x return ans print(caculate(1, 2, 3, 4, 5))这里的*temp会把传入的所有参数打包成一个元组函数内部就可以像遍历列表一样遍历它。你传 3 个数temp就是(1, 2, 3)传 5 个数就是(1, 2, 3, 4, 5)。这就像你去自助餐厅拿一个盘子想装几样菜就装几样厨师函数只管把你盘子里的菜全炒了。这种写法在实际开发中很常见比如日志函数要接收任意数量的消息片段或者数学计算函数要接收任意多个操作数。它让函数的接口变得灵活不用提前规定“你必须传 3 个参数”。3. 数字转换与回文素数两个经典练习背后的思维训练3.1 进制转换为什么“除 R 取余、反序输出”能行第 8 章要求写三个函数分别把整数转成十六进制、八进制、二进制字符串。核心方法就一句话不断用 n 除以 R取余数直到 n 为 0最后把余数反着拼起来。以十进制转二进制为例假设 n 1313 % 2 1n 变成 66 % 2 0n 变成 33 % 2 1n 变成 11 % 2 1n 变成 0余数依次是 1、0、1、1反序就是 1101正好是 13 的二进制。为什么反序因为第一次算出来的是最低位最后算出来的是最高位就像你读数字是从左往右读但计算时是从右往左拆的。十六进制稍微麻烦一点因为 10 到 15 要用 A 到 F 表示。代码里用一个字典做映射Map {0:0, 1:1, ..., 10:A, 11:B, 12:C, 13:D, 14:E, 15:F}每次取余后查字典最后同样反序。这个思路不仅适用于 2、8、16 进制换成任意进制 R 都成立只要处理好大于 9 的数字怎么表示就行。3.2 回文素数两个判断条件怎么优雅地合在一起回文素数是指一个数既是素数又是回文数比如 131。第 8 章要求打印前 100 个回文素数每行 10 个。这道题把两个独立的知识点揉在了一起素数判断和回文判断。素数判断用的是筛选法从 2 开始把每个素数的倍数都标记为“非素数”剩下的就是素数。代码里用了一个字典count来记录被筛掉的数prime [] count {} for i in range(2, 100000): temp i if temp not in count: prime.append(temp) while temp 100000: count[temp] 1 temp i回文判断则是把数字转成字符串然后比较首尾字符是否对称def judge(temp): value_str str(temp) Len len(value_str) for i in range(Len // 2): if value_str[i] ! value_str[Len - 1 - i]: return False return True最后遍历素数列表用judge过滤出回文数凑够 100 个就停。这道题的价值在于它让你练习了把两个小功能组合成一个大功能而且每个小功能都写成独立的函数逻辑清晰改起来也方便。3.3 折半查找为什么“砍一半”能这么快折半查找也叫二分查找是第 8 章另一个重点。题目给了一个已按学号排序的学生列表要求用折半查找找到指定学号。核心思想是每次拿中间位置的元素和目标比较如果目标比中间小就去左半边找比中间大就去右半边找相等就找到了。def Binary_Find(Stu): bottom 0 top len(Stu) - 1 while bottom top: mid (bottom top) // 2 if num int(Stu[mid][0]): return 已找到 elif num int(Stu[mid][0]): bottom mid 1 elif num int(Stu[mid][0]): top mid - 1 return 没有找到为什么快因为每次比较都能把查找范围缩小一半。20 个学生最多比较 5 次就能确定有没有1000 个学生最多 10 次。这就像你猜一个 1 到 100 之间的数字每次猜中间值最多 7 次就能猜中。折半查找的前提是数据必须有序如果列表是乱序的这个方法就失效了。4. 常见误区与易混概念这些坑你踩过几个4.1 局部变量和全局变量同名时到底用哪个易混点函数里写x 77外面也有x 1函数里打印x是 77外面打印还是 1。很多人以为函数里的赋值会“覆盖”外面的。为什么混因为其他语言比如 C里函数内外的同名变量可能通过指针或引用互相影响但 Python 的赋值是“创建新名字”不是“修改原名字”。怎么区分记住一句话——函数内赋值即局部除非用global声明。如果你真的想在函数里修改全局变量得写global x然后x 77这样外面的x才会变。4.2 递归和循环什么时候非得用递归易混点求奇数和既可以用递归也可以用循环那为什么还要学递归为什么混因为很多问题用循环也能解决初学者觉得递归多此一举。怎么区分递归适合问题本身是嵌套结构的场景比如“分治法找假硬币”——把一堆硬币分成两半称一称假币在哪边就继续分哪边。这种“大问题拆成同类小问题”的逻辑用递归写出来几乎和自然语言一样直白。循环适合“重复做同一件事直到满足条件”比如遍历列表、累加求和。简单说结构嵌套用递归线性重复用循环。4.3 进制转换时为什么余数要反序易混点算出来的余数直接拼起来不行吗为什么非要反序为什么混因为第一次算出的余数是最低位但人读数字是从最高位读的。怎么区分拿 13 转二进制举例余数依次是 1、0、1、1如果直接拼是 1011但 1011 是 11不是 13。反序后 1101 才是 13。记住先算出来的是低位后算出来的是高位输出时要倒过来。4.4 折半查找为什么要求数据有序易混点折半查找看起来只是“每次取中间”跟数据有没有排序有什么关系为什么混因为代码里只写了比较中间值没写排序容易忽略前提。怎么区分折半查找的逻辑是“目标比中间小就去左边比中间大就去右边”这个判断成立的前提是左边所有数都比中间小右边所有数都比中间大。如果数据没排序左边可能藏着比中间大的数你的判断就错了。所以用折半查找之前必须先排序。4.5 不定参数*temp和普通参数混用时顺序怎么排易混点def func(a, *temp)和def func(*temp, a)有什么区别为什么混因为*temp看起来像个普通参数实际上它会把后面所有位置参数都吃掉。怎么区分*temp必须放在普通位置参数后面。def func(a, *temp)表示第一个参数给a剩下的全给tempdef func(*temp, a)在 Python 3 里表示a必须用关键字传参比如func(1, 2, a3)。初学阶段记住普通参数在前*temp在后就不会出错。5. 什么时候该翻出这份练习题一个“卡住时”的用法示例学 Python 函数编程最怕的不是看不懂语法而是看懂了却写不出来。这份第 8 章练习题及解答的价值恰恰在于它把“写不出来”的瞬间提前暴露给你。比如你觉得自己会递归了但让你写一个“用递归求 1 到 9 的奇数和”你可能卡在终止条件上你觉得自己会进制转换了但让你处理十六进制的 A 到 F你可能忘了做映射。那什么时候该用它我的经验是当你学完函数、递归、循环这几章感觉自己“好像会了”的时候直接翻到这份题盖住答案自己先写一遍。写不出来说明你只是“看懂了”没真正掌握写出来了但和答案不一样对比一下思路往往能发现更简洁的写法。比如回文素数那道题答案用了筛选法先把素数全找出来再过滤回文数比“逐个判断是不是素数、是不是回文”效率高得多。这种“先批量筛选、再条件过滤”的思路在以后处理大数据时非常有用。但也要注意这份题不是万能的。它偏重函数编程的基础训练不涉及面向对象、文件操作、异常处理这些后续章节的内容。如果你已经能熟练写出递归、进制转换、折半查找那这份题对你来说就偏简单了可以直接跳过去练更复杂的项目。另外题目里的代码风格偏“教学版”比如变量名用temp、ans实际开发中建议用更有意义的名字比如remainder、total。提示做题时不要只看答案的“思路”部分一定要自己动手敲一遍。很多坑比如递归忘记写终止条件、折半查找忘记排序只有亲手写才会踩到。从那以后我每次带人学 Python 函数都强制让他们先自己做一遍第 8 章的题做完再对照答案。因为“看会”和“写会”之间隔着一条河而练习题就是那座桥。希望这份资料能帮你顺利过河。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站