Python入门学习笔记,2025最新Python详细教程,(代码示例讲解)

发布于:2025-08-12 ⋅ 阅读:(21) ⋅ 点赞:(0)

python官方文档资料

python第三方库

Python 入门学习笔记

一:简介

Python 是一种高级的、解释型、动态类型并支持面向对象编程的语言。它具有简洁易读的语法,常使用英文关键字而非繁琐的符号(例如用 and 代替 &&),从而降低了学习门槛。Python 最初由 Guido van Rossum 在 1989 年末设计,并于 1991 年发布了第一个版本。Python 拥有丰富的标准库和第三方库,在 web 开发、数据分析、人工智能等领域被广泛应用。值得注意的是,Python 2.x 系列已于 2010 年确定 2.7 为最后一个版本(目前已停止维护),学习时应使用 Python 3.x。

  • 提示:学习时建议使用 Python 官方文档和可靠教程,保持持续动手练习。
  • 交互模式:可以在终端输入 python3 进入交互式解释器,快速测试代码。
  • 命名规范:遵循 PEP8 规范,变量和函数名采用小写蛇形(snake_case),类名采用首字母大写的驼峰式(CamelCase)。常量名用全大写加下划线分隔(例如 MAX_SIZE)。
  • 调试建议:在调试代码时,可插入 print() 打印变量,或使用 IDE/调试器单步跟踪。注释和文档字符串(docstring)有助于提高代码可读性并方便调试。

二:Python 基础语法

