第 2 章 Python 语言基础与数据结构¶

本章主要内容如下:

  • 2.1 Python基本语法
  • 2.2 变量与数据类型
  • 2.3 数值型
  • 2.4 字符串
  • 2.5 列表(List)
  • 2.6 元组(Tuple)
  • 2.7 字典(Dictionary)
  • 2.8 集合(Set)
  • 2.9 类型转换

2.1 Python 基本语法¶

2.1.1 代码语句¶

  1. 大部分情况下,一行即为一句完整的代码语句,可以实现一定的功能。程序会依次从上到下执行每一句代码,直到单元格结束。

【小练习】分别在一行中键入 3 + 2,和分三行键入 3 + 2,看看结果有何不同?

In [ ]:
 
  1. 要求不按照顺序执行代码时,可以使用复合语句来实现。具体方法,将在后续使用时介绍。

2.1.2 注释语句¶

  • 注释语句是在单元格中的普通文字,不会被执行,仅提供解释说明的作用。
  • 单行注释以 # 开头。
  • 多行注释以首尾各加上三引号'''或""" 来表示。
  • Ctrl + / 可以快速注释/取消注释选中行。

📓 拓展知识
多行注释常常被用来写文档字符串(docstring)。 这主要用于大段代码的文字解释,常常放在需要解释的代码块的开头几行。

In [ ]:
这是单行注释
x = 10  此表示期数(period)
In [ ]:
这里演示多行注释的写法。
多行注释常用来作为代码的详细说明。

y = 20  此表示每期现金流(cash flow)

计算并输出(这里做一个最简单的“相加”示例)
print(x + y)

2.1.3 print() 函数和 input() 函数¶

  1. print() 函数用于输出信息到控制台。可以接受多个参数,默认以空格分隔输出。
In [ ]:
# Print函数演示

# 输出一个或多个值
print("Hello, Python!")  # 输出字符串
print("The answer is", 42)  # 输出多个值,默认以空格分隔
  1. input() 函数用于让用户自行输入信息。
  • input(prompt)可向用户显示提示信息,其中的prompt表示任意提示文本。
  • 用户输入完成后按回车,输入的内容会作为被存储为文本。
In [ ]:
# input() 函数演示
# 请显示提示语:"请输入您的名字:"。
print("Hello,", input() )  # 输出问候语和用户输入的名字

2.2 变量与数据类型¶

2.2.1 变量的作用¶

Python中,变量(variable)可以理解为对数据的命名,这样我们就可以非常方便快速地使用某个数据。
类比:购物车中的物品可以看作变量,物品的种类(如苹果、香蕉)可以看作变量的类型,物品的数量可以看作变量的值。我们不需要提前告诉购物车里会放什么类型的物品,只要把物品放进去,购物车就会自动识别它们的类型和数量。

2.2.2 变量的赋值与调用¶

变量赋值时被自动创建,赋值表达式:变量名 = 值。
注意: = 用于赋值,而不是比较是否相等。

2.2.3 变量命名规范

  • 以字母或下划线开头,不能以数字开头。
  • 只能包含字母、数字、下划线。
  • 大小写敏感。
  • 避免与 Python 关键字重名(例如 for, if, class 等)。
    • 关键字列表:https://docs.python.org/3/reference/lexical_analysis.html#keywords
  • 命名应体现含义,例如 invoice_id, gross_profit, customer_balance。
  • Python3可以用中文变量名,但不推荐使用,因为可能会导致编码问题和可读性下降。
  • (推荐)蛇形命名方法(snake_case):变量名由小写字母和下划线组成,单词之间用下划线分隔,例如 customer_balance、gross_profit。
    • 这种命名方式在 Python 社区中非常流行,符合 PEP 8 的命名规范,能够提高代码的可读性和一致性。
In [ ]:
# 变量演示: 错误命名
# 1. 使用关键字作为变量名会导致语法错误
for = 10  # 这行代码会报错,因为 for 是 Python 的关键字,不能用作变量名

# 2. 变量名不能以数字开头
1st_value = 100  # 这行代码会报错,因为变量名不能以数字开头

# 3. 变量名不能包含空格或特殊字符
my variable = 5  # 这行代码会报错,因为变量名不能包含空格

# 4. 变量名只能包含字母、数字和下划线
my-variable = 10  # 这行代码会报错,因为变量名不能包含连字符

# 5. 变量名区分大小写
Value = 50
value = 100
print(Value)  # 输出 50
print(value)  # 输出 100

# 6. 变量名应具有描述性,避免使用单个字母或无意义的名称
x = 10  # 这行代码虽然合法,但变量名 x 没有描述性,不清楚它代表什么
customer_balance = 1000.00  # 这行代码更好,因为变量名 customer_balance 清晰地描述了它的含义
账户余额 = 1000.00  # 虽然合法,但不推荐使用中文变量名,因为可能会导致编码问题和可读性下降
zhanghuyue = 1000.00  # 虽然合法,但不推荐使用拼音变量名,因为可能会降低代码的可读性,尤其是对于非中文母语的开发者来说
ZHYE = 1000.00  # 使用拼音缩写的变量名会进一步减少可读性,不推荐使用

2.2.4 变量类型

变量类型用以确定该变量可以取哪些值,以及可以进行哪些运算操作。 Python 不需要提前声明变量类型,会根据赋值自动确定其类型。 可以使用 `type(变量名)` 查看变量类型。
类别 数据类型 type() 特点 举例
基础 数字(number) int / float / complex 整数、浮点数、复数 x = 8;y = 88.8;z = 8+8j
基础 字符串(string) str 字符序列 s = 'hello'
基础 布尔(boolean) bool True / False flag = True
进阶 列表(list) list 有序,可修改 a = [1,2,3]
进阶 元组(tuple) tuple 有序,不可修改 b = (1,2,3)
进阶 集合(set) set 无序,不重复 c = {1,2,3}
进阶 字典(dictionary) dict 键值对,键唯一 d = {'name':'Tom','age':18}
进阶 None 类型 NoneType 空值/缺失值 x = None

None类型:None 是一个特殊的类型,表示“没有值”或“缺失值”。它常用于函数没有返回值时的默认返回值,或者表示变量尚未赋值。在这节不会单独展开

In [ ]:
# 赋值运算演示

a = 3 # 将整数 3 赋值给变量 a
b = a # 将变量 a 的值(3)赋值给变量 b,此时 a 和 b 都是 3
total = a + b
print(a)
print(total)
a = 6 # 将整数 6 赋值给变量 a,此时 a 的值变为 6,但 b 的值仍然是 3,因为它们是独立的变量
print(a)
print(total)
a = b # 将变量 b 的值(3)赋值给变量 a,此时 a 的值变回 3
print(a)
print(total)

a = a + 1 # 将 a 的值增加 1,相当于 a = a + 1 (这里可以看出 = 是一个赋值运算符,而 + 是一个加法运算符)
a += 1 # 这是 a = a + 1 的简写形式,效果相同
print(a)

a = a - 1 # 将 a 的值减少 1,相当于 a = a - 1
a -= 1 # 这是 a = a - 1 的简写形式,效果相同
print(a)
In [ ]:
# 变量演示: 变量类型
x1 = 3 #x1是一个变量,值为3
x2 = 10 #x2是一个变量,值为10
total = x1 +x2 # total是一个变量,值为x1和x2的和,即13

X1 = "3" # X1是一个变量,值为字符串 "3"
X2 = "10" # X2是一个变量,值为字符串 "10"
TOTAL = X1 + X2 # TOTAL是一个变量,值为字符串 "3" 和 "10" 的连接,即 "310"

print(total)
print("total") # 输出字符串 "total",而不是变量 total 的值
print(TOTAL)
In [ ]:
# 变量格式示例:不同类型的变量

x = 8                       # int:整数(例如数量、期数)
y = 88.8                    # float:小数(例如金额、利率)
s = "hello"                 # str:字符串(例如备注、ID)

a = [1, 2, 3]               # list:列表(例如一组金额)
b = (1, 2, 3)               # tuple:元组(例如一条固定结构记录)
c = {1, 2, 3}               # set:集合(例如去重后的ID集合)
d = {"name": "Tom", "age": 18}  # dict:字典(例如“字段名 → 字段值”映射)

flag = True                 # bool:布尔值(条件判断)
empty = None                # None:空值/缺失值(还没填、暂时没有)

# type(...) 查看变量当前存储的数据类型
print(type(x), type(y), type(s))
print(type(a), type(b), type(c), type(d))
print(type(flag), type(empty))

2.3 数值型变量¶

2.3.1 数值型变量类型¶

数字类型用于表示数量与数值。常见数字类型:

  • int:整型 (整数)
  • float:浮点型 (小数)
  • bool:布尔型 (True/False)
    • 有固定写法: 一个为True, 一个为False,首字母大写,不能写成true或TRUE
    • 布尔型可以当作整型对待,其中 True 的值为 1,False 的值为 0。
  • complex:复数,包含实部和虚部,例如 3 + 4j (了解即可,本课程不涉及复数的运算)
    • z = a + bj,其中 a 是实部,b 是虚部,j 是虚数单位
    • 数学课本上虚数单位用i表示,但python中用j表示,可能是因为i在数学中常用来表示索引或计数器,而j则不太常见,所以被选来表示虚数单位。
In [ ]:
# 数字类型:int / float / bool
# bool 在 Python 中也属于一种数值类型:True 相当于 1,False 相当于 0(了解即可)

a = 3          # int:整数
b = -10        # int:负数也是 int
c = 3.14       # float:小数
flag1 = True   # bool:真
flag2 = False  # bool:假

print(type(a), a)
print(type(b), b)
print(type(c), c)

# int(flag) 会把 True/False 转成 1/0(在计数、条件累计时偶尔会用到)
print(type(flag1), flag1, int(flag1))
print(type(flag2), flag2, int(flag2))

2.3.2 常用算术运算符¶

运算符 含义 示例
+ 加 3 + 2
- 减 3 - 2
* 乘 3 * 2
/ 除 3 / 2
// 整除 7 // 3
% 取余 7 % 3
** 幂 2 ** 3

Note

  • / 的结果通常是 float;// 是整除(向下取整)。
  • 负数时的取整和取余:-7 // 3 结果为 -3,-7 % 3 结果为 2。
  • 复杂表达式建议加括号,避免优先级带来的误读。
In [ ]:
#算术运算符示例

print(3 + 2)     # 加法
print(3 - 2)     # 减法
print(3 * 2)     # 乘法
print(3 / 2)     # 除法(float) 
print(3 / 3)     # 除法(float) 使用算术运算符 /, 商一定是浮点数
print(3 // 3)    # 整除(floor division) 使用算术运算符 //, 商向下取整
print(7 // 3)    # 整除(floor division)
print(-7 // 3)  # 负数的整除,结果是向下取整的整数部分
print(7.0 // 3) # 整除(floor division) 即使是浮点数,结果也是向下取整的整数部分,但结果是一个浮点数
print(7 % 3)     # 取余(remainder)
print(-7 % 3)    # 负数的取余,结果是一个非负数,满足 (a // b) * b + (a % b) == a 的关系
print(2 ** 3)    # 幂(power)

2.3.3 运算符优先级¶

  • 幂 > 乘除、取余、整除 > 加减
  • 同级运算符从左向右计算
  • 可以用括号调节计算的优先级
In [ ]:
# 运算符优先级演示

print(3**2+5/2) # 先计算幂,再计算除法,最后计算加法

print( (3**2+5)/2 ) # 先计算括号内的幂和加法,再计算除法

2.3.4 其他常用数值处理函数¶

函数 作用
abs(x) 绝对值
round(x, n) 四舍五入到 n 位
pow(x, n) 幂运算
divmod(a, b) 同时返回整除结果与余数
In [ ]:
print(abs(-12.5))           # 绝对值
print(round(12.3456, 2))    # 四舍五入到 2 位小数(用于金额显示)
print(pow(2, 10), 2**10)    # 幂运算,两种写法等价
print(divmod(17, 5))        # 返回 (17 // 5, 17 % 5)

练习¶

  • 将月份映射为季度:month = 1~12 → quarter = 1~4
    • 若输入 month = 1/2/3,输出 quarter = 1;输入 month = 4/5/6,输出 quarter = 2;以此类推
  • 计算复利终值:FV = principal * (1 + r) ** t

双击阅读参考答案

2.4 字符串(String)¶

字符串(str)是字符序列,常用于表示文本、编码、标识符等。

字符串的常见定义方法:

  • 单引号:'hello'
  • 双引号:"hello"(与单引号等价,常用于字符串里包含单引号)
  • 三引号:'''...''' 或 """..."""(多行字符串,或包含引号时更方便)
    • 多行注释和多行字符串如何区分?
      • 多行注释通常不赋值给变量,直接写在代码中;多行字符串通常会赋值给变量,或者作为函数的 docstring。
In [ ]:
# 字符串举例:

# 不能像列表那样直接修改某个位置的字符
# s = INV1001 # 这行代码会报错,因为 INV1001 没有定义为字符串,应该加上引号
s = 'INV1001'
s = "INV1001"
s = '''INV1001'''  # 这也是一个字符串,包含了换行符

'''INV1001代表关于存货的一个记录ID,
通常用于标识某个特定的存货项目。''' # 这是一个多行注释,用来解释 INV1001 的含义

2.4.1 字符串的编码(移至第 5 章)¶

2.4.2 字符串的常用操作¶

运算符 描述 实例
+ 字符串拼接 'Hello' + 'World' -> 'HelloWorld'
* 重复输出字符串 'A' * 3 -> 'AAA'
In [ ]:
# --- 0:准备一个字符串 ---
s = "Life is short, you need Python."
print("original =", s)

# 1. 拼接 (+)
s1 = "Life is short"
s2 = "You need Python"
print(s1 + ", " + s2)

# 2. 重复 (*)
print("-" * 20)
print(s * 3)
print((s+'\n') * 3)

# 3. 成员运算 (in / not in)
print("Li" in s1)
print("Py" not in s1)

2.4.3 索引与切片¶

运算符 描述 实例
[] 通过索引获取字符 'Hello'[0] -> 'H'
[:] 截取字符串中的一部分 (切片) 'Hello'[1:4] -> 'ell'

索引¶

字符串中的每个字符都有一个对应的索引(index),表示它在字符串中的位置。

  • 索引从0开始
  • 负数索引表示从末尾开始计数

string_index

切片¶

切片(slice)是指从字符串中提取出一个子字符串的操作。

切片基本形式:s[start:end:step]

  • start:起始位置(包含)
  • end:结束位置(不包含)
  • step:步长(可省略)
  • 左闭右开原则:切片结果包含 start 索引对应的字符,但不包含 end 索引对应的字符。

    勘误:在之前的版本写作“左开右闭”,是不合适的。 但后面的表述“切片结果包含 start 索引对应的字符,但不包含 end 索引对应的字符”是正确的。

字符串长度¶

可以使用 len() 函数获取字符串的长度(字符数)。

In [ ]:
# --- 1:基本信息:长度 / 索引 / 切片 ---
print("len(s) =", len(s))
print("s[0]   =", s[0])      # 第 1 个字符(可能是空格)
print("s[-1]  =", s[-1])     # 最后 1 个字符
print("s[-5:] =", s[-5:])    # 最后 5 个字符
print("s[:10] =", s[:10])    # 前 10 个字符
print("s[2:12]=", s[2:12])   # 索引 2 到 11(左闭右开)
print("s[::-1] =", s[::-1])   # 反转字符串(step = -1)

2.4.5 其他常用字符串操作¶

类别 常用方法 说明
去空格 strip, lstrip, rstrip 删除首尾空白
大小写 lower, upper, title 统一文本格式
查找/计数 find, count, in, not in 查找位置 / 计数
判断 startswith, endswith 条件判断
替换 replace 替换子串
拆分与合并 split,join 拆分和合并字符串
In [ ]:
# --- 2:清洗与大小写:strip / lower / upper / title ---
s_withblank = "  Life is short, you need Python.  "
print("raw string =", s_withblank)
t = s_withblank.strip()        # 去掉首尾空白
print("strip() =", t)
print("lstrip() =", s_withblank.lstrip())  # 去掉左侧空白
print("rstrip() =", s_withblank.rstrip())  # 去掉右侧空白
print("lower() =", t.lower())
print("upper() =", t.upper())
print("title() =", t.title())
In [ ]:
# --- 3:查找与判断:find / count / startswith / endswith / in ---
print("find('Python') =", t.find("Python"))        # 找不到返回 -1
print("index('Python') =", t.index("Python"))       # 找不到会抛出 ValueError 错误
print("count('o')     =", t.count("o"))            # 统计出现次数
print("startswith('Life') =", t.startswith("Life"))
print("endswith('.')      =", t.endswith("."))
print("'need' in t ?      =", "need" in t)
In [ ]:
# 若find找不到时,会返回 -1
print("find('Python') =", t.find("Java"))  
# 而index找不到时,会抛出 ValueError 错误
print("index('Python') =", t.index("Java"))
# 故而在实际使用中,如果不确定子串是否存在,建议使用 find() 方法来避免程序崩溃;
# 如果需要确保子串一定存在,可以使用 index() 方法,这样如果子串不存在时会立即抛出错误,方便调试和错误处理。
In [ ]:
# --- 4:替换:replace ---
t2 = t.replace("Python", "SQL")  # replace 会生成新字符串
print("t  =", t)
print("t2 =", t2)
In [ ]:
# --- 5:拆分与合并:split / join ---
words = t.split()                # 按空白拆分(自动处理多个空格)
print("split() =", words)

joined = " | ".join(words)       # 用分隔符把列表合成字符串
print("join(...) =", joined)

Note: 字符串是不可变对象(immutable),不能对单个字符进行原地修改。所谓“修改字符串”,本质上都是创建一个新字符串。

In [ ]:
# 字符串(str)是不可变对象(immutable)
#t[0] = "X"  # ❌ 会报错:TypeError,因为字符串不支持原地修改
new_t = "X" + t[1:]  # ✅ 正确做法:用切片 + 拼接“生成新字符串”
print(new_t) 

练习:字符串切片¶

将字符串"uforbgood"赋值到s1变量中
分别访问s1中的第0个、第3个、第5到8个元素,并将它们组合在一起,并且打印出来.(第0个元素和第3个元素之间要有空格相间,第3个元素和第5-8个元素要有空格相间)

双击阅读参考答案

练习¶

  1. 统计Python之禅前三句有多少个字符。

  2. 不包括空格和标点符号, 有多少个字符。

  3. 统计Python之禅前三句有多少单词,忽略标点符号 "."

双击阅读参考答案

2.4.6 字符串格式化输出(f-string)¶

f"{...}": 花括号 {} 里不仅可以引用变量,还可以直接写表达式(例如计算、拼接、函数调用等)。

  • 引用变量:f"{name}"
  • 使用表达式:f"{a+b}"
In [ ]:
# 例子 1:引用变量
name = "Jack"
course = "Python"
print(f"Hello, {name}! Welcome to {course}.")

# 例子 2:在 {} 里写表达式(expression),不仅限于变量名
a = 12
b = 5
# :.2f 表示“保留两位小数”
print(f"a+b = {a+b}, a*b = {a*b}, a/b = {a/b}")

练习:使用数字型变量¶

编写程序,实现如下功能:输入出生年份,输出年龄(简化为2026 - 出生年份)并在屏幕上打印输出:Your age is XX。XX为年龄。

In [ ]:
a = int(input('Please enter your birth year (e.g., 2001):')) # int(...) 把输入的字符串转换成整数

b = 2026 - a

#print(...) #请在...中使用格式化输出使得屏幕上打印: Your age is XX. XX为年龄。

双击阅读参考答案

2.4.7 转义字符与原始字符串¶

在字符串字面量(string literal)里,反斜杠 \ 用来写“转义序列”(escape sequence):它不是普通字符,而是用来表示一些特殊字符。

  • \n:换行(newline)
  • \t:制表符(tab)
  • \\:一个反斜杠本身(因为单个 \ 被用作转义开头)
  • \" / \':在字符串里输出引号(当你需要把引号当作普通字符时)

原始字符串(raw string):r"..."

  • 含义:把反斜杠 \ 当作普通字符保留,不再把它当作转义开头
  • 典型用途:Windows 路径、正则表达式(regex)

Note

  1. 在代码里如果写 "C:\temp\new\2026.txt" 这种“单反斜杠路径”,其中 \t、\n 可能会被当作转义序列(制表符/换行),打印出来就会“看起来乱码”。
  2. 更稳妥的写法:r"C:\temp\new\2026.txt" 或 "C:\\temp\\new\\2026.txt",也可以用正斜杠 "C:/temp/new/2026.txt"。
  3. raw string 不能以单个反斜杠结尾,例如 r"C:\temp\" 会报错(最后的 \ 会把引号“吃掉”)。
In [ ]:
# 转义字符(escape sequence):\n 换行、\t 制表符
text = "Line1\nLine2"
print(text)            # \n换行

tabbed = "col1\tcol2"
print(tabbed)          # \t制表符

# 路径\t / \n 可能被当作转义,导致显示异常
bad_path = "C:\temp\new\2026.txt"     
print("bad_path:", bad_path)

# 更稳妥的写法 1:raw string(原始字符串)—— 反斜杠按字面意思保留
good_path_raw = r"C:\temp\new\2026.txt"

# 更稳妥的写法 2:双反斜杠 —— 把每个 \ 写成 \\
good_path_escaped = "C:\\temp\\new\\2026.txt"

# 更稳妥的写法 3:正斜杠 —— 在很多场景也可以正常工作
good_path_slash = "C:/temp/new/2026.txt"

print("good_path_raw    :", good_path_raw)
print("good_path_escaped:", good_path_escaped)
print("good_path_slash  :", good_path_slash)

练习:在字符串中表示特殊字符¶

  1. 在屏幕上打印输出一个类似电脑路径的字符串,如:C:\Users\Jack
  2. 在屏幕上输出如下内容:"\n" is a line break.
In [ ]:
#1

#print('...') # 在...中尝试输出路径 C:\Users\Jack

#2

#print('...') # 在...中尝试输出如下内容 "\n" is a line break.

双击阅读参考答案

2.5 列表(List)¶

2.5.1 列表(List)的定义与性质¶

列表(List)是 Python 中最常用的有序容器。它通过方括号 [ ] 定义,并使用逗号分隔内部元素。

核心特性¶
  • 有序(Ordered): 元素按照插入顺序排列,可以通过索引(Index)精确访问。
  • 可变(Mutable): 支持在原地对元素进行修改、新增(插入/追加)或删除。
  • 异构性(Heterogeneous): 列表可以同时包含不同数据类型的元素(如整数、字符串、布尔值等)。
  • 可嵌套性(Nested): 列表的元素可以是另一个列表,从而构建多维数据结构。

Note
由于列表是可变对象(Mutable),它非常适合处理动态变化的数据流。例如:记录一组实时波动的金额、存储待处理的发票编号序列等。

In [ ]:
# 列表举例

list1 = [1, 2, 3, 4, 5] # 包含整数的列表
list2 = ['a', 'b', 'c', 'd', 'e'] # 包含字符串的列表
list3 = [1, 'a', 2.5, True] # 包含不同类型元素的列表
x = x = ['abc', 1, 0.1, True, [1, 2, 3, 'a', 'b'], (1, 2, 3, 'a', 'b'), {'two':2, 'three':3}, {1, 2, 3}] # 包含各种类型元素的列表
print(list1)
print(list2)
print(list3)

2.5.2 索引与切片¶

  • 类似字符串索引,我们可以用同样的方法索引列表中元素。
  • 如果列表中的某个元素也是列表,我们可以通过二次索引来进一步索引、切片。比如,先从嵌套列表中提取索引为4的元素,这个元素还是一个列表。然后进一步再提取子列表中索引为1的元素。
In [ ]:
x = ['abc', 1, 0.1, True, [1, 2, 3, 'a', 'b'], (1, 2, 3, 'a', 'b'), {'two':2, 'three':3}, {1, 2, 3}]
print(x)

print(x[0], x[-1])   # 第一个、最后一个
print(x[1:4])        # 切片:取第 2~4 个元素(不含右端)
print(x[::-1])       # 反转(step = -1)
print(x[4][1])      # 列表中的列表:取第 5 个元素(列表),再取其中的第 2 个元素(字符串 'b')

元素修改:索引赋值与切片赋值¶

列表是可修改对象(mutable),因此可以通过“索引/切片赋值”直接改动列表内容:

  • 索引赋值:lst[i] = x(修改某一个位置)
  • 切片赋值:lst[a:b] = [...](一次修改一段;长度可能变化)
  • 删除指定位置:del lst[i](按位置删,不返回被删元素)
  • 删除一段:del lst[a:b] 或 lst[a:b] = []

Note

  • 修改元素前,索引必须有效;常用 len(lst) 或 0 <= i < len(lst) 先判断。
  • 与字符串不同:字符串是不可变对象(immutable),不能用 s[0] = "X" 直接改字符。
In [ ]:
# 元素修改演示:索引赋值 / 切片赋值 / del(每一步都 print)

items = ["INV1001", "INV1002", "INV1003", "INV1004"]
print("start:", items)

# 1) 索引赋值:修改某一个位置的元素
items[0] = "INV1001-UPDATED"   # 修改第 1 个元素
print("after items[0] update:", items)

items[-1] = "INV1004-UPDATED"  # 负索引:修改最后一个元素
print("after items[-1] update:", items)

# 2) 切片赋值:一次替换一段(长度可能变化)
items[1:3] = ["INV2001", "INV2002", "INV2003"]   
print("after items[1:3] replace:", items)

# 3) del:按索引删除(不返回被删元素)
del items[0]
print("after del items[0]:", items)

2.5.3 常用列表操作¶

1. 增 (Adding)¶
操作 方法/写法 说明
追加 append(x) 在末尾追加一个元素
扩展 extend(iter) 追加多个元素(逐个加入)
插入 insert(i, x) 在位置 i 插入元素(后面整体右移)

2. 删 (Deleting)¶
操作 方法/写法 说明
删除末尾 pop() 删除并返回最后一个元素
删除指定位置 pop(i) 删除并返回索引 i 的元素
按值删除 remove(x) 删除第一个匹配元素(找不到会报错)
清空 clear() 删除所有元素,列表变为空

3. 改 (Reordering / Sorting)¶
操作 方法/写法 说明
排序 sort() / sorted(lst) 原地排序 / 返回新列表(不改原列表)
反转 reverse() / reversed(lst) 原地反转 / 返回迭代器(常配合 list(...))

4. 查 (Accessing / Searching)¶
操作 方法/写法 说明
查询位置 index(x) 返回 x 第一次出现的索引(找不到会报错)
计数 count(x) 统计 x 出现次数
切片 lst[a:b:step] 取子列表(返回新列表),可用于“批量取/跳着取”

5. 其他 (Others / Operations)¶
操作 方法/写法 说明
复制 copy() 浅拷贝(复制一层)
拼接 lst1 + lst2 生成新列表(不改原列表)
重复 lst * n 列表内容重复 n 次(生成新列表)
成员判断 x in lst / x not in lst 判断是否包含某元素
长度 len(lst) 列表元素个数
最值/求和 min(lst) / max(lst) / sum(lst) 常见汇总(列表里应是数字)
In [ ]:
# --- 0:准备数据 + 一个小工具函数(统一打印) ---

def show(tag, lst):
    """统一打印格式:tag + 当前列表 + 长度"""
    print(f"{tag:<20} -> {lst} ") # {tag:<20} 表示 tag 左对齐,占 20 个字符宽度
In [ ]:
# --- 1:添加类操作 append / extend / insert ---

items = ["INV1001", "INV1002"]
print("start items:", items)

show("before append", items)

items.append("INV1003")  # 末尾追加 1 个元素
show("after append", items)

items.extend(["INV1004", "INV1005"])  # 末尾追加多个元素(逐个加入)
show("after extend", items)

items.extend("INV1006") # 尝试用extend追加一个元素,但是extend将字符串作为一个可迭代对象,逐个字符追加到列表中
show("after extend with a string", items)

items.insert(1, "INV9999")  # 在索引 1 插入
show("after insert(1)", items)
In [ ]:
# --- 2:删除类操作 pop / pop(i) / remove / clear / del ---

items = ["INV1001","INV9999", "INV1002", "INV1003", "INV1004","INV1005"]
show("start for delete", items)

# pop():删除末尾,并返回被删元素
last = items.pop()
show("after pop()", items)
print("popped last:", last)

# pop(i):删除索引 i,并返回被删元素
mid = items.pop(1)
show("after pop(1)", items)
print("popped index 1:", mid)

# remove(x):按值删除(删除第一个匹配项)
items.remove("INV1002")
show("after remove INV1002", items)

# del:按索引删除(不会返回被删元素)
# 注意:del 只能按位置删,不能像 remove 那样“按值删”
del items[0]
show("after del items[0]", items)

# 下面这行如果执行,会报错(因为列表里没有这个值)
# items.remove("INV_NOT_EXIST")   # ValueError: list.remove(x): x not in list
In [ ]:
# --- 3:查询类操作 len / in / index / count ---

items = ["INV1001", "INV1001", "INV1003", "INV1002"]
show("reset items", items)

print("len(items) =", len(items)) # 列表长度

print("'INV1003' in items ?", "INV1003" in items) # 判断某个元素是否在列表中,返回 True 或 False
print("'INV9999' in items ?", "INV9999" in items) # 判断某个元素是否在列表中,返回 True 或 False

print("items.count('INV1001') =", items.count("INV1001"))  # 出现次数
print("items.index('INV1002') =", items.index("INV1002"))  # 首次出现位置

#items.index("INV_NOT_EXIST") # index 找不到会报错 
In [ ]:
# --- 4:排序 sort / sorted(对比“是否修改原列表”) ---


items = ["INV1003", "INV1001", "INV1002"]
show("reset items", items)

# 1) 原地排序:sort() 会改变 items 本身,返回 None
ret = items.sort()
show("after items.sort()", items)
print("sort() return:", ret)

# 2) sorted(...) 返回新列表,不改变原列表
items = ["INV1003", "INV1001", "INV1002"]
show("reset items", items)

new_items = sorted(items)
show("original items", items)
show("sorted(items)", new_items)

# 3) 降序
desc_items = sorted(items, reverse=True)
show("sorted desc", desc_items)

# 4) reverse():原地反转(不排序)
items.reverse()
show("after items.reverse()", items)
In [ ]:
# --- 小练习:现金账户流水分析 ---

# 场景:模拟“库存现金”账户的一组流水记录(单位:元)。
# 正数代表增加(收款),负数代表减少(支出/付款)。
cash_journal = [1000, -200, 500, -200, 3000, -150, 1000]
print("现金日记账流水:", cash_journal)

# -----------------------------------------------------------
# 任务 1:审计检查
# 请检查账目中是否包含一笔 -5000 的大额支出?(打印结果应该为 False)
# [在此处编写代码]


# -----------------------------------------------------------
# 任务 2:统计频次
# 统计金额为 1000 的收款在流水中出现了几次?
# [在此处编写代码]


# -----------------------------------------------------------
# 任务 3:整理账目
# 将所有流水按“从大到小”(金额高低)进行排序,可以快速看到最大的流入和流出。
# 请打印排序后的列表。
# [在此处编写代码]

请双击阅读参考答案

2.5.5 可变类型与赋值拷贝¶

1. 先区分:原地修改 vs 返回新列表¶

有些列表操作会直接改原来的列表;有些操作会生成一个新列表,把结果放在新对象里。

  • 原地修改: 原列表本身变了,常见如 append()、sort()、reverse()。
  • 返回新列表: 原列表不动,结果放在另一个列表里,常见如 sorted(lst)、切片 lst[:]、拼接 lst1 + lst2。

下面的代码先用 append 和 sorted 对比这两件事。

In [ ]:
# --- 演示 1:原地修改 vs 返回新列表 ---

print("=== 1. 原地修改 append vs 返回新列表 sorted ===")
items = ["INV1002", "INV1001"]
print("Start:", items, "id =", id(items))

items.append("INV1003")
print("After append:", items, "id =", id(items), "(原列表被修改)")

sorted_items = sorted(items)
print("sorted_items:", sorted_items, "id =", id(sorted_items), "(新列表)")
print("items after sorted:", items, "id =", id(items), "(原列表保持 append 后的状态)")

2. 核心概念:可变 vs 不可变¶

在 Python 中,理解数据的“类型特性”是理解赋值与拷贝的基础。

  • 不可变对象(Immutable): 内容一旦创建不可修改。
    • 类型: 整数(int)、浮点数(float)、字符串(str)、元组(tuple)。
    • 行为: 对变量重新赋值时,会创建一个新对象,并将变量名指向新对象(内存地址改变)。
  • 可变对象(Mutable): 内容可以在原地被修改。
    • 类型: 列表(list)、字典(dict)、集合(set)。
    • 行为: 修改对象内部元素时,对象的内存地址不变。

3. 赋值(Assignment)的本质¶

Python 的赋值运算符 = 不是复制对象,而是建立引用(Binding)。

  • 对于不可变对象: 赋值始终是引用;但由于不可变对象无法原地修改,对一个变量重新赋值会创建新对象并重新绑定变量名,因此看起来互不影响。
  • 对于可变对象: b = a 只是给同一个对象起了个别名(Alias)。修改这个对象时,a 和 b 都会看到变化。
In [ ]:
# --- 演示 2:不可变类型 vs 可变类型的赋值差异 ---

print("\n=== 2. 不可变类型(如 int/str) ===")
a = 100
b = a        # b 引用同一个整数对象
print(f"Start: a={a}, b={b}")
print(f"id(a)={id(a)}, id(b)={id(b)}")  # 地址相同

# 重新赋值 a:整数不能原地改,a 会绑定到另一个整数对象
a = 200
print(f"After 'a=200': a={a}, b={b}")   # b 不受影响,仍指向原来的 100
print(f"id(a)={id(a)}, id(b)={id(b)}")  # a 绑定到新对象


print("\n=== 3. 可变类型(如 list) ===")
list_a = [1, 2, 3]
list_b = list_a  # list_b 引用同一个列表对象(Alias)
print(f"Start: list_a={list_a}, list_b={list_b}")
print(f"id(list_a)={id(list_a)}, id(list_b)={id(list_b)}") # 地址相同

# 修改 list_a 指向的列表内容(原地修改):地址不变
list_a.append(4)
print(f"After append: list_a={list_a}, list_b={list_b}")   # list_b 也会变
print(f"id(list_a)={id(list_a)}, id(list_b)={id(list_b)}") # 地址没变

4. 拷贝(Copy)¶

如果想让两个列表完全独立,需要进行拷贝。

Note

  • list_2 = list_1:引用同一个对象(Alias)。
  • list_1.copy() 或 list[:]:浅拷贝(Shallow Copy),只复制第一层,也就是外层列表本身。
  • copy.deepcopy(list_1):深拷贝(Deep Copy),递归复制所有层级(需要 import copy)。

浅拷贝里的“第一层 / 第二层”可以这样看:

a -> 外层列表 A -> [子列表 X, 子列表 Y]
c -> 外层列表 C -> [子列表 X, 子列表 Y]
                   ↑          ↑
                内层对象仍共享
  • a[0] = 123:替换的是 a 外层列表的第 0 格,浅拷贝 c 的外层列表不变,所以 c[0] 不受影响。
  • a[1][1] = 888:修改的是共享的嵌套列表内部元素,所以 a 和浅拷贝 c 都会看到变化。
In [ ]:
# --- 演示 3:浅拷贝的“第一层 / 第二层” ---
# 第一层:外层列表 a 的元素位置(a[0], a[1], ...)
# 第二层:嵌套列表内部的元素(a[1][1] 这种“第二个子列表里的第二个元素”)

import copy

a = [["INV1001", 100], ["INV1002", 200]]  # 原始列表:元素是“嵌套列表”
b = a                    # alias:不是复制,只是同一个列表的另一个名字
c = a.copy()             # 浅拷贝:只复制第一层(外层列表),第二层仍共享
d = copy.deepcopy(a)     # 深拷贝:第一层和第二层都复制(了解)

print("图示:浅拷贝会新建外层列表,但内层列表仍共享")
print("a -> 外层列表 A -> [子列表 X, 子列表 Y]")
print("c -> 外层列表 C -> [子列表 X, 子列表 Y]")
print("                   ↑          ↑  内层对象共享")

print("\nstart(初始状态)")
print("a:", a)
print("b:", b)
print("c:", c)
print("d:", d)

print("\nPart A:替换外层元素 a[0] = 123")
a[0] = 123
print("a:", a)
print("b:", b)  # b 是 alias,所以会跟着变
print("c:", c)  # c 的外层列表独立,所以 c[0] 不变
print("d:", d)  # d 完全独立,不变

print("\nPart B:修改嵌套元素 a[1][1] = 888")
a[1][1] = 888
print("a:", a)
print("b:", b)  # alias:会变
print("c:", c)  # 浅拷贝:第二层共享,所以也会变
print("d:", d)  # 深拷贝:第二层独立,不变

2.5.6 列表推导式列表推导式用于“快速生成新列表”,语法是:¶

[表达式 for 变量 in 可迭代对象]

也可以加入条件过滤:

[表达式 for 变量 in 可迭代对象 if 条件]

Note

  • 列表推导式会生成一个“新列表”,不修改原列表。
  • 当逻辑很简单时,用列表推导式更清晰;逻辑复杂时,用普通 for 循环更易读。
    --->

练习:列表的特性及操作练习¶

  1. 建立一个列表命名为assets,存储如下字符串:Cash, Receivable, Inventory。
  2. 使用index()方法,将上述列表中的Receivable字符串改为Accounts receivable。
  3. 在Accounts receivable之后插入字符串Notes receivable。

双击阅读参考答案

2.6 元组(Tuple)¶

2.6.1 定义与性质¶

元组(tuple)是一组有序元素,但不可修改(immutable)。常用于表示结构固定的一条记录。

为什么有了列表,还要元组呢?

因为有些数据是结构固定的,比如:一条发票记录包含发票号码、金额、日期等字段,这些字段的含义和位置是固定的,不需要修改。使用元组可以更好地表达这种“结构固定”的数据概念,同时也能节省内存和提高性能。

  • 函数的参数和返回值

元组只支持查询操作,不支持增删改操作

In [ ]:
# 元组(tuple)常用于表示“结构固定的一条记录”
# 例如:一笔交易记录 (date, invoice_id, account, amount)

tx = ("2026-03-01", "INV1001", "Sales", 1200.50)

print(tx)        # 整条记录
print(tx[1])     # 通过索引访问:发票号

# 解包(unpacking):把元组按位置拆成多个变量
date, inv, acct, amt = tx
print(date, inv, acct, amt)

# 查询类操作:len / in / index / count
print("len(tx) =", len(tx))  # 元组长度
print("tx[0:2] =", tx[0:2])  # 切片:取前两个元素
print("'INV1001' in tx ?", "INV1001" in tx)  # 判断某个元素是否在元组中,返回 True 或 False
print("tx.index('Sales') =", tx.index("Sales"))  # 首次出现位置
print("tx.count('2026-03-01') =", tx.count("2026-03-01"))  # 出现次数

2.7 字典(Dictionary)¶

2.7.1 定义与性质¶

字典(dict)是一组键值对(key-value pairs),

基本格式: {key1: value1, key2: value2, ...}

具有以下特点:

  • 键(key)必须唯一,值(value)可以重复
  • 通过 key 快速访问 value
  • 适合做“映射表”“分类汇总”“计数统计”
In [ ]:
# 键必须唯一
dict1 = {"INV1001": 1200.50, "INV1002": 850.00}
print(dict1)

dict1 = {"INV1001": 1200.50, "INV1002": 850.00, "INV1001": 1300.00} # 键重复,后一个覆盖前一个
print(dict1)

2.7.2 常用字典方法¶

方法/写法 作用 备注
d[key] 按键取值 key 不存在会报错
d.get(key, default) 安全取值 key 不存在返回 default
d.keys() 所有键 可用于遍历键
d.values() 所有值 可用于遍历值
d.items() (key, value) 对 最常用于遍历
d.update(other) 批量更新/合并 other 可以是 dict 或键值对序列
d.setdefault(key, default) 若 key 不存在则插入 返回该 key 的值
d.pop(key[, default]) 删除并返回值 可提供 default 避免报错
d.popitem() 删除并返回一对键值 Python 3.7+ 通常是“最后插入”的键值对
d.clear() 清空字典 变为空字典 {}

Note

  • d[key] 在 key 不存在时会抛出 KeyError;不确定 key 是否存在时优先用 d.get(key, default)。
In [ ]:
# --- 0:定义字典(key -> value) ---

accounts = {
    "Sales": 1700.50,
    "COGS": 630.20,
    "Rent": 15000.00
}
print("Cell 0 | start accounts =", accounts)
In [ ]:
# --- 1:增 (Add) ---
# 核心逻辑:直接赋值(key不存在时)或 update()

print("Initial accounts:", accounts)

# 方式 A:直接赋值(key 不存在 = 新增)
accounts["Utilities"] = 420.00
accounts["Tax"] = 0.00
print("After add Utilities & Tax:", accounts)

# 方式 B:update() 批量新增
accounts.update({"Interest": 55.50, "Other": 100.00})
print("After update/add:", accounts)
In [ ]:
# --- 2:删 (Delete) ---
# 核心逻辑:del (语句) / pop (方法) / clear (清空)

print("Before delete:", accounts)

# 方式 A:del 语句(按键删除,不返回被删值)
if "Other" in accounts:
    del accounts["Other"]
    print("Deleted 'Other'")

# 方式 B:pop() 方法(删除并返回被删值,安全起见可给默认值或先判定)
if "Tax" in accounts:
    removed_tax = accounts.pop("Tax")  
    print("Popped 'Tax':", removed_tax)

# 方式 C:clear() 清空(慎用,这里先不演示清空,以免后面没得玩)
# accounts.clear()
accounts1 = accounts.copy()  # 先复制一份,演示清空
accounts1.clear()  # 清空 accounts1,但不影响原来的 accounts
print("After clear accounts1:", accounts1)


print("After delete:", accounts)
In [ ]:
# --- 3:改 (Modify) ---
# 核心逻辑:直接赋值(key已存在)或 update()

print("Before modify:", accounts)

# 方式 A:直接赋值(key 已存在 = 修改)
accounts["Sales"] = 2000.00  # 原来可能是 1700.50
print("After modify 'Sales':", accounts)

# 方式 B:update() 批量修改
accounts.update({"COGS": 800.00, "Rent": 16000.00})
print("After update/modify:", accounts)
In [ ]:
# --- 4:查 (Query) ---
# 核心逻辑:[] / get() / in / keys() / values() / items()

# 1) 按键查询
print("Sales:", accounts["Sales"])
# print(accounts["Unknown"])  # 键不存在会报错 KeyError

# 2) get() 安全查询
print("Unknown (get):", accounts.get("Unknown", 0.0))  # 键不存在返回默认值

# 3) 成员判断 (in)
print("Is 'Rent' in accounts?", "Rent" in accounts)

# 4) 查看结构:keys(), values(), items()
print("All keys:  ", list(accounts.keys()))
print("All values:", list(accounts.values()))
print("All items: ", list(accounts.items()))

练习:字典的特性及操作练习¶

建立一个字典,存储下列会计科目表的信息。注:标题行的信息无需存入该字典。

编号 科目名称
1001 Cash
1002 Bank deposits
1015 Other cash
6666 Stock price
1122 Accounts Receivable
  1. 添加一个元素,编号1241,科目名称为:Allowance for bad debts。
  2. 删除Stock price科目。

双击阅读参考答案

2.8 集合(Set)¶

2.8.1 定义与性质¶

集合(set)是一组无序且不重复的元素,常用于去重与集合运算。

  • 无序(unordered)
  • 元素唯一(unique)

Note

  • 集合是无序的:打印/遍历的顺序不保证稳定。
  • 如果你需要稳定顺序,可以用 sorted(s) 得到一个“排好序的列表”。

2.8.2 常用集合操作¶

集合因为无序性,没有索引和切片操作,但支持丰富的集合运算:

操作 写法 说明
去重 set(list_like) 把列表/元组等转为集合,自动去重
成员判断 x in s 判断元素是否在集合中
交集 A & B 两个集合共同拥有的元素
并集 A \| B 两个集合所有元素(自动去重)
差集 A - B 在 A 中但不在 B 中的元素
对称差 A ^ B 只在其中一个集合出现的元素
In [ ]:
# 集合(set)常用于“去重”和“集合运算”
invoice_ids = ["INV1001", "INV1002", "INV1001", "INV1003"]

unique_ids = set(invoice_ids)  # 去重

# 成员判断(in)
print("INV1002" in unique_ids)

# 集合运算:交集、并集等

# 集合和字典都使用花括号 {} 定义,但集合没有键值对,只有元素(无序且唯一)
A = {"INV1", "INV2", "INV3"}
B = {"INV2", "INV4"}

# 若要定义空集合,不能直接用 {},因为 {} 是空字典的语法
empty_set = set()  # 定义一个空集合
print("Empty set:", empty_set)

print("A & B:", A & B)   # 交集
print("A | B:", A | B)   # 并集
print("A - B:", A - B)   # 差集

2.9 类型转换(Type Conversion)¶

不同数据类型之间可以相互转换。常见的转换函数有:

  • 基础类型: int(), float(), str(), bool()
  • 容器类型: list(), tuple(), set(), dict()

Note
类型转换常用于数据清洗。例如:将字符串数字转为整数计算,或利用集合去除列表中的重复项。

In [ ]:
# --- 1:基础类型转换 (int / float / str) ---
# 场景:从文件读取的数据往往是字符串,需要转为数字进行计算

s_num = "100"
f_num = 3.14159

# 字符串 -> 整数
i_val = int(s_num)
print(f"str '{s_num}' -> int {i_val}, type: {type(i_val)}")

# 浮点数 -> 整数 (截断小数部分,不四舍五入)
i_from_float = int(f_num)
print(f"float {f_num} -> int {i_from_float} (truncated)")

# 数字 -> 字符串
s_val = str(f_num)
print(f"float {f_num} -> str '{s_val}', type: {type(s_val)}")

# 非法转换示例
# int("abc")  # ValueError: invalid literal for int() with base 10: 'abc'
In [ ]:
# --- 2:容器类型转换 (list / tuple / set) ---
# 场景:去重、转换为不可变对象作为字典键等

data = [1, 2, 2, 3, 3, 3]

# list -> set (去重)
unique_data = set(data)
print(f"list {data} -> set {unique_data}")

# set -> list (变回列表,方便索引访问)
clean_list = list(unique_data)
print(f"set -> list {clean_list}")

# list/tuple 互转
t_data = tuple(data)
print(f"list -> tuple {t_data} (immutable)")

l_from_t = list(t_data)
print(f"tuple -> list {l_from_t} (mutable)")

# zip -> dict (两个列表合并为字典)
keys = ["name", "age", "role"]
values = ["Alice", 30, "Dev"]
user_dict = dict(zip(keys, values))
print(f"lists -> dict {user_dict}")

【练习:年龄计算器】

请编写代码实现以下功能:

  1. 使用 input() 函数提示用户输入出生年份。
  2. 将得到的输入(字符串类型)转换为整数类型。
  3. 计算并输出该用户当前的年龄(以 2026 年为准)。

双击阅读参考答案