Python入门到精通(二):掌握控制流与数据结构,轻松写出优雅代码

引言

今天我们来深入探讨Python的控制流和数据结构。这些知识是编程的骨架,掌握好它们,你就能写出更强大、更优雅的代码!

一、条件判断进阶:不只是if-else

条件判断多使用if进行判定。在Python 3.10中引入了match语句,让复杂的条件判断变得简洁优雅。
match语句接受一个表达式并把它的值与一个或多个 case 块给出的一系列模式进行比较。类似于C、Java 或 JavaScript中的 switch-case语句。

# 传统方式:多层if-elif
def handle_http_status(code):
	if code == 200:
		return "成功"
	elif code == 404:
		return "未找到"
	elif code == 500:
		return "服务器错误"
	else:
		return "未知状态"

# match函数
def http_error(status):
	match status: #开启判断
		case 400:
			return "Bad request"
		case 404:
			return "Not found"
		case 418:
			return "I'm a teapot"
		case _: #通配符
			return "Something's wrong with the internet"

你可以用 | (“或”)将多个字面值组合到一个模式中。

case 401 | 403 | 404:
    return "Not allowed"

其他更高级的匹配模式

# 更高级的模式匹配
def process_data(data):
	match data:
		case []:
			print("空列表")
		case [x]:
			print(f"单元素列表: {x}")
		case [x, y, *rest]:
			print(f"多元素列表: 前两个是{x}, {y}, 剩余{len(rest)}个")
		case {"status": "success", "data": result}:
			print(f"成功获取数据: {result}")
		case {"status": "error", "message": msg}:
			print(f"错误: {msg}")
		case _:
			print("未知数据格式")

二、函数定义:让你的代码更模块化

定义函数使用关键字def,后跟函数名与括号内的形参列表。函数定义支持可变数量的参数。

2.1 默认值参数

为参数指定默认值是非常有用的方式。调用函数时,可以使用比定义时更少的参数。

def ask_ok(prompt, retries=4, reminder='Please try again!'):
    while True:
        reply = input(prompt)
        if reply in {'y', 'ye', 'yes'}:
            return True
        if reply in {'n', 'no', 'nop', 'nope'}:
            return False
        retries = retries - 1
        if retries < 0:
            raise ValueError('invalid user response')
        print(reminder)

ask_ok函数可以用以下方式调用:
1)只给出必选实参:ask_ok(‘Do you really want to quit?’)
2)给出一个可选实参:ask_ok(‘OK to overwrite the file?’, 2)
3)给出所有实参:ask_ok(‘OK to overwrite the file?’, 2, ‘Come on, only yes or no!’)

2.2 关键字参数

key=value形式的关键字参数,也可以用于调用函数。

def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
	pass
# 函数调用
parrot(1000)                                          # 1 个位置参数
parrot(voltage=1000)                                  # 1 个关键字参数
parrot(voltage=1000000, action='VOOOOOM')             # 2 个关键字参数
parrot(action='VOOOOOM', voltage=1000000)             # 2 个关键字参数
parrot('a million', 'bereft of life', 'jump')         # 3 个位置参数
parrot('a thousand', state='pushing up the daisies')  # 1 个位置参数,1 个关键字参数

2.3 特殊参数

一般默认情况下,参数可以按位置或显式关键字传递给 Python 函数。函数综合定义如下:其中/ 和 * 是可选的。

def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
      -----------    ----------     ----------
        |               |                |
        |          位置或关键字            |
        |                                - 仅限关键字
         -- 仅限位置

函数调用要求独立的位置参数,但实参在列表或元组里时,要执行相反的操作。
不独立的参数,则要在调用函数时,用 *操作符 把实参从列表或元组解包出来。
字典可以用 ** 操作符 传递关键字参数。

# *args:接收任意数量的位置参数
def sum_numbers(*args):
    """计算任意数量数字的和"""
    print(f"参数类型: {type(args)}")  # tuple
    return sum(args)
print(sum_numbers(1, 2, 3, 4, 5))  # 15

# **kwargs:接收任意数量的关键字参数
def create_person(**kwargs):
    """创建个人信息字典"""
    person = {
        "name": kwargs.get("name", "匿名"),
        "age": kwargs.get("age", 18),
        "city": kwargs.get("city", "未知")
    }
    # 添加其他所有参数
    person.update(kwargs)
    return person
print(create_person(name="小明", age=25, job="工程师", city="北京"))
# {'name': '小明', 'age': 25, 'city': '北京', 'job': '工程师'}

2.4 闭包和装饰器:函数的高级用法

闭包:一个能记住并访问其创建时所在作用域(即便该作用域已经执行完毕)的函数
其关键点在于函数的嵌套使用,内部函数引用外部函数的变量,外部函数可以返回内部函数本身,它允许你保存状态。