Python 代码由一系列语句构成,每个语句通常以换行结束。下面介绍一些基础概念:

  • 字面量(Literal):字面量是源代码中直接写出的固定值。Python 支持多种字面量类型,例如整数、浮点数、复数、字符串、布尔值(True/False)以及特殊值 None。例如:

    x = 42        # 整数字面量
    y = 3.14      # 浮点数字面量
    z = 1+2j      # 复数字面量
    s = "Hello"   # 字符串字面量
    flag = True   # 布尔字面量
    n = None      # None特殊字面量,表示空值
    
  • 注释:使用 # 表示单行注释,注释内容从 # 开始直到行尾,Python 解释器会忽略它。例如:

    # 这是单行注释
    x = 10  # 这是代码后的注释
    

    Python 没有专门的多行注释符号,但可以用三重引号'''""")包围一段文字充当多行注释(严格来说,它是字符串字面量,但如果没有赋值就会被忽略)。
    提示:编写函数或类时,建议使用文档字符串来说明功能,例如:

    def add(a, b):
        """返回两个数的和"""
        return a + b
    

    根据约定,公有函数、类和模块都应包含三重引号的文档字符串。

  • 数据类型转换:Python 提供内置函数将数据从一种类型强制转换为另一种类型,例如 int(), float(), str(), bool() 等。例如:

    num_str = "123"
    num = int(num_str)    # 将字符串转换为整数,num = 123
    pi = str(3.14)        # 将浮点数转换为字符串,pi = "3.14"
    b = bool(0)           # 将数字转换为布尔,0 会被转换为 False,除了0和None以外其他的都是True
    

    常见用法还包括 list(), tuple(), set(), dict() 函数进行数据类型转换。

  • 标识符:变量名、函数名和类名等称为**标识符。标识符由字母、数字和下划线**组成,不能以数字开头,且区分大小写。例如 my_var_temp2 是合法标识符。标识符不能与 Python 的关键字同名(如 if, for, def 等)。
    提示:避免使用单字符 l, O, I 等作为变量名,因为在某些字体下与数字 1, 0 不易区分。

  • 运算符:Python 支持多种运算符,包括:

    • 算术运算符:+ (加), - (减), * (乘), / (除,结果为浮点数), // (整除), % (取余), ** (幂运算)。
    • 赋值运算符:= 及组合形式如 +=, -=, *= 等。
    • 比较运算符:==, !=, >, <, >=, <=
    • 逻辑运算符:and, or, not(注意 Python 中逻辑运算符是**关键字而非符号**)。
    • 成员运算符:in, not in检查元素是否在容器中)。
    • 身份运算符:is, is not判断对象标识是否相同)。
      例如:
    a = 10
    b = 3
    print(a + b, a ** b, a > b)    # 输出:13 1000 True
    print("py" + "thon")           # 字符串拼接,输出:python
    print(3 * "ha")               # 字符串重复,输出:hahaha
    

    提示:在 Python 3 中,/ 始终返回浮点数,若要整除请使用 //。逻辑表达式优先级可用括号明确。

  • 字符串:字符串可以用单引号 ''、双引号 "" 或三引号 ''' '''/""" """ 定义(后者可表示多行字符串)。例如:

    str1 = 'Hello'
    str2 = "Python"
    str3 = '''多行
    字符串'''
    

    字符串可以用 + 连接,用 * 重复。Python 3.6+ 引入了格式化字符串字面量(f-string),在字符串前加 f(或 F)并在 {} 内嵌入表达式。例如:

    name = "Alice"
    age = 30
    print(f"{name} 的年龄是 {age}")      # 输出:Alice 的年龄是 30
    print("{0} 的年龄是 {1}".format(name, age))
    

    以上两种方式都可以格式化输出。旧式的 % 格式化也仍然可用,但更推荐使用 str.format() 或 f-string。

  • 数据输入:使用 input(prompt) 从标准输入读取一行文本,返回**一个字符串**。例如:

    user = input("请输入用户名:")
    age_str = input("请输入年龄:")
    age = int(age_str)  # 将输入的年龄字符串转换为整数
    

    注意 input() 返回类型为字符串,需要根据需要进行类型转换。调试时可先打印输入值以检查正确性。

三:判断语句(if)

Python 使用缩进来划分代码块,判断语句格式为:

if 条件:
    语句块
elif 另一个条件:
    语句块
else:
    语句块

条件表达式结果为布尔值 (TrueFalse)。例如:

x = 5
if x > 0:
    print("正数")
elif x == 0:
    print("零")
else:
    print("负数")

在判断中,可以使用比较运算符和逻辑运算符组合复合条件。Python 中**非零数、非空字符串/列表**等被视为真(True),而 0""[]None 等视为假(False)。

注意ifelifelse 后面**必须加冒号 :,且对应的代码块需缩进(通常 4 个空格)**。缩进错误会导致 IndentationError。建议使用编辑器自动缩进或在设置中启用可视化缩进指南。pycharm中可以使用快捷键

Ctrl + Alt + L

  • 示例

    score = int(input("请输入成绩:"))
    if score >= 90:
        print("优秀")
    elif score >= 60:
        print("及格")
    else:
        print("不及格")
    

四:循环语句

Python 支持 while 循环和 for 循环:

  • while 循环:按照条件判断来重复执行代码块。例如:

    n = 0
    while n < 5:
        print(n)
        n += 1
    

    如果循环条件始终为真,则会无限循环,需要注意保证循环变量能够改变以终止循环。可以使用 break 跳出循环,continue 跳过本次continue下面的语句直接执行下一次。

  • for 循环:用于遍历序列可迭代对象。常见用法是 range 函数生成数值序列:

    for i in range(5):    # 生成 0,1,2,3,4
        print(i)
    

    range(stop) 生成从 0 到 stop-1 的整数序列;range(start, stop) 生成从 startstop-1range(start, stop, step) 可指定步长。需要注意的是,range 返回一个不可变的序列对象,可以通过 list(range(...)) 转为列表查看具体数字。

    也可以遍历列表字符串等序列:

    for ch in "Python":
        print(ch)
    for item in [1, 2, 3]:
        print(item)
    

    for 循环也支持可选的 else 分支,当循环正常完成(无 break)时会执行 else 后的代码。

提示:尽量避免使用复杂的嵌套循环或不必要的 while True 无限循环。调试循环时,可在循环体中打印循环变量检查运行情况。

五:函数(Function)

函数通过 def 关键字定义,用于封装可复用的代码。基本形式:

def 函数名(参数列表):
    """函数文档字符串,可选,用于说明功能"""
    函数体
    return 返回值  # 可选

示例:

def add(a, b):
    """返回两个数的和"""
    return a + b

result = add(3, 5)   # 调用函数,result = 8
print(add)          # 打印函数对象信息

函数在被调用时执行其中的语句,并可通过 return 返回结果。若没有显式 return,函数执行完毕后返回 None

参数与调用:调用函数时可以使用位置参数或**关键字参数**,例如 add(2, 3)add(a=2, b=3)。Python 函数支持以下几种参数形式:

  • 位置参数:按顺序传递
  • 关键字参数func(x=..., y=...) 指定参数名传递。
  • 默认参数def func(a, b=10),若调用时未提供 b,则使用默认值 10。
  • 可变参数*args 表示**接收多个位置参数为元组**kwargs 表示接收多个关键字参数为字典。一个*表示接受多余的没有使用关键字传递的参数,保存为元组类型**,两个**号表示接受多余的传递的关键字参数,按照字典进行存储.

示例:

def greet(name, msg="你好"):
    print(f"{msg}{name}!")

greet("李雷")            # 使用默认消息
greet("韩梅梅", msg="欢迎")

文档字符串:如前所述,推荐在函数内部以三重引号文档字符串第一行简要说明功能,其后可详述参数和返回值。可以使用 help(func)print(func.__doc__) 查看文档字符串。使用help(function)或者function.__doc__都可以.一个是python内置的函数,一个是类的魔法函数.

作用域函数内部定义的变量为局部变量,只在函数内部有效;函数外部的变量可在函数中通过参数或使用 global 声明访问。避免不恰当地使用全局变量,以免引起命名冲突。

六:数据容器

1. 列表(list

列表是最常用的可变序列类型,支持动态增删查改。以下方法在日常开发中非常常见。

# 假设有一个初始列表
lst = [3, 1, 4, 1, 5]
print("初始列表: ", lst)  # [3, 1, 4, 1, 5]
1.1 append(x)

在列表末尾添加一个元素 x(原地修改)。

lst.append(9)
print("append(9) 之后: ", lst)  # [3, 1, 4, 1, 5, 9]
1.2 extend(iterable)

将可迭代对象 iterable 中的所有元素依次添加到列表末尾(原地修改)。

lst.extend([2, 6, 5])
print("extend([2,6,5]) 之后: ", lst)  # [3, 1, 4, 1, 5, 9, 2, 6, 5]
1.3 insert(i, x)

在索引 i 处插入元素 x,之后原来位于 i 及之后的元素依次后移(原地修改)。

lst.insert(2, 8)  
# 在索引 2 处插入 8,原 [3,1,4,...] 中索引2对应 4,插入后 4 向后移
print("insert(2,8) 之后: ", lst)  # [3, 1, 8, 4, 1, 5, 9, 2, 6, 5]
1.4 remove(x)

删除列表中第一个值为 x 的元素,若不存在则抛出 ValueError(原地修改)。

lst.remove(1)  
# 列表中第一个元素 1 会被移除
print("remove(1) 之后: ", lst)  # [3, 8, 4, 1, 5, 9, 2, 6, 5]
1.5 pop([i])

删除并返回索引 i 处的元素(原地修改)。若不传入 i,则默认删除并返回最后一个元素;若索引越界抛出 IndexError

val_last = lst.pop()    
print("pop() 返回: ", val_last)      # 5
print("pop() 之后: ", lst)           # [3, 8, 4, 1, 5, 9, 2, 6]

val_idx3 = lst.pop(3)  
print("pop(3) 返回: ", val_idx3)     # 索引 3 处元素是 1
print("pop(3) 之后: ", lst)          # [3, 8, 4, 5, 9, 2, 6]
1.6 clear()

清空列表中的所有元素(原地修改),变成空列表。

tmp = lst.copy()
tmp.clear()
print("clear() 之后: ", tmp)  # []
1.7 index(x[, start[, end]])

返回列表中第一个值为 x 的元素索引。从 start 开始(包含),到 end 结束(不包含)。若找不到会抛出 ValueError

lst2 = [10, 20, 30, 20, 40]
idx1 = lst2.index(20)  # 第一个 20 的索引
print("lst2.index(20):", idx1)  # 1

# 可以指定查找范围
idx2 = lst2.index(20, 2, 5)  # 从索引2到索引4(不含5)查找 20
print("lst2.index(20, 2, 5):", idx2)  # 3
1.8 count(x)

返回元素 x 在列表中出现的次数(不修改列表)。

cnt = lst2.count(20)
print("lst2.count(20):", cnt)  # 2
1.9 sort(key=None, reverse=False)

对列表进行排序(原地修改)。

  • key:用于指定排序依据的函数,如 key=lambda x: x['age']
  • reverse=True 则降序,否则升序(默认)。
lst3 = [5, 2, 9, 1, 5, 6]
lst3.sort()
print("lst3.sort() 之后升序:", lst3)  # [1, 2, 5, 5, 6, 9]

lst3.sort(reverse=True)
print("lst3.sort(reverse=True) 之后降序:", lst3)  # [9, 6, 5, 5, 2, 1]

# 自定义 key,例如按绝对值排序
lst4 = [-3, 1, -2, 5, -1]
lst4.sort(key=lambda x: abs(x))
print("按绝对值排序:", lst4)  # [1, -1, -2, -3, 5]
1.10 reverse()

将列表中元素反转(原地修改,不同于 lst3[::-1] 返回一个新列表)。

lst5 = [1, 2, 3, 4]
lst5.reverse()
print("lst5.reverse():", lst5)  # [4, 3, 2, 1]
1.11 copy()

返回列表的浅拷贝(相当于 lst[:])。若列表中包含可变对象,仍然是引用相同的可变对象。

lst6 = [[1, 2], [3, 4]]
lst6_copy = lst6.copy()
lst6_copy[0][0] = 100
print("lst6:", lst6)          # [[100, 2], [3, 4]]  —— 注意,浅拷贝依然修改了原来的子列表
print("lst6_copy:", lst6_copy)  # [[100, 2], [3, 4]]

2. 元组(tuple

元组是有序、不可变的序列。由于不可变,方法相对较少,仅保留查询类操作。

tup = (10, 20, 30, 20, 40)
print("初始元组: ", tup)
2.1 count(x)

返回元素 x 在元组中出现的次数。

cnt = tup.count(20)
print("tup.count(20):", cnt)  # 2
2.2 index(x[, start[, end]])

返回元组中第一个值为 x 的元素索引,可指定起止位置,找不到抛出 ValueError

idx = tup.index(20)  # 第一个 20 出现的位置
print("tup.index(20):", idx)  # 1

# 指定范围查找
idx2 = tup.index(20, 2, 5)  
print("tup.index(20, 2, 5):", idx2)  # 3

注意:由于元组不可变,没有 append/pop/remove 等修改方法。


3. 字典(dict

字典是以“键-值”对(key-value pair)存储的无序容器,键必须是不可变类型(如字符串、数字、元组等)。以下方法在开发中经常用到。

d = {'name': 'Alice', 'age': 30, 'city': 'Beijing'}
print("初始字典: ", d)
3.1 get(key[, default])

返回键 key 对应的值。如果 key 不存在,则返回 default(若未提供 default 则返回 None),不会抛错。

print("d.get('name'):", d.get('name'))        # 'Alice'
print("d.get('salary'):", d.get('salary'))    # None
print("d.get('salary', 0):", d.get('salary', 0))  # 0
3.2 keys()

返回一个可迭代的视图(dict_keys)对象,其中包含字典的所有键。可以用 list(d.keys()) 转为列表。

print("d.keys():", d.keys())          # dict_keys(['name', 'age', 'city'])
print("转换为 list:", list(d.keys()))  # ['name', 'age', 'city']
3.3 values()

返回一个可迭代的视图(dict_values)对象,其中包含字典的所有值。

print("d.values():", d.values())         # dict_values(['Alice', 30, 'Beijing'])
print("转换为 list:", list(d.values()))   # ['Alice', 30, 'Beijing']
3.4 items()

返回一个可迭代的视图(dict_items)对象,其中包含字典的所有 (key, value) 对。可用于解包、遍历等。

print("d.items():", d.items())  
# dict_items([('name', 'Alice'), ('age', 30), ('city', 'Beijing')])

# 遍历示例
for k, v in d.items():
    print(f"{k} -> {v}")
# 输出:
# name -> Alice
# age -> 30
# city -> Beijing
3.5 update([other])

用字典 other(或可迭代的一系列 (key,value) 对)更新当前字典:如果键存在则覆盖其值;不存在则新增。返回 None(原地修改)。

d2 = {'age': 31, 'salary': 100000}
d.update(d2)
print("d.update({'age':31,'salary':100000}) 之后:", d)
# {'name': 'Alice', 'age': 31, 'city': 'Beijing', 'salary': 100000}

# 也可以传入可迭代对象
d.update([('city', 'Shanghai'), ('dept', 'IT')])
print("再 update 一次:", d)
# {'name': 'Alice', 'age': 31, 'city': 'Shanghai', 'salary': 100000, 'dept': 'IT'}
3.6 pop(key[, default])

删除并返回键 key 对应的值。如果 key 不存在且未给出 default,则抛出 KeyError;若提供 default,则返回 default

age = d.pop('age')
print("pop('age') 返回:", age)   # 31
print("pop 之后的 d:", d)       # {'name':'Alice','city':'Shanghai','salary':100000,'dept':'IT'}

# key 不存在却提供 default,不会抛错
val = d.pop('不存在的键', 'N/A')
print("pop('不存在的键','N/A') 返回:", val)  # 'N/A'
3.7 popitem()

随机删除并返回字典中的一对 (key, value)。在 Python 3.7+ 中,popitem() 默认删除最后插入的那一对(相当于“栈”的行为)。如果字典为空则抛出 KeyError

pair = d.popitem()
print("popitem() 返回:", pair)  
print("popitem 之后的 d:", d)
3.8 setdefault(key[, default])

如果 key 存在于字典中,返回其对应的值;否则插入 key: default,并返回 defaultdefault 默认为 None

d.setdefault('dept', 'HR')
print("调用 setdefault('dept','HR'):", d['dept'])  # 已经存在,不会改动,仍为 'IT'

# setdefault 插入一个新的键
val = d.setdefault('region', 'North')
print("setdefault('region','North') 返回:", val)  # 'North'
print("之后的 d:", d)
# {'name': 'Alice', 'city': 'Shanghai', 'salary': 100000, 'dept': 'IT', 'region': 'North'}
3.9 clear()

清空字典,变成空字典 {}(原地修改)。

tmp = d.copy()
tmp.clear()
print("clear() 之后:", tmp)  # {}
3.10 copy()

返回字典的浅拷贝。拷贝之后,修改拷贝或原字典不会影响对方,但若值是可变对象,仍然引用同一对象。

d3 = {'a': [1, 2], 'b': [3, 4]}
d3_copy = d3.copy()
d3_copy['a'][0] = 999
print("d3:", d3)         # {'a': [999, 2], 'b': [3, 4]}
print("d3_copy:", d3_copy)  # {'a': [999, 2], 'b': [3, 4]}

4. 集合(set)与 冰冻集合(frozenset

集合是无序、不重复的元素集合,常用于去重、数学集合运算等。frozenset 是不可变版本,常用于做字典键等。

s = {1, 2, 3}
print("初始集合 s:", s)
4.1 add(x)

将元素 x 添加到集合中,若 x 已存在则不做任何操作(原地修改)。

s.add(4)
print("add(4) 之后:", s)  # {1, 2, 3, 4}
s.add(2)  # 集合已有 2,不会发生变化
print("add(2) 之后:", s)  # {1, 2, 3, 4}
4.2 update(iterable)

用可迭代对象中的元素更新集合(相当于多次调用 add,原地修改)。

s.update([3, 5, 6])
print("update([3,5,6]) 之后:", s)  # {1, 2, 3, 4, 5, 6}
4.3 remove(x)

从集合中移除元素 x,若 x 不存在则抛出 KeyError(原地修改)。

s.remove(6)
print("remove(6) 之后:", s)  # {1, 2, 3, 4, 5}
# s.remove(10)  # KeyError: 10
4.4 discard(x)

从集合中移除元素 x,若 x 不存在则不做任何操作(原地修改)。

s.discard(5)
print("discard(5) 之后:", s)  # {1, 2, 3, 4}
s.discard(10)  # 不存在也不会报错
print("discard(10) 之后:", s)  # {1, 2, 3, 4}
4.5 pop()

随机移除并返回一个元素(原地修改)。如果集合为空则抛出 KeyError
注意:因为集合是无序的,pop() 返回的元素是随机的。

elem = s.pop()
print("pop() 返回:", elem)
print("pop() 之后:", s)
4.6 clear()

清空集合中的所有元素(原地修改),变成空集合 set()

tmp = s.copy()
tmp.clear()
print("clear() 之后:", tmp)  # set()
4.7 并集、交集、差集、对称差集

关于两个集合 AB 的常见运算:

  • A.union(B)A | B:并集,返回包含 A、B 中所有元素的新集合。
  • A.intersection(B)A & B:交集,返回同时在 A 和 B 中出现的元素的新集合。
  • A.difference(B)A - B:差集,返回在 A 中但不在 B 中的元素的新集合。
  • A.symmetric_difference(B)A ^ B:对称差集,返回在 A、B 中但不同时在二者中的元素的新集合。
A = {1, 2, 3, 4}
B = {3, 4, 5, 6}

print("A ∪ B =", A.union(B), " 等价于 ", A | B)  
# {1,2,3,4,5,6}

print("A ∩ B =", A.intersection(B), " 等价于 ", A & B)  
# {3,4}

print("A - B =", A.difference(B), " 等价于 ", A - B)  
# {1,2}

print("A △ B =", A.symmetric_difference(B), " 等价于 ", A ^ B)  
# {1,2,5,6}
4.8 子集与超集判断
  • A.issubset(B)A <= B:判断 A 是否为 B 的子集。
  • A.issuperset(B)A >= B:判断 A 是否为 B 的超集。
  • A < B:真子集(A 是 B 子集且 A ≠ B)。
  • A > B:真超集。
print("{1,2} 是否为 {1,2,3} 的子集?", {1,2}.issubset({1,2,3}))  # True
print("{1,2} < {1,2,3}?", {1,2} < {1,2,3})  # True

print("{1,2,3} 是否为 {1,2} 的超集?", {1,2,3}.issuperset({1,2}))  # True
print("{1,2,3} > {1,2}?", {1,2,3} > {1,2})  # True
4.9 frozenset(iterable)

创建一个不可变(哈希化)的集合,适合做字典键或集合元素。其方法与 set 类似,但所有“就地修改”方法都不存在。

fs = frozenset([1, 2, 3, 2])
print("frozenset:", fs)  # frozenset({1, 2, 3})
# fs.add(4)  # AttributeError: 'frozenset' object has no attribute 'add'

6. 其他与容器相关的常见方法或技巧

在开发中,除了上面列举的容器方法外,还有一些通用技巧或函数常与容器一起使用,这里一并简要列出。

6.1 列表生成式(List Comprehension)

虽然不是“方法”,但列表生成式几乎是 Python 开发中最常用的构造列表的方式。通常用于对现有列表或可迭代对象进行快速转换/过滤。

# 生成 0~9 的平方列表
squares = [i * i for i in range(10)]
print("squares:", squares)  # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

# 带条件的生成式,筛选偶数的平方
even_squares = [i * i for i in range(10) if i % 2 == 0]
print("even_squares:", even_squares)  # [0, 4, 16, 36, 64]
6.2 生成器表达式(Generator Expression)

与列表生成式类似,但返回一个生成器对象,可逐个迭代生成值,节省内存。常用于对大数据流的“惰性”处理。

gen = (i * i for i in range(10))
print("gen:", gen)  # <generator object ...>
print("取前两个值:", next(gen), next(gen))  # 0 1

# 可以用 for 循环遍历
for val in gen:
    print(val, end=" ")  # 接着输出 4,9,16,...
6.3 zip()enumerate()map()filter()
  • zip(*iterables):并行遍历多个可迭代对象,返回元组序列。
  • enumerate(iterable, start=0):将可迭代对象元素“打包”为 (index, value) 对,常用于同时需要索引和元素的场景。
  • map(func, *iterables):将 func 应用于每个元素,返回一个生成器(Python 3 中)。
  • filter(func, iterable):筛选 iterable 中使 func(item) 返回 True 的元素,返回生成器。
A = ['a', 'b', 'c']
B = [1, 2, 3]

# zip 示例
for x, y in zip(A, B):
    print(x, y)
# a 1
# b 2
# c 3

# enumerate 示例
for idx, val in enumerate(['x', 'y', 'z'], start=1):
    print(idx, val)
# 1 x
# 2 y
# 3 z

# map 示例:把字符串列表转为大写
words = ['hello', 'world']
upper_words = list(map(str.upper, words))
print("upper_words:", upper_words)  # ['HELLO', 'WORLD']

# filter 示例:筛选偶数
nums = list(range(10))
evens = list(filter(lambda x: x % 2 == 0, nums))
print("evens:", evens)  # [0, 2, 4, 6, 8]

6.4 解包(Unpacking)

对于序列或可迭代对象,可以使用“星号”进行拆包,把第一部分、最后一部分或中间部分单独提取出来。在处理函数参数、交换变量、拆分列表时非常方便。

# 交换变量
a, b = 1, 2
a, b = b, a
print("交换后 a, b:", a, b)  # 2 1

# 序列解包
numbers = [1, 2, 3, 4, 5]
first, *middle, last = numbers
print("first:", first)    # 1
print("middle:", middle)  # [2, 3, 4]
print("last:", last)      # 5

# 函数参数解包
def func(x, y, z):
    print("x:", x, "y:", y, "z:", z)

args = (10, 20, 30)
func(*args)  # 相当于 func(10,20,30)

总结

以上内容涵盖了 Python 常用内置容器listtupledictset)及 collections 模块常见的高级容器dequedefaultdictOrderedDictCounternamedtuple)的核心方法,并通过示例演示了它们的典型用法。

  • 列表 (list):动态增删查改涉及 appendextendinsertremovepopclearindexcountsortreversecopy 等。
  • 元组 (tuple):由于不可变,只保留查询类方法 countindex
  • 字典 (dict):常见 getkeysvaluesitemsupdatepoppopitemsetdefaultclearcopy
  • 集合 (set):常见 addupdateremovediscardpopclear、集合运算 (union/intersection/difference/symmetric_difference)、子集/超集判断。
  • frozenset:不可变集合,仅支持读取和集合运算,不支持增删。
  • 其他常见函数/技巧zipenumeratemapfilter,以及列表/生成器表达式、解包等,虽非“容器方法”,但在开发中与容器操作常结合使用。
  • 提示:选用合适的数据结构。例如查找列表复杂度较高,可以使用字典或集合。操作前可先打印或调试以检查数据结构正确性。

掌握并灵活运用上述方法,能大幅提升日常 Python 开发的效率与可读性。

七:函数进阶

在熟悉基本函数定义后,还可以掌握以下高级特性:

  • 返回多个值:Python 中一个函数可以通过逗号返回多个值,实质上是返回一个元组。例如:

    def get_point():
        return 10, 20
    
    x, y = get_point()
    print(x, y)  # 10 20
    
  • 参数传递方式:详述参数形式:

    • 默认值参数:def func(a, b=10)
    • 可变参数 *args允许传入任意数量的位置参数,函数内部以元组形式访问;
    • 关键字可变参数 **kwargs:允许传入任意数量的关键字参数,函数内部以字典形式访问;
    • 关键字-only 参数:在 * 之后定义的参数只能通过关键字传入(Python 3)。

    示例:

    def demo(a, b=2, *args, **kwargs):
        print(a, b, args, kwargs)
    
    demo(1, 3, 4, 5, x=9, y=8)  # a=1, b=3, args=(4,5), kwargs={'x':9,'y':8}
    
  • 匿名函数(Lambda):使用 lambda 定义简单函数,通常用于短小函数或作为参数传递。语法:lambda 参数: 表达式。例如:

    f = lambda x, y: x + y
    print(f(2, 3))  # 5
    

    lambda 函数只能写单个表达式,无法包含复杂语句。

  • 函数作为参数:Python 函数是对象,可以作为其他函数的参数或返回值。例如:

    def apply(func, arg):
        return func(arg)
    
    def square(x):
        return x * x
    
    print(apply(square, 5))        # 25
    print(apply(lambda z: z+1, 7))  # 8
    

    常见应用有内置函数 map(func, iterable)filter(func, iterable)sorted(iterable, key=func) 等,都使用函数作为参数。

  • 作用域与闭包:函数内部定义的变量默认是局部的,可以使用 global 声明访问全局变量。闭包是一种高级特性(函数返回另一个函数后者使用了前者的局部变量),对初学者可稍后深入。

提示:为提高可读性,函数体内部的代码缩进通常为 4 个空格。避免使用过长或过多的参数(超过 3~4 个就考虑用字典传参)。尽量为复杂的函数写单元测试或使用 assert 进行简单调试。

八:文件操作

Python 提供丰富的文件读写功能。常见流程是使用 open() 打开文件,然后读取或写入,最后关闭文件。示例:

# 打开并读取文件内容
with open("data.txt", "r", encoding="utf-8") as f:
    content = f.read()    # 读取整个文件
    print(content)

# 遍历读取行
with open("data.txt", "r", encoding="utf-8") as f:
    for line in f:
        print(line.strip())

# 写入文件(会覆盖原内容)
with open("out.txt", "w", encoding="utf-8") as f:
    f.write("Hello, world!\n")
    f.write("第二行内容\n")

# 追加写入
with open("out.txt", "a", encoding="utf-8") as f:
    f.write("追加的一行\n")
  • open(filename, mode, encoding)mode 参数可为 'r'(读)、'w'(写,覆盖)、'a'(追加)、'rb'/'wb'(二进制读写)等。默认文本模式下使用 UTF-8 编码(Python 3)。
  • 使用 with open(...) as f: 结构可以自动管理文件关闭,即使发生异常也会自动关闭文件。完成后无需手动调用 f.close()
  • 读操作方法:f.read() 读取全部内容,f.readline() 读取一行,f.readlines() 读取所有行返回列表
  • 写操作方法:f.write(string) 写入字符串(需要显式换行符 \n)。

提示:文件操作时建议捕获异常(如文件不存在时会 FileNotFoundError),并确保使用 with 管理上下文。对二进制文件(如图片、音频),应使用 'rb'/'wb' 模式。不要忘了在写入文本时指定正确的编码以避免乱码。

九:异常、模块与包

  • 异常处理:在程序运行过程中可能发生错误(如除零、文件未找到等)。可以用 try...except 结构捕获并处理异常。基本语法:

    try:
        可能引发异常的代码
    except 具体异常类型 as e:
        异常处理代码
    except (异常类型1, 异常类型2):
        处理多种异常
    else:
        如果没有发生异常则执行
    finally:
        无论是否发生异常都会执行的代码(通常用于清理)
    

    示例:

    try:
        num = int(input("输入一个整数:"))
        result = 10 / num
    except ValueError:
        print("请输入有效的整数")
    except ZeroDivisionError:
        print("除数不能为 0")
    else:
        print("计算结果:", result)
    finally:
        print("程序结束")
    

    若不捕获异常,程序会终止并打印错误信息。正确的异常处理使程序健壮且用户友好。注意:不要使用过于宽泛的 except:,最好指定具体异常类型。

  • 模块:Python 模块(Module)是一个以 .py 为后缀的文件,用于定义函数、类和全局变量等。通过模块可以逻辑分块组织代码,提高复用性。使用 import 关键字导入模块:

    # 假设当前目录下有 utils.py 文件
    import utils         # 导入整个模块
    from utils import foo  # 从模块导入函数/变量
    
    print(utils.foo(5))
    

    模块可以重命名:import utils as u。避免循环导入和命名冲突。

    自定义模块:直接在项目文件夹中创建 .py 文件,或将其放在 Python 的搜索路径内。可以使用 if __name__ == "__main__": 语句块让模块既能导入也能作为脚本运行。

  • 包(Package)是具有层次结构的目录结构用于组织模块。简单来说,包就是一个包含子模块(和子包)的文件夹,必须包含一个名为 __init__.py 的文件(可为空),用于标识该目录为包。示例目录结构:

    mypackage/
        __init__.py
        module1.py
        subpkg/
            __init__.py
            module2.py
    

    使用时可以这样导入:import mypackage.module1from mypackage.subpkg import module2。包机制避免了模块命名冲突,提高了代码组织性。

  • 异常(扩展):可以使用 raise 自己触发异常,或者定义新的异常类(通常继承自 Exception)。示例:

    def sqrt(x):
        if x < 0:
            raise ValueError("参数必须是非负数")
        return x ** 0.5
    

    x 为负时,函数会抛出 ValueError 异常。调用方可捕获并处理。

提示:捕获异常时,应尽量缩小 try 块的范围,只包含可能发生错误的代码。使用 print() 或日志记录模块 logging 记录错误信息有助于调试。模块导入时,可使用虚拟环境和 pip 管理依赖。

十:Python 面向对象编程学习笔记

1. 类的定义与实例化

在 Python 中,使用 class 语句定义类,类体中可以包含类变量和方法。类的构造函数是 __init__() 方法,它会在创建实例时自动调用。举例来说:定义一个 Person 类,并创建其实例(instance):

class Person:
    """表示一个人"""
    species = "Homo sapiens"  # 类变量,所有实例共享
    def __init__(self, name):
        self.name = name      # 实例变量,仅属于当前实例

# 实例化类
p = Person("Alice")
print(p.name)    # 输出: Alice
print(Person.species)  # 访问类变量,共享值

以上代码中,Person 类的实例通过调用 Person("Alice") 创建。实例化时会执行 __init__(),其中 self.name = name 将参数 name 存到实例属性。species 是类变量,对所有实例共享。可以使用 实例.属性类名.类变量 访问这些属性。

2. 成员变量与类变量的区别

Python 类的变量分为类变量实例变量。类变量定义在类体内,属于类本身,所有实例共享同一个值;实例变量通常在 __init__ 方法里通过 self.xxx 定义,每个实例独立拥有。示例:

class Example:
    counter = 0       # 类变量

    def __init__(self, value):
        self.value = value   # 实例变量
        Example.counter += 1

# 创建实例
e1 = Example(10)
e2 = Example(20)
print(e1.value, e2.value)    # 输出: 10 20,实例变量互不影响
print(Example.counter)       # 输出: 2,类变量被所有实例共享:contentReference[oaicite:3]{index=3}
  • 类变量:在类体中定义(如 counter),通过 ClassName.counter实例.counter 访问,所有实例共用一个值。
  • 实例变量:通常在 __init__() 中用 self.xxx 定义(如 self.value),仅作用于各自的实例,不同实例互不干扰。

3. 方法类型:实例方法、类方法、静态方法

Python 类中的方法根据参数和装饰器分为三种类型:

  • 实例方法:第一个参数为 self,代表实例自身。调用时只能通过实例调用,它可以访问实例属性和类属性。例如:

    class MyClass:
        def instance_method(self):
            print(f"Instance method called on {self}")
    obj = MyClass()
    obj.instance_method()    # 调用实例方法,隐式传入 obj 作为 
    # self:contentReference[oaicite:6]{index=6}
    
  • 类方法:使用 @classmethod 装饰,第一个参数为 cls,代表类本身。类方法可以访问类变量或修改类状态,但**不能访问实例属性**。调用时可以使用 ClassName.method()instance.method()(不推荐实例调用)。例如:

    class MyClass:
        count = 0
        @classmethod
        def class_method(cls):
            print(f"类方法: 当前 count={cls.count}")
    MyClass.class_method()    # 输出: 类方法: 当前 count=0:contentReference[oaicite:7]{index=7}
    
  • 静态方法:使用 @staticmethod 装饰,不接收 selfcls 参数。静态方法类似普通函数,但属于类的命名空间。它不能访问实例属性或类属性,只是放在类里便于组织代码。调用方式同类方法。示例:

    class MyClass:
        @staticmethod
        def static_method(x, y):
            return x + y
    print(MyClass.static_method(2, 3))  # 输出: 5:contentReference[oaicite:8]{index=8}
    

简言之,实例方法绑定实例并自动传递实例;类方法绑定类并自动传递类;静态方法没有自动传参。

4. 封装与 @property

封装是将对象的属性和实现细节隐藏起来,只通过公开的方法与外界交互。在 Python 中,可以通过以下方式实现封装:

  • 属性(变量)加下划线前缀(如 _x__x),表明这是受保护或私有的属性外部不宜直接访问
  • 使用 @property 装饰器,将方法伪装成属性提供对私有属性的读写控制。这样外部可以像访问属性一样调用方法,从而在赋值和取值时加入检查逻辑。示例:
class Account:
    def __init__(self, balance):
        self.__balance = balance   # 私有属性
    @property
    def balance(self):
        return self.__balance      # 定义 getter
    @balance.setter
    def balance(self, amount):
        if amount < 0:
            raise ValueError("余额不能为负")
        self.__balance = amount   # 定义 setter

acct = Account(100)
print(acct.balance)   # 通过属性访问 getter
acct.balance = 200    # 通过属性访问 setter

上例中,balance 方法被 @property 装饰后可通过 acct.balance 访问,外部无法直接修改私有变量 __balance,从而实现了数据保护。

5. 继承、多重继承与 MRO

Python 通过继承(inheritance)实现类之间的代码复用。子类在定义时将父类放入括号中,例如:class B(A): 表示 B 继承 A。Python 支持多重继承,例如 class C(A, B): 同时继承 AB。在多重继承的情况下,方法解析顺序(MRO, Method Resolution Order)决定了搜索方法和属性的顺序(就是决定使用那个变量那个方法,如果存在重复的话)。Python 3 使用 C3 线性化算法来确定 MRO,使得菱形继承(多个父类继承自同一祖先)时有一致的查找顺序。可以通过 ClassName.__mro__ 查看类的继承顺序。示例:

class A:
    def msg(self): print("来自 A")
class B(A):
    def msg(self): print("来自 B")
class C(A):
    def msg(self): print("来自 C")
class D(B, C):  # 多重继承
    pass
# 上述就是一个菱形继承

d = D()
d.msg()   # 调用 B 的 msg,按照 D -> B -> C -> A 的 MRO 查找
print(D.__mro__)  # 查看方法解析顺序

在上述例子中,D.msg() 会先调用 B 的实现,按照 MRO 依次为 D, B, C, A, object

应该比较懵逼吧,接下来就是详细的讲解一下MRO的知识点:

5.2 Python 继承与 MRO 深度解析

一、继承基础概念
class Parent:
    def show(self):
        print("Parent method")

class Child(Parent):
    def show(self):
        print("Child method")
        super().show()  # 调用父类方法

c = Child()
c.show()
"""
输出:
Child method
Parent method
"""
  • 单继承时方法解析简单明确
  • super() 用于显式调用父类方法

二、多继承带来的挑战
菱形继承问题(钻石问题)
class A:
    def show(self):
        print("A")

class B(A):
    def show(self):
        print("B")
        super().show()

class C(A):
    def show(self):
        print("C")
        super().show()

class D(B, C):
    def show(self):
        print("D")
        super().show()

d = D()
d.show()

问题:方法调用路径存在歧义
期望结果:D → B → C → A(调用完成D的之后接下来到底该先调用谁的B?C?)


三、MRO 的演进历史
1. Python 2.2 之前:深度优先搜索(DFS)
class X: pass
class Y(X): pass
class Z(X, Y): pass  # 旧式类会报错
  • 经典类(不继承 object)使用 DFS
  • 无法处理菱形继承问题
2. Python 2.2:新旧 MRO 混合
  • 新式类(继承 object)引入 __mro__ 属性
  • 使用广度优先搜索(BFS)与 DFS 混合算法
3. Python 2.3+:C3 线性化算法
  • 当前 Python 3 统一采用的算法
  • 解决多继承的二义性问题

四、C3 算法原理
1. 算法核心规则
  • 单调性:如果类 C 在 MRO 中出现在类 B 之前,那么 C 的所有子类也必须出现在 B 之前
  • 局部优先父类声明顺序决定优先级
2. 算法步骤

给定类 C 的继承列表 [B1, B2, …, BN]:

  1. 计算每个父类的 MRO
  2. 创建合并列表:C + 父类 MRO 的合并
  3. 检查头元素是否合法:
    • 不在其他列表的尾部
    • 满足单调性要求
3. 公式表示
L[C(B1...BN)] = C + merge(L[B1],..., L[BN], B1...BN)

其中 merge 操作:

  1. 取第一个列表的头元素
  2. 如果该元素不在其他列表的尾部,则加入结果
  3. 否则检查下一个列表的头元素
  4. 重复直到所有列表为空

五、MRO 实战分析
1. 查看 MRO 顺序
print(D.__mro__)  # 或 D.mro()
# 输出:(<class '__main__.D'>, <class '__main__.B'>, 
#        <class '__main__.C'>, <class '__main__.A'>, 
#        <class 'object'>)
2. 复杂继承案例
class O: pass
class F(O): pass
class E(O): pass
class D(O): pass
class C(D,F): pass
class B(D,E): pass
class A(B,C): pass

print(A.mro())
"""
结果:
[<class '__main__.A'>, 
 <class '__main__.B'>, 
 <class '__main__.D'>, 
 <class '__main__.C'>, 
 <class '__main__.E'>, 
 <class '__main__.F'>, 
 <class '__main__.O'>, 
 <class 'object'>]
"""

六、MRO 冲突与检测
1. 合法继承结构
class X: pass
class Y: pass
class A(X, Y): pass  # 合法
2. 非法继承结构
class X: pass
class Y(X): pass
class Z(X, Y): pass  # 触发 TypeError
"""
TypeError: Cannot create a consistent method resolution
order (MRO) for bases X, Y
"""

七、super() 的工作原理
1. 动态查找机制
class A:
    def method(self):
        print("A")

class B(A):
    def method(self):
        print("B")
        super().method()

class C(A):
    def method(self):
        print("C")
        super().method()

class D(B, C):
    def method(self):
        print("D")
        super().method()

d = D()
d.method()
"""
输出:
D
B
C
A
"""
2. super() 参数详解
class Base:
    def method(self):
        print("Base")

class Child(Base):
    def method(self):
        print("Child")
        super(Child, self).method()  # 显式指定

八、最佳实践与注意事项
  1. 避免复杂多重继承:优先使用组合模式
  2. 统一使用 super():保持 MRO 一致性
  3. 注意初始化顺序
class Base:
    def __init__(self):
        print("Base init")

class Mixin:
    def __init__(self):
        print("Mixin init")
        super().__init__()

class MyClass(Mixin, Base):
    def __init__(self):
        print("MyClass init")
        super().__init__()

obj = MyClass()
"""
输出:
MyClass init
Mixin init
Base init
"""

通过理解 MRO 机制,开发者可以:
✅ 准确预测方法调用顺序
✅ 设计合理的类继承结构
✅ 避免多继承常见陷阱
✅ 编写可维护的面向对象代码

6. 方法重写与 super()

子类可以通过重写(override)父类的方法来改变或扩展其行为,即在子类中定义一个与父类同名的方法。如果子类想在重写的同时调用父类的原方法,可以使用 super() 函数。例如:

class Animal:
    def eat(self):
        print("Animal 正在吃")
class Cat(Animal):
    def eat(self):
        super().eat()        # 调用父类的 eat()
        print("Cat 吃老鼠")  # 添加新的行为
c = Cat()
c.eat()

输出结果会先执行 Animaleat(),再执行 Cat 的额外行为。这样就利用 super() 在保留父类功能的基础上添加了子类特有的逻辑。

7. 多态与鸭子类型

多态(polymorphism)指的是允许不同类的实例相同的接口调用表现出不同的行为。在 Python 中,通过继承并重写方法可以实现多态。例如:

class Animal:
    def speak(self):
        print("动物发声")
class Dog(Animal):
    def speak(self):
        print("汪汪汪")
class Cat(Animal):
    def speak(self):
        print("喵喵喵")

def make_sound(obj: Animal):
    obj.speak()

make_sound(Dog())  # 输出: 汪汪汪
make_sound(Cat())  # 输出: 喵喵喵

上述例子中,make_sound 函数接受 Animal 类型的对象,对不同子类实例调用同名方法,产生不同输出,实现了多态。

Python 中的鸭子类型(duck typing)则强调不关心对象的实际类型,只关心对象是否具有特定的方法或行为。即“如果它像鸭子、走起路来像鸭子、叫起来像鸭子,那就是鸭子”。例如,只要一个对象有 speak() 方法,就可以被当作具有多态性的对象使用,无需显式继承某个接口。

8. 魔术方法

魔术方法(special methods)也称双下划线方法,如 __init__, __str__ 等,它们由 Python 内部调用,用于定制类的行为。常见的魔术方法包括:

  • __init__(self, …):构造函数,用于初始化新实例。

  • __str__(self):定义 str(obj)print(obj) 时的用户可读输出;

  • __repr__(self):定义调试和交互环境下的输出,目标是“正式的”字符串表示。Python 规定 __str__ 用于给用户看,__repr__ 用于给开发者看。例:

    class Person:
        def __init__(self, name):
            self.name = name
        def __str__(self):
            return f"Person: {self.name}"
        def __repr__(self):
            return f"Person({self.name!r})"
    p = Person("Bob")
    print(p)      # 使用 __str__ 输出: Person: Bob
    p             # 在 REPL 中显示 __repr__: Person('Bob')
    
  • __len__(self):使 len(obj) 返回对象长度。如果类表现得像序列或集合,通常定义该方法。

  • __eq__(self, other) / __lt__(self, other) 等比较方法:定义 ==< 等运算符的行为。

  • __getitem__(self, key):使对象支持索引切片,如 obj[key] 调用该方法。

例如,自定义一个容器类实现这些魔术方法:

class MyList:
    def __init__(self, data):
        self._data = list(data)
    def __len__(self):
        return len(self._data)                # 支持 len()
    def __getitem__(self, index):
        return self._data[index]             # 支持索引操作
    def __eq__(self, other):
        return self._data == other._data     # 支持 == 比较
    def __str__(self):
        return str(self._data)
    def __repr__(self):
        return f"MyList({self._data})"

总之,魔术方法赋予类自定义行为,例如让对象能使用内置函数 len()、通过 == 比较、通过索引访问等,使得自定义类能像内置类型一样使用

9. 运算符重载

运算符重载实质上就是定义相应的魔术方法,比如 + 运算符调用 __add__() 方法。通过在类中实现这些方法,可以让实例支持使用标准运算符。示例:

class Vector2D:
    def __init__(self, x, y):
        self.x, self.y = x, y
    def __add__(self, other):
        # 重载加法运算符:返回新向量
        return Vector2D(self.x + other.x, self.y + other.y)
    def __repr__(self):
        return f"Vector2D({self.x}, {self.y})"

v1 = Vector2D(1, 2)
v2 = Vector2D(3, 4)
print(v1 + v2)   # 输出: Vector2D(4, 6)

在上述代码中,对象 v1 + v2 实际上调用了 v1.__add__(v2),将两个向量的对应分量相加后返回新向量。类似地,可重载减法、乘法等(__sub____mul__ 等)以及其他运算符。这样使自定义类的实例能够自然地使用算术运算符。

10. 抽象类与接口(使用 abc 模块)

抽象类定义了一个接口规范,不能被实例化只能被子类继承。它通常包含一个或多个抽象方法,这些方法没有实现,要求所有子类必须重写。Python 通过 abc 模块支持抽象基类(ABC)。例如:

from abc import ABC, abstractmethod

class Shape(ABC):
    """形状的抽象基类"""
    @abstractmethod
    def area(self):
        pass

class Circle(Shape):
    def __init__(self, r):
        self.r = r
    def area(self):
        return 3.14 * self.r * self.r

# Shape() 不能实例化,否则会报错
c = Circle(5)
print(c.area())

在以上代码中,Shape 类继承自 ABC 并使用 @abstractmethod 装饰 area() 方法。这意味着 Shape 是抽象类,不能直接创建实例。 只有在子类中实现了所有抽象方法后,才能实例化子类。Python 中没有专门的接口关键字,但抽象类可以充当接口的角色。

11. 组合 vs 继承

在面向对象设计中,继承(is-a)和组合(has-a)是两种不同的复用方式。

  • 继承:子类与父类之间是一种“”的关系,如 Cat 是一种 Animal。继承适用于类别之间具有明确的层次关系、子类需要重用父类的大部分功能的场景。
  • 组合:一个类包含另一个类的实例,建立“”的关系,比如 Car 有一个 Engine。组合适用于两个类各自独立,一个类需要在实现时使用另一个类的场景

例如:

# 继承示例
class Animal:
    def walk(self): print("动物在走")
class Dog(Animal):
    pass
dog = Dog()
dog.walk()   # 输出: 动物在走

# 组合示例
class Engine:
    def start(self): print("发动机启动")
class Car:
    def __init__(self):
        self.engine = Engine()  # 组合:Car 拥有一个 Engine
    def drive(self):
        self.engine.start()
        print("汽车驾驶中")
car = Car()
car.drive()

设计建议通常多用组合,少用继承。即只有在类之间确实存在 “是-a” 关系且能增强代码可复用性时才使用继承;否则更倾向于通过组合来组合对象以保持灵活性

12. 常见 OOP 设计建议与模式

面向对象设计中常用一些设计模式和建议来提高代码质量:

  • 单例模式(Singleton):确保某个类只有一个实例,并提供全局访问点。在 Python 中可通过重写 __new__ 来实现单例,确保多次创建返回同一个实例。示例代码:

    class Singleton:
        _instance = None
        def __new__(cls, *args, **kwargs):
            if not cls._instance:
                cls._instance = super().__new__(cls)
            return cls._instance
    a = Singleton()
    b = Singleton()
    print(a is b)  # True,只有一个实例
    
  • 工厂模式(Factory):提供一个用于创建对象的接口,根据输入参数决定实例化哪个类。适用于需要创建多种相关对象且需解耦客户端与具体类的场景。示例代码:

    class BMW:
        def run(self): print("宝马奔跑中")
    class Audi:
        def run(self): print("奥迪奔驰中")
    class CarFactory:
        @staticmethod
        def make_car(model):
            if model == "BMW":
                return BMW()
            elif model == "Audi":
                return Audi()
    
    car = CarFactory.make_car("BMW")
    car.run()  # 输出: 宝马奔跑中:contentReference[oaicite:41]{index=41}
    
  • 策略模式(Strategy):定义一系列可互换算法,将它们分别封装在不同类中,使得算法可独立于使用它的客户端变化。客户端可以在运行时动态选择策略对象。示例结构:

    class StrategyA:
        def execute(self): print("算法A")
    class StrategyB:
        def execute(self): print("算法B")
    class Context:
        def __init__(self, strategy):
            self.strategy = strategy
        def do_something(self):
            self.strategy.execute()
    context = Context(StrategyA())
    context.do_something()  # 输出: 算法A
    
  • 设计建议:遵循面向对象设计原则,如“依赖抽象单一职责接口隔离”,以及优先使用组合而非继承。良好的模式能提高代码复用性、可维护性和可扩展性。

十一: print函数

1. 基本语法

print() 是 Python 中最基础且最常用的函数之一,用于将对象输出到标准输出(通常是控制台)。它支持丰富的参数和格式化功能。

1.1 最简单的形式
print("Hello World")  # 输出: Hello World
1.2 打印多个对象
name = "Alice"
age = 25
print("Name:", name, "Age:", age)  # 输出: Name: Alice Age: 25

2. 参数详解

print() 函数有四个关键参数:

print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)
2.1 sep (分隔符)
  • 默认使用空格分隔多个对象
print("2023", "10", "01", sep="-")  # 输出: 2023-10-01
print("a", "b", "c", sep=" → ")     # 输出: a → b → c
2.2 end (结束符)
  • 默认添加换行符 \n
print("Hello", end=" ")
print("World")  # 输出: Hello World(同一行)
2.3 file (输出目标)
  • 默认输出到控制台,可重定向到文件
with open("output.txt", "w") as f:
    print("Save to file", file=f)  # 内容写入 output.txt
2.4 flush (强制刷新)
  • 默认 False,设置为 True 会立即刷新缓冲区
import time

print("Loading", end="", flush=True)
for _ in range(3):
    time.sleep(1)
    print(".", end="", flush=True)  # 实时显示进度
# 输出: Loading...(每秒显示一个点)

3. 字符串格式化

3.1 旧式格式化 (%)
price = 19.99
print("Price: $%.2f" % price)  # 输出: Price: $19.99
3.2 str.format()
print("{0} + {1} = {2}".format(3, 5, 3+5))  # 输出: 3 + 5 = 8
print("Name: {name}, Age: {age}".format(name="Bob", age=30))
3.3 f-strings (Python 3.6+)
quantity = 5
item = "apples"
print(f"I have {quantity} {item}")       # 输出: I have 5 apples
print(f"Result: {2**10}")               # 输出: Result: 1024
print(f"Hex: {255:#x}")                 # 输出: Hex: 0xff

4. 输出重定向

4.1 临时重定向
import sys

with open('log.txt', 'w') as f:
    sys.stdout = f  # 重定向标准输出
    print("This goes to log.txt")
    sys.stdout = sys.__stdout__  # 恢复默认
4.2 同时输出到控制台和文件
from contextlib import redirect_stdout

with open('output.txt', 'w') as f, redirect_stdout(f):
    print("This writes to file")
    print("Also visible on console", file=sys.stdout)

5. 高级用法

5.1 打印特殊字符
print("Line1\nLine2")     # 换行符
print("Tab\tSeparated")   # 制表符
print("Backslash: \\")    # 转义反斜杠
5.2 打印原始字符串
print(r"C:\Users\Name")  # 输出: C:\Users\Name(不转义)
5.3 打印对象信息
class Dog:
    def __init__(self, name):
        self.name = name
        
buddy = Dog("Buddy")
print(buddy)  # 默认输出: <__main__.Dog object at 0x...>
5.4 彩色输出
print("\033[31mRed Text\033[0m")  # 输出红色文字
print("\033[44mBlue Background\033[0m")

6. 注意事项

6.1 类型自动转换
print(10 + 20)          # 输出: 30(数字计算)
print("10" + "20")      # 输出: 1020(字符串拼接)
6.2 处理非ASCII字符
print("中文")            # Python 3 默认支持UTF-8
print("😊 Unicode Emoji") 
6.3 性能优化
  • 避免在循环中频繁打印:
# 低效方式
for i in range(10000):
    print(i)

# 高效方式
output = '\n'.join(str(i) for i in range(10000))
print(output)

提示:在 Jupyter Notebook 中,print() 可能不会立即显示所有输出,可使用 flush=True 强制刷新缓冲区。


网站公告

今日签到

点亮在社区的每一天
去签到