十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Python编程进阶:流程控制、函数与数据结构核心语法详解

Python编程进阶:流程控制、函数与数据结构核心语法详解 1. 项目概述从“能跑”到“写好”上一期我们聊了Python的变量、数据类型和基础运算符算是把盖房子的砖瓦备齐了。这一期我们得聊聊怎么用这些砖瓦砌墙、搭结构了。如果说基础语法一是让你写的代码“能跑”那么基础语法二的目标就是让你写的代码“能跑得好”逻辑清晰、结构合理、易于维护。这涉及到程序流程的控制、代码的复用与组织是区分“写脚本”和“做开发”的关键一步。很多新手在学完变量和运算符后会迫不及待地想写个“大程序”结果往往写成一团乱麻的“面条代码”调试起来痛苦不堪。究其原因就是跳过了流程控制和函数这些构建程序骨架的核心语法。本次内容我们将深入Python的三大流程控制结构顺序、分支、循环并重点攻克函数这个核心概念。掌握这些你才算是真正拿到了用Python构建复杂应用的入门券。无论你未来是想做数据分析、Web开发还是自动化脚本这里的每一个知识点都是你每天都会用到的“肌肉记忆”。2. 核心语法解析构建程序的逻辑骨架2.1 顺序结构代码的执行基线顺序结构是最基础、最直观的执行方式即代码按照从上到下的顺序一行一行地执行。这听起来简单但却是所有复杂逻辑的基石。在Python中没有特殊关键字来标识顺序结构它就是解释器的默认行为。理解顺序结构的关键在于理解程序的“执行流”。例如# 顺序结构示例 name “张三” age 25 print(f“大家好我是{name}今年{age}岁。”)解释器会先执行第一行将字符串“张三”赋值给变量name接着执行第二行将整数25赋值给变量age最后执行第三行打印出格式化后的字符串。这个顺序是不可逆的你不能指望在赋值之前就打印出变量的值。注意虽然顺序是默认的但代码的书写顺序直接影响可读性。良好的习惯是先导入模块再定义常量或配置接着是函数定义最后是主程序逻辑。这种结构清晰的顺序能让他人包括未来的你快速理解你的代码意图。2.2 分支结构让程序学会“思考”程序不能总是直线执行它需要根据不同的条件做出不同的反应这就是分支结构也称为选择结构。Python中使用if、elif、else关键字来实现。2.2.1 单分支与双分支最基本的if语句用于判断一个条件是否成立。score 85 if score 60: print(“恭喜考试及格”)如果条件score 60为True则执行缩进块内的打印语句如果为False则跳过。当我们需要处理“是”与“否”两种情况时使用if...else...。score 55 if score 60: print(“恭喜考试及格”) else: print(“很遗憾需要补考。”)2.2.2 多分支判断当条件超过两种时就需要if...elif...else...链。elif是 “else if” 的缩写可以无限叠加。score 82 if score 90: grade “A” elif score 80: grade “B” elif score 70: grade “C” elif score 60: grade “D” else: grade “F” print(f“你的成绩等级是{grade}”)这里有一个关键细节elif的判断是顺序进行的且一旦某个条件为真其后的elif和else都将被跳过。因此条件的顺序非常重要。如果把score 60放在最前面那么只要分数大于60无论多高都只会得到“D”。2.2.3 分支结构的嵌套分支结构内部可以再包含分支结构形成嵌套。这用于处理更复杂的、分层级的条件判断。weather “晴天” temperature 28 if weather “晴天”: if temperature 30: print(“天气炎热建议室内活动。”) elif temperature 20: print(“天气晴好适宜户外运动。”) else: print(“天气晴但较冷注意保暖。”) else: print(“非晴天出行请带伞。”)嵌套虽然强大但不宜过深通常不建议超过3层否则代码会变得难以理解和调试。过深的嵌套是“代码坏味道”的一种通常可以通过提取函数或优化逻辑来“拍平”。2.3 循环结构自动化重复的利器循环用于重复执行某段代码块直到满足特定条件。Python提供了两种主要的循环语句while循环和for循环。2.3.1 while循环当条件满足时while循环在给定条件为真时重复执行语句块。它适用于循环次数不确定的场景。count 0 while count 5: print(f“这是第 {count 1} 次循环”) count 1 # 千万别忘了改变循环条件否则会陷入死循环while循环的核心是“条件测试”。在每次循环开始前Python都会评估条件表达式。如果为True则执行循环体如果为False则终止循环继续执行后续代码。死循环与主动控制如果条件永远为真如while True:循环将无限进行形成死循环。这在服务器监听、事件循环等场景中有用但通常需要配合break语句在特定条件下跳出。忘记更新循环条件如上面的count 1是新手最常见的错误之一会导致程序卡死。2.3.2 for循环遍历已知序列for循环用于遍历任何可迭代对象如列表、字符串、字典、元组等并对其中每个元素执行代码块。它更适用于循环次数确定的场景。fruits [“apple”, “banana”, “cherry”] for fruit in fruits: print(f“我喜欢吃 {fruit}”)for循环的语法非常清晰for 临时变量 in 可迭代对象:。每次循环临时变量都会被自动赋值为序列中的下一个元素直到遍历完所有元素。与range()函数搭档当我们需要执行特定次数的循环时range()函数是for循环的最佳搭档。for i in range(5): # 生成序列 0, 1, 2, 3, 4 print(i)range(start, stop, step)可以生成一个整数序列常用于循环计数。需要注意的是range(5)生成的是0到4不包含5本身这与Python中常见的“左闭右开”区间风格一致。2.3.3 循环控制语句break与continue这两个关键字用于更精细地控制循环流程。break立即终止当前所在的最内层循环并跳出循环体继续执行循环之后的代码。它像循环的“紧急停止”按钮。for num in range(10): if num 5: break print(num) # 输出0, 1, 2, 3, 4continue跳过当前循环体中剩余的语句直接进入下一轮循环。它像循环的“跳过本轮”按钮。for num in range(5): if num 2: continue print(num) # 输出0, 1, 3, 4实操心得在多层嵌套循环中break只能跳出它所在的那一层循环。如果需要从深层嵌套中直接跳出所有循环一个常见的技巧是使用一个标志变量或者将循环封装成函数利用return来退出。2.4 异常处理让程序更健壮程序运行中难免会遇到错误异常比如文件不存在、网络连接失败、除零错误等。如果不对异常进行处理程序会崩溃并打印难懂的Traceback信息。Python使用try...except...else...finally语句块来捕获和处理异常。2.4.1 基本异常捕获try: result 10 / 0 print(“这行不会被执行”) except ZeroDivisionError: print(“错误除数不能为零”)将可能出错的代码放在try块中。如果在执行try块时发生了ZeroDivisionError异常则立即跳转到对应的except块执行程序不会崩溃。2.4.2 捕获多种异常及获取异常信息可以指定多个except块来捕获不同类型的异常也可以用一个except捕获所有异常但不推荐因为会隐藏未知错误。try: num int(input(“请输入一个整数: “)) result 10 / num except ValueError: print(“输入的不是有效整数”) except ZeroDivisionError: print(“除数不能为零”) except Exception as e: # 捕获其他所有异常e是异常对象 print(f“发生了未知错误: {e}”) else: print(f“计算成功结果是{result}”) # 仅当try块无异常时执行 finally: print(“这段代码无论是否发生异常都会执行。”) # 常用于清理资源如关闭文件else子句是可选的它在try块没有引发异常时执行非常适合放置那些依赖于try块成功执行的代码。finally子句则无论是否发生异常都会执行是进行资源清理关闭文件、断开连接的黄金位置。重要原则异常处理应该“具体而非宽泛”。永远不要用一个空的except:或者except Exception:来忽略所有错误这会让你对程序中的Bug视而不见。只捕获你预料到并知道如何处理的异常。3. 函数深度剖析代码复用的艺术如果说流程控制语句决定了程序的执行路径那么函数就决定了程序的模块化结构。函数是一段组织好的、可重复使用的、用来实现单一或相关联功能的代码块。3.1 函数的定义与调用定义函数使用def关键字。def greet(name): “”“向指定的人打招呼”“” # 文档字符串用于说明函数用途 message f“Hello, {name}!” return messagedef: 定义函数的关键字。greet: 函数名应遵循蛇形命名法snake_case且见名知义。(name): 参数列表可以接收外部传入的数据这里是形式参数形参。“”“...”“”: 文档字符串强烈建议为每个函数编写它可以通过help(greet)查看。return message: 使用return语句将结果返回给调用者。如果没有return函数默认返回None。调用函数非常简单使用函数名加括号即可括号内传入实际参数实参。result greet(“Alice”) # 调用函数传入实参“Alice” print(result) # 输出Hello, Alice!3.2 参数传递的多种方式Python的函数参数非常灵活主要有以下几种形式3.2.1 位置参数这是最常见的方式实参的顺序必须和形参的顺序一一对应。def describe_pet(animal_type, pet_name): print(f“I have a {animal_type} named {pet_name}.”) describe_pet(“hamster”, “Harry”) # 正确 describe_pet(“Harry”, “hamster”) # 逻辑错误顺序不对3.2.2 关键字参数在调用函数时使用形参名实参值的方式传递参数。这种方式无需记住参数顺序且提高了代码可读性。describe_pet(pet_name“Harry”, animal_type“hamster”) # 顺序不影响结果3.2.3 默认参数在定义函数时可以为形参指定一个默认值。调用时如果未提供该参数则使用默认值。def describe_pet(pet_name, animal_type“dog”): # animal_type 默认为 ‘dog’ print(f“I have a {animal_type} named {pet_name}.”) describe_pet(“Willie”) # 输出I have a dog named Willie. describe_pet(“Harry”, “hamster”) # 输出I have a hamster named Harry.注意默认参数必须指向不可变对象如数字、字符串、元组。如果指向可变对象如列表、字典可能会产生意料之外的行为因为默认值在函数定义时就被计算并存储多次调用共享同一个可变对象。**3.2.4 可变参数*args 和kwargs有时你无法预先确定函数需要接收多少个参数Python提供了两种可变参数机制。*args: 接收任意数量的位置参数并将其打包成一个元组。def make_pizza(*toppings): print(“Making a pizza with the following toppings:“) for topping in toppings: print(f“- {topping}”) make_pizza(“pepperoni”) make_pizza(“mushrooms”, “green peppers”, “extra cheese”)**kwargs: 接收任意数量的关键字参数并将其打包成一个字典。def build_profile(first, last, **user_info): profile {} profile[‘first_name’] first profile[‘last_name’] last for key, value in user_info.items(): profile[key] value return profile user_profile build_profile(‘albert’, ‘einstein’, location‘princeton’, field‘physics’) print(user_profile)在函数内部user_info就是一个包含所有额外关键字参数的字典。3.3 变量作用域局部与全局理解变量在何处可用作用域至关重要它能避免很多难以调试的Bug。局部变量在函数内部定义的变量其作用域仅限于该函数内部。函数执行完毕后局部变量会被销毁。全局变量在函数外部、模块顶层定义的变量其作用域在整个模块内都有效。global_var “I am global” # 全局变量 def test_scope(): local_var “I am local” # 局部变量 print(global_var) # 可以在函数内访问全局变量 print(local_var) # 可以访问局部变量 test_scope() print(global_var) # 可以访问全局变量 # print(local_var) # 错误无法在函数外访问局部变量在函数内修改全局变量如果需要在函数内部修改全局变量必须使用global关键字进行声明。count 0 def increment(): global count # 声明 count 是全局变量 count 1 increment() print(count) # 输出1不加global声明count 1这行会被解释为创建一个新的局部变量count然后对其加1而外部的全局count不变这通常会导致UnboundLocalError。最佳实践尽量减少使用全局变量。全局变量破坏了函数的封装性使得函数的行为依赖于外部状态难以测试和理解。优先考虑通过参数传递数据通过返回值输出结果。3.4 lambda表达式匿名函数lambda关键字用于创建小巧的匿名函数。它只是一个表达式而不是一个代码块因此能写的逻辑有限但非常适合需要函数对象作为参数的场景。# 传统函数定义 def square(x): return x ** 2 # lambda 表达式实现同样功能 square_lambda lambda x: x ** 2 print(square(5)) # 输出25 print(square_lambda(5)) # 输出25lambda函数通常用于高阶函数如sorted(),map(),filter()中作为简短的键函数或判断函数。# 按字符串长度排序 words [“apple”, “banana”, “cherry”, “date”] sorted_words sorted(words, keylambda word: len(word)) print(sorted_words) # 输出[‘date’, ‘apple’, ‘cherry’, ‘banana’] # 使用 map 对列表中每个元素求平方 numbers [1, 2, 3, 4] squared list(map(lambda x: x**2, numbers)) print(squared) # 输出[1, 4, 9, 16]lambda函数虽然简洁但不应过度使用。如果逻辑超过一行或者需要清晰的文档说明还是应该使用普通的def来定义函数。4. 数据结构进阶列表、字典与集合操作掌握了流程和函数我们还需要更强大的工具来组织数据。Python内置的列表、字典和集合配合其丰富的操作方法是处理数据的核心容器。4.1 列表List的增删改查与推导式列表是可变的有序序列功能极其强大。4.1.1 核心操作fruits [“apple”, “banana”] # 增 fruits.append(“orange”) # 末尾添加: [‘apple‘, ‘banana‘, ‘orange‘] fruits.insert(1, “grape”) # 指定位置插入: [‘apple‘, ‘grape‘, ‘banana‘, ‘orange‘] # 删 removed fruits.pop() # 删除并返回末尾元素: ‘orange‘, fruits变为[‘apple‘, ‘grape‘, ‘banana‘] fruits.remove(“banana”) # 删除第一个匹配项: [‘apple‘, ‘grape‘] # del fruits[0] # 删除指定索引元素 # 改 fruits[0] “kiwi” # 直接赋值修改: [‘kiwi‘, ‘grape‘] # 查 if “grape” in fruits: # 成员检查 print(“Found grape!”) index fruits.index(“grape”) # 查找索引找不到会引发ValueError4.1.2 列表切片Slicing切片是Python中非常优雅且高效的操作用于获取列表的子集。numbers [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] print(numbers[2:5]) # 输出[2, 3, 4] (索引2到5不包含5) print(numbers[:4]) # 输出[0, 1, 2, 3] (从头开始到索引4) print(numbers[5:]) # 输出[5, 6, 7, 8, 9] (从索引5到末尾) print(numbers[::2]) # 输出[0, 2, 4, 6, 8] (步长为2) print(numbers[::-1]) # 输出[9, 8, 7, 6, 5, 4, 3, 2, 1, 0] (反转列表)切片语法[start:stop:step]创建了一个新的列表不会修改原列表。4.1.3 列表推导式List Comprehension这是Python的语法糖能用一行代码简洁地生成新列表其效率和可读性通常高于传统的for循环。# 生成1到10的平方列表 squares [x**2 for x in range(1, 11)] print(squares) # 输出[1, 4, 9, 16, 25, 36, 49, 64, 81, 100] # 带条件的推导式只保留偶数平方 even_squares [x**2 for x in range(1, 11) if x % 2 0] print(even_squares) # 输出[4, 16, 36, 64, 100] # 嵌套循环的推导式 matrix [[1, 2], [3, 4], [5, 6]] flattened [num for row in matrix for num in row] print(flattened) # 输出[1, 2, 3, 4, 5, 6]推导式虽然强大但也要避免过度使用导致一行代码过长影响可读性。4.2 字典Dict的妙用与遍历字典是无序的键值对集合通过键来快速访问值速度极快。4.2.1 基本操作与安全访问student {“name”: “张三”, “age”: 20, “major”: “Computer Science”} # 增/改 student[“grade”] “A” # 键不存在则添加存在则修改 # 查 print(student[“name”]) # 输出张三 # 如果键不存在直接访问会引发KeyError # print(student[“height”]) # KeyError! # 安全访问方法 height student.get(“height”) # 键不存在返回None height_safe student.get(“height”, 175) # 键不存在返回默认值175 # 使用 setdefault 在键不存在时设置默认值并返回 city student.setdefault(“city”, “Beijing”) # city不存在设置并返回“Beijing”4.2.2 遍历字典有三种主要方式遍历字典person {“name”: “Alice”, “age”: 30, “city”: “New York”} # 遍历所有键 for key in person.keys(): print(key) # 遍历所有值 for value in person.values(): print(value) # 遍历所有键值对最常用 for key, value in person.items(): print(f“{key}: {value}”)4.2.3 字典推导式与列表推导式类似用于快速生成字典。# 将列表元素与其索引组成字典 words [“hello”, “world”, “python”] word_dict {index: word for index, word in enumerate(words)} print(word_dict) # 输出{0: ‘hello‘, 1: ‘world‘, 2: ‘python‘} # 基于现有字典创建新字典 original {‘a’: 1, ‘b’: 2, ‘c’: 3} squared_dict {k: v**2 for k, v in original.items()} print(squared_dict) # 输出{‘a’: 1, ‘b’: 4, ‘c’: 9}4.3 集合Set与元组Tuple集合是无序且元素唯一的容器主要用于成员测试、消除重复元素以及集合运算并集、交集、差集。set_a {1, 2, 3, 4, 5} set_b {4, 5, 6, 7, 8} print(set_a | set_b) # 并集: {1, 2, 3, 4, 5, 6, 7, 8} print(set_a set_b) # 交集: {4, 5} print(set_a - set_b) # 差集 (在a中但不在b中): {1, 2, 3} # 快速去重 duplicate_list [1, 2, 2, 3, 4, 4, 5] unique_list list(set(duplicate_list)) # 注意结果顺序可能改变元组是不可变的有序序列。一旦创建其元素不能被修改、添加或删除。它常用于保证数据不被意外更改以及作为字典的键因为键必须是不可变的。coordinates (10, 20) # coordinates[0] 5 # 错误元组不可变 # 单元素元组需要一个逗号否则会被认为是普通括号 single_element_tuple (42,)5. 综合实战与常见问题排查理论学得再多不如动手写一段。我们通过一个综合小项目来串联本期知识点并总结新手最容易踩的坑。5.1 实战简易学生成绩管理系统我们来设计一个命令行下的简易学生成绩管理程序它需要实现添加学生、查询成绩、计算平均分等功能。# 定义一个全局的学生列表每个学生是一个字典 students [] def add_student(): “”“添加学生信息”“” name input(“请输入学生姓名: “).strip() # 简单的输入验证 if not name: print(“姓名不能为空”) return try: score float(input(“请输入学生成绩 (0-100): “)) if not 0 score 100: print(“成绩必须在0到100之间”) return except ValueError: print(“请输入有效的数字”) return student {“name”: name, “score”: score} students.append(student) print(f“学生 {name} 的成绩 {score} 已添加成功。”) def query_student(): “”“查询学生成绩”“” name input(“请输入要查询的学生姓名: “).strip() for student in students: if student[“name”] name: print(f“学生 {name} 的成绩是 {student[‘score’]}.”) return print(f“未找到名为 {name} 的学生。”) def calculate_average(): “”“计算平均成绩”“” if not students: print(“暂无学生数据无法计算平均分。”) return # 使用列表推导式提取所有成绩 scores [stu[“score”] for stu in students] average_score sum(scores) / len(scores) print(f“当前共有 {len(students)} 名学生平均成绩为{average_score:.2f}”) # 保留两位小数 def show_menu(): “”“显示主菜单”“” print(“\n 简易学生成绩管理系统 “) print(“1. 添加学生成绩”) print(“2. 查询学生成绩”) print(“3. 计算平均成绩”) print(“4. 显示所有学生”) print(“5. 退出系统”) def main(): “”“主程序循环”“” while True: show_menu() choice input(“请选择操作 (1-5): “).strip() if choice “1”: add_student() elif choice “2”: query_student() elif choice “3”: calculate_average() elif choice “4”: if students: print(“\n所有学生信息“) for stu in students: print(f“ 姓名{stu[‘name’]} 成绩{stu[‘score’]}”) else: print(“暂无学生数据。”) elif choice “5”: print(“感谢使用再见”) break else: print(“输入无效请重新选择”) # 程序入口 if __name__ “__main__”: main()这个项目虽然简单但涵盖了输入输出、分支判断、循环控制、列表和字典操作、函数定义与调用等多个核心知识点。if __name__ “__main__”:这行代码是一个常见惯用法它确保当此脚本被直接运行时main()函数才会被执行如果此脚本被作为模块导入到其他文件main()不会自动运行。5.2 常见问题与排查技巧实录在实际编码中你几乎一定会遇到下面这些问题。这里我把自己踩过的坑和解决方法记录下来。问题1IndentationError缩进错误现象代码运行时提示IndentationError: unexpected indent或IndentationError: expected an indented block。原因Python使用缩进来定义代码块混用空格和Tab键或者缩进层级不一致会导致此错误。解决统一使用4个空格进行缩进这是PEP 8官方建议绝大多数现代编辑器如VSCode, PyCharm都可以自动将Tab转换为4个空格。检查编辑器是否显示了不可见字符确保没有混用。检查if,else,for,def,class等语句后面的冒号:以及其下方代码块的缩进是否正确。问题2UnboundLocalError局部变量在赋值前被引用现象在函数内部对一个变量先进行读取操作然后再赋值会报错UnboundLocalError: local variable ‘x‘ referenced before assignment。原因Python在函数内发现对一个变量有赋值操作就认为这个变量是局部变量。但在赋值之前去读取它此时局部变量还未创建。示例与解决count 0 # 全局变量 def increment(): print(count) # 错误Python认为count是局部变量但此时未赋值 count 1 # 正确做法使用 global 声明或者通过参数传递 def increment_correct(): global count print(count) count 1问题3修改了列表但迭代时出现意外结果现象在for循环中直接对正在遍历的列表进行增删操作可能导致漏掉元素或无限循环。示例numbers [1, 2, 3, 4, 5] for num in numbers: if num % 2 0: numbers.remove(num) # 危险操作 print(numbers) # 输出可能是 [1, 3, 5]但也可能是 [1, 3, 4, 5]漏删了4解决遍历列表的副本或者创建新列表。# 方法一遍历副本 for num in numbers[:]: # 使用切片创建副本 if num % 2 0: numbers.remove(num) # 方法二使用列表推导式创建新列表推荐 numbers [num for num in numbers if num % 2 ! 0]问题4可变对象作为函数默认参数现象函数多次调用后默认参数的值似乎“记住”了上一次的修改。示例def add_item(item, my_list[]): # 默认参数是空列表 my_list.append(item) return my_list print(add_item(‘a‘)) # 输出[‘a‘] print(add_item(‘b‘)) # 期望是[‘b‘]实际输出[‘a‘, ‘b‘]原因默认参数my_list[]在函数定义时就被求值并绑定到函数对象上。后续所有调用如果没有提供my_list参数都会共享这同一个列表对象。解决使用None作为默认值在函数内部进行判断和初始化。def add_item_fixed(item, my_listNone): if my_list is None: my_list [] my_list.append(item) return my_list问题5与is的区别混淆比较两个对象的值是否相等。is比较两个对象的内存地址是否相同即是否是同一个对象。示例a [1, 2, 3] b [1, 2, 3] c a print(a b) # True值相等 print(a is b) # False不是同一个对象 print(a is c) # Truec是a的引用是同一个对象对于小整数通常为-5到256和短字符串Python会进行缓存优化驻留is比较可能为True但这属于实现细节不应依赖。在绝大多数情况下比较值用检查是否为None时用is因为None是单例对象。掌握了这些流程控制、函数和核心数据结构你已经具备了用Python构建小型程序脚本的完整能力。下一期我们将进入面向对象编程OOP的世界学习如何用类和对象来组织更庞大、更复杂的代码那是构建中型乃至大型项目的基石。在进入OOP之前务必把本期的代码多敲几遍理解每一个细节尝试修改和扩展上面的学生管理系统比如增加成绩排序、文件存储等功能这是巩固知识最好的方式。编程没有捷径唯手熟尔。
返回列表