# 闭包:函数记住并访问创建时的作用域
def make_multiplier(factor):
    """创建一个乘法器函数"""
    def multiplier(x):
        return x * factor
    return multiplier
double = make_multiplier(2)
triple = make_multiplier(3)
print(double(5))  # 10
print(triple(5))  # 15

装饰器:一个用于修改或增强其他函数功能的特殊函数,而不改变被装饰函数的源代码和调用方式。
其目的是不修改原函数代码,为其添加新功能。方法是使用 @decorator_name 的语法糖,让代码非常简洁清晰。其底层实现依赖于闭包,因为它返回的包装函数需要“记住”传入的那个原始函数

# 装饰器:修改函数的行为
def timer(func):
    """计算函数执行时间的装饰器"""
    def wrapper(*args, **kwargs):
        import time
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"{func.__name__} 执行时间: {end - start:.4f}秒")
        return result
    return wrapper

@timer #此处使用方式
def slow_function():
    """模拟耗时函数"""
    import time
    time.sleep(1)
    return "完成"
print(slow_function())  # 输出执行时间并返回"完成"

三、range函数:不只是生成数字

range函数是Python中非常高效的工具,多用于循环当中。

# 基本用法
for i in range(5):
    print(i, end=" ")  # 0 1 2 3 4

# 指定起始和结束
for i in range(2, 6):
    print(i, end=" ")  # 2 3 4 5

# 指定步长
for i in range(0, 10, 2):
    print(i, end=" ")  # 0 2 4 6 8

# 逆序
for i in range(5, 0, -1):
    print(i, end=" ")  # 5 4 3 2 1

# 与enumerate结合使用
fruits = ["苹果", "香蕉", "橙子"]
for i, fruit in enumerate(fruits, start=1):
    print(f"{i}. {fruit}")
#1. 苹果
#2. 香蕉
#3. 橙子

# 用于创建列表
numbers = list(range(10))  # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
even_numbers = list(range(0, 20, 2))  # [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

四、数据结构详解:Python的"瑞士军刀"

4.1 列表list:最灵活的数据结构

定义:列表一种可变的、有序的数据结构,用于存储一组元素,这些元素可以是不同类型的对象,并且可以通过索引访问。列表使用方括号[]定义,元素之间用逗号分隔。

定义格式:[元素1, 元素2, 元素3, ...]

列表基本操作包括:创建、增删改查。
创建为直接赋值式创建和推导式创建。
增加包括尾部追加,指定位置追加以及批量扩展;
删除包括尾部移除,指定元素移除以及指定位置移除;
修改直接采用赋值式修改;
查询直接通过索引即可。

# 列表创建 & 赋值/修改
fruits = ["苹果", "香蕉", "橙子"]
numbers = [1, 2, 3, 4, 5]
mixed = [1, "hello", 3.14, True]
# 列表推导式创建(超级实用!)
squares = [x^2 for x in range(10)]  # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
even_squares = [x^2 for x in range(10) if x % 2 == 0]  # 只计算偶数的平方
matrix = [[i*j for j in range(3)] for i in range(3)]  # 二维列表

# 列表操作 - 添加
fruits.append("葡萄")          # 添加元素
fruits.insert(1, "草莓")      # 在指定位置插入
fruits.extend(["西瓜", "菠萝"]) # 扩展列表
# 列表操作 - 移除
removed = fruits.pop()        # 移除并返回最后一个元素
fruits.remove("香蕉")         # 移除指定元素
del fruits[2]  # 移除指定位置元素
# 列表操作 - 查询
print(fruits[0]) #注意索引越界会提示 list index out of range

索引:通过列表名加中括号内的数字进行索引,正数为正向获取,负数为逆向索引。
切片:通过start:end形式获取子列表,含start不含end。::step为布长获取,::-1为反转列表。

# 列表切片(非常重要!)
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(numbers[2:5])     # [2, 3, 4]  索引2到5(不含5)
print(numbers[:3])      # [0, 1, 2]   从开头到索引3
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]  反转列表

4.2 元组tuple:不可变的列表

定义:不可变的列表,使用括号()定义,元素之间用逗号分隔。
元组包括索引和切片,仅不支持修改。
常用于不可外界修改的用户数据。
而括号在运算中代表优先级,因此特制定元组时,元素后面添加逗号。

# 创建元组
point = (10, 20)
colors = ("红", "绿", "蓝")
single_element = (42,)  # 注意逗号!(42)会被认为是整数42

# 元组解包
x, y = point  # x=10, y=20
first, *rest = colors  # first="红", rest=["绿", "蓝"]

# 元组与列表转换
numbers_list = [1, 2, 3]
numbers_tuple = tuple(numbers_list)  # (1, 2, 3)
tuple_to_list = list(numbers_tuple)  # [1, 2, 3]

# !!! 元组内对象元素可扩展
t = (1, 2, 3, ['张三', '男', 28])
t[3].append("山东") #t会变成(1, 2, 3, ['张三', '男', 28, '山东'])

4.3 集合set:去重和集合运算

集合是一种无序的、可变的、不重复元素的数据结构,用于存储唯一的元素集合。集合使用花括号{}或set()函数定义,元素之间用逗号分隔。

# 定义格式:
- 非空集合:{元素1, 元素2, 元素3, ...}
- 空集合:set()  (注意:{} 是空字典)

Python中集合Set进行存储时,需要将元素数据进行哈希处理,进而存储。若存储对象不可哈希,则会出错。
可哈希对象:字符串str、元组tuple、文本、数字number、布尔值等不可变的数据类型
不可哈希对象:列表List、字典、集合Set等可变数据类型

常见的创建、运算见示例,其主常见作用快速去重

# 创建集合
unique_numbers = {1, 2, 3, 3, 2, 1}  # {1, 2, 3},自动去重
empty_set = set()                    # 空集合,注意不是{}(这是空字典)

# 添加元素
s = {1, 2, 3}
s.add(4)  # → {1, 2, 3, 4}
s.update([5, 6])  # → {1, 2, 3, 4, 5, 6}

# 删除元素
s.remove(3)  # 删除存在的元素
s.discard(10)  # 删除不存在的元素不报错

# 集合运算
A = {1, 2, 3, 4, 5}
B = {4, 5, 6, 7, 8}
print(A | B)   # 并集: {1, 2, 3, 4, 5, 6, 7, 8}
print(A & B)   # 交集: {4, 5}
print(A - B)   # 差集: {1, 2, 3}
print(A ^ B)   # 对称差集: {1, 2, 3, 6, 7, 8}

# 集合推导式
squares_set = {x**2 for x in range(10)}  # {0, 1, 4, 9, 16, 25, 36, 49, 64, 81}

# 实际应用:快速去重
words = ["apple", "banana", "apple", "orange", "banana", "apple"]
unique_words = set(words)  # {"apple", "banana", "orange"}

4.4 字典dict:键值对的威力

字典是一种可变的、无序的键值对集合,用于存储映射关系
字典使用花括号{}定义,每个元素由键和值组成,格式为键: 值,元素之间用逗号分隔。
每个元素包含一个键(key)和一个值(value),键必须是不可变类型且唯一。
注:Python 3.7+更改为有序

定义格式:{1:1,2:2,3:3, ...}

例如:
- 空字典:{}dict()
- 简单字典:{'name': '张三', 'age': 25, 'city': '北京'}
- 嵌套字典:{'person': {'name': '李四', 'age': 30}, 'scores': [85, 90, 88]}
- 混合类型:{1: 'one', 'two': 2, (1,2): 'tuple_key', 3.14: 'pi'}
- 重复键(后者覆盖前者):{'a': 1, 'a': 2}{'a': 2}

使用示例

# 创建字典
student = {"name": "小明", "age": 20, "major": "计算机"}
grades = dict([("数学", 90), ("英语", 85), ("物理", 88)])
grades.setdefault("综合", 80) #设置默认值是综合80,如果已经设置过则不生效
 
# 访问和修改
print(student["name"])          # 小明
student["age"] = 21             # 修改值
student["grade"] = "大二"       # 添加新键
# print(student["height"])      # KeyError! 键不存在时抛出异常
print(student.get("height", 175))  # 安全访问,返回默认值175

# 字典方法
keys = student.keys()           # 所有键
values = student.values()       # 所有值
items = student.items()         # 所有键值对
# 循环访问
for key, value in student.items():
    print(f"{key}: {value}")

# 字典推导式
squares_dict = {x: x**2 for x in range(5)}  # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

# 合并字典(Python 3.9+)
dict1 = {"a": 1, "b": 2}
dict2 = {"b": 3, "c": 4}
merged = dict1 | dict2  # {"a": 1, "b": 3, "c": 4},后面的覆盖前面的

五、循环技巧:让代码更高效

5.1 enumerate

enumerate是 Python 的内置函数,用于给可迭代对象(如列表、元组、字符串等)添加序号(索引),返回一个包含索引和元素的枚举对象

基本语法:enumerate(iterable, start=0)
iterable:要遍历的对象(列表、元组、字符串等)
start:索引起始值,默认为 0

示例:

fruits = ["苹果", "香蕉", "橙子"]
for index, fruit in enumerate(fruits, start=1):
    print(f"{index}. {fruit}")
# 1. 苹果
# 2. 香蕉
# 3. 橙子

5.2 zip

zip 是 Python 的内置函数,用于将多个可迭代对象"打包"成一个元组序列,按位置对应组合元素。

基本语法:zip(iterable1, iterable2, ...)
iterable1:可迭代的对象(列表、元组、字符串等)。

示例:

names = ["小明", "小红", "小刚"]
scores = [85, 92, 78]
for name, score in zip(names, scores):
    print(f"{name}: {score}分")

5.3 reversed:反向迭代

reversed 是 Python 的内置函数,用于返回一个序列的反向迭代器,将元素顺序反转。
基本语法:reversed(sequence)
示例:

for i in reversed(range(5)):
    print(i, end=" ")  # 4 3 2 1 0
#反转字符串   
s = "hello"
''.join(reversed(s))  # → "olleh"
# 反转元组
t = (1, 2, 3, 4)
tuple(reversed(t))  # → (4, 3, 2, 1)

5.4 else子句

else 在 Python 中有多种用法,不仅限于 if-else 条件判断。
for-else(循环结束后)
当循环正常完成(没有被 break 中断)时执行。

numbers = [2, 4, 6, 8, 10]
for num in numbers:
    if num % 2 != 0:  # 找到奇数
        print("找到奇数")
        break
else:
    print("全是偶数")  # 循环正常结束才会执行

while-else(循环结束后)
与 for-else 类似,循环正常结束执行。

count = 0
while count < 5:
    print(count)
    count += 1
else:
    print("循环完成")  # 当while条件为假时执行

5.5 其他

1)列表推导式替代简单循环

# 传统方式
squares = []
for i in range(10):
    squares.append(i**2)
# 推导式方式(更简洁高效)
squares = [i**2 for i in range(10)]

2)生成器表达式处理大数据

# 列表推导式(立即计算,占用内存)
big_list = [x**2 for x in range(1000000)]  # 占用大量内存
# 生成器表达式(惰性计算,节省内存)
big_generator = (x**2 for x in range(1000000))  # 几乎不占内存
for value in big_generator:
    # 处理每个值
    pass

六、高频面试题解析

问题1:Python中的可变对象和不可变对象有什么区别

参考答案:
1)可变对象可以直接修改,多个变量指向同一对象时会相互影响
2)不可变对象创建后不能修改,每次"修改"都会创建新对象
3)函数参数传递时要注意这一点,特别是默认参数

# 可变对象(mutable):列表、字典、集合
list1 = [1, 2, 3]
list2 = list1
list2.append(4)
print(list1)  # [1, 2, 3, 4]  # list1也被修改了!

# 不可变对象(immutable):数字、字符串、元组
str1 = "hello"
str2 = str1
str2 = str2 + " world"
print(str1)  # "hello"  # str1没有被修改
print(str2) # hello world 
# str2的值更改了,其id也发生了变化,不可变对象上任何"修改"操作实际上都会创建新的对象

问题2: 写一个装饰器,记录函数的执行时间

import time
import functools

def timer(func):
    """记录函数执行时间的装饰器"""
    @functools.wraps(func)  # 保留原函数的元信息
    def wrapper(*args, **kwargs):
        start_time = time.perf_counter()
        result = func(*args, **kwargs)
        end_time = time.perf_counter()
        
        print(f"{func.__name__} 执行时间: {end_time - start_time:.6f}秒")
        return result
    return wrapper

@timer
def slow_function(n):
    """模拟耗时函数"""
    time.sleep(n)
    return f"休眠了{n}秒"

# 使用
print(slow_function(1))  # 会输出执行时间
print(slow_function.__name__)  # 输出'slow_function'而不是'wrapper'

七、总结

学习要点回顾

条件判断:match语句让复杂条件更清晰
函数定义:args和*kwargs处理可变参数,装饰器增强函数功能
range函数:高效生成数字序列
数据结构:
列表:灵活可变,支持切片和推导式
元组:不可变,适合作为字典键或函数返回值
集合:去重和集合运算
字典:高效的键值对存储

编程建议

✅ 推荐做法
· 使用列表推导式替代简单循环
· 善用enumerate和zip简化代码
· 使用get()方法安全访问字典
· 用集合进行快速去重和成员检查
❌ 避免做法
· 不要在循环中修改正在迭代的集合
· 避免使用可变对象作为函数默认参数
· 不要用"+"大量拼接字符串(用join替代)

相关实战小游戏见代码篇:简单文字冒险游戏

下一期预告

下一篇我们将深入学习Python模块和输入输出,包括:如何创建和使用自己的模块、文件读写的高级技巧、JSON、CSV等格式的处理、异常处理的最佳实践、标准输入输出的高级用法。
掌握这些知识后,你就能编写更加实用、健壮的Python程序了!

如果觉得有帮助,请关注+点赞+收藏,这是对我最大的鼓励! 如有问题,请评论区留言

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序媛小王ouc

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值