#1 算法学习 - Python基础
环境
➜ ~ python --version
Python 3.13.5
➜ ~ uv --version
uv 0.8.11 (f892276ac 2025-08-14)
➜ ~ which python
/Users/laoer/.local/share/uv/tools/ruff/bin/python
➜ ~ which uv
/Users/laoer/.local/bin/uv
标准输出
在 Python 中,标准输出使用 print
函数在控制台打印内容。print
默认会在末尾换行,如果想取消换行,可以使用参数 end=“”
。
a = 10
# 输出:10
print(a)
# 串联输出(通过字符串拼接或逗号分隔)
# 输出:Hello, World!
print("Hello" + ", " + "World!")
# 使用 sep 指定分隔符
print("Hello", "World!", sep=", ")
s = "abc"
# 输出:abc 10
print(s, a)
# 格式化输出
# 输出:abc 10
print(f"{s} {a}")
在上述的示例中,sep
参数只在一次 print
调用里输出“多个对象”时才生效。比如:
>>> print("hello"+"world!",sep="+")
helloworld!
>>> print("hello" "world!",sep=" ")
helloworld!
可以看到这两个示例,sep
参数并没有起作用,是因为"hello"+"world!"
(运行时执行字符串拼接,生成单个字符串 "helloworld!"
)和"hello" "world!"
( 属于相邻字面量自动合并(implicit concatenation),在编译阶段就被合并成单个字符串 "helloworld!"
,运行时根本看不见两个独立的 "hello"
和 "world!"
)只会被看作是一个字符串,正确的做法是把两个字符串作为两个独立参数穿进去。比如:
>>> print("hello","world!",sep="+")
hello+world!
>>> print("hello","world!",sep=" ")
hello world!
控制语句
Python 中的控制语句包括条件判断和循环。
条件判断 if
,elif
,else
a = 10
if a > 5:
print("a > 5")
elif a == 5:
print("a == 5")
else:
print("a < 5")
# 输出:a > 5
循环 for
/ while
for
循环通常用于遍历可迭代对象或已知范围的序列,while
循环用于满足条件时反复执行代码块。
# 输出:0 1 2 3 4
for i in range(5):
print(i, end=" ")
print()
num = 100
# 输出:100 50 25 12 6 3 1
while num > 0:
print(num, end=" ")
num //= 2
print()
关于 for
循环部分:
for i in range(5)
:
range(5)
会依次产生整数 0、1、2、3、4(共 5 个)。每产生一个整数,就把它赋给循环变量
i
,然后进入循环体一次。
print(i, end=" ")
print
把当前的i
打印出来。关键在
end=" "
:它告诉print
打印完后不要换行,而是在末尾留一个空格。因此
5
次循环会连续打印在同一行:0 1 2 3 4
(末尾多一个空格)。
print()
不带任何参数,只输出一个换行符,把光标移到下一行,防止后续输出继续粘在刚才那行后面。
关于 while
循环部分:
num = 100
把变量
num
初始化为 100。
while num > 0:
只要
num
大于 0 就继续循环。
print(num, end=" ")
打印当前 num 的值,并在末尾留一个空格(不换行)。
num //= 2
把
num
整除 2(向下取整)后再存回num
。
print()
循环结束后输出一个换行符。
特别注意,num //=2
中 //=
之间不能有空格,否则 Python 会把 //
当成单行注释符,后面的 = 2
被注释掉,于是解释器看到 num
后面突然冒出一个 =
,结果抛出如下错误(语法错误):
/Users/laoer/Documents/learn-python/.venv/bin/python /Users/laoer/Documents/learn-python/python_basic.py
File "/Users/laoer/Documents/learn-python/python_basic.py", line 5
num// = 2
^
SyntaxError: invalid syntax
同时,num
与 //=
之间最好是能空一格,因为 PEP 8(官方排版说明书) 推荐运算符前后留空格。
关于 for 与 while 的区别:
for :走“可迭代对象”的流水线——有多少元素就转多少圈,圈数事先可算(除非中途 break)。
while :走“布尔信号灯”的单行道——只要灯还绿就一直走,圈数完全取决于条件什么时候变 False。
for
适合如下场景:
遍历序列、文件、生成器、range
列表/字典/集合推导式
计数循环(配合
enumerate
,zip
)
while
适合如下场景:
不确定次数的等待:
while not queue.empty()
交互式输入:
while (cmd := input('> ')) != 'quit'
轮询/重试:网络重连、硬件状态检测
状态机:游戏主循环、协议解析
通过以下简单的示例可以看出 for
与 while
在语法和执行流程的差异。
# ===== 1) for 循环:遍历已知范围 =====
total_for = 0
for i in range(1, 11): # 范围写死,循环 10 次
total_for += i
print('for 结果:', total_for)
# ===== 2) while 循环:条件驱动 =====
total_while = 0
n = 1
while n <= 10: # 条件满足就继续
total_while += n
n += 1 # 必须手动更新变量,否则死循环
print('while 结果:', total_while)
输出结果如下:
for 结果: 55
while 结果: 55
for
:range(1, 11)
把次数“摆好”,循环变量 i 自动更新。while
:只有自己写的n <= 10
和n += 1
在控制“何时停、下一步是谁”。
基本数据结构
Python 提供了丰富的内置数据结构,如 list
、deque
(/dek/)、dict
( /dɪkt/)、set
等。以下是一些常用数据结构的介绍及其使用方法。
列表 list(动态数据)
list
是 Python 的可变序列类型,可以用作动态数组。
“可变序列(mutable sequence)”——抽象层面
list 支持原地修改(append、pop、insert、sort…)而不需要重新创建对象。
对比 tuple、str 等不可变序列,它们一旦创建内容就固定;list 可以随时增删改。
“动态数组(dynamic array)”——实现层面
CPython 在 C 里用一块连续的内存缓冲区(C 的 PyObject *
指针数组)来存放元素引用。
当缓冲区满时,内部会重新申请一块更大的连续内存(通常按 0、4、8、16、25、35… 的过度分配策略增长),然后把旧元素整体搬到新地址——这正是传统“动态数组”做的事情。
对外暴露的接口却是“链表式”的随意插入/删除,但底层仍然是O(1)随机索引、O(1)尾部追加均摊的动态数组特性。
初始化方法:
# 初始化一个空列表
nums = []
# 初始化一个包含元素 1, 3, 5 的列表
nums = [1, 3, 5]
# 初始化大小为 n,元素都为0的列表
n = 10
nums = [0] * n
# 二维列表,m 行 n 列,元素都为 1
m, n = 3, 4
matrix = [[1] * n for _ in range(m)]
输出结果
[1, 3, 5]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
Python 中列表的常用方法:
nums = [0] * 10
# 输出:False
print(len(nums) == 0)
# 输出:10
print(len(nums))
# 在列表尾部插入一个元素 20
nums.append(20)
# 输出:11
print(len(nums))
# 得到列表最后一个元素,输出:20
print(nums[-1])
# 删除列表的最后一个元素
nums.pop()
# 输出:10
print(len(nums))
# 索引访问与修改
nums[0] = 11
# 输出:11
print(nums[0])
# 在索引 3 处插入一个元素 99
nums.insert(3, 99)
# 删除索引 2 处的元素
nums.pop(2)
# 交换 nums[0] 和 nums[1]
nums[0], nums[1] = nums[1], nums[0]
# 遍历列表
# 输出示例:
# 0 11 99 0 0 0 0 0 0 0
for num in nums:
print(num, end=" ")
print()
在上述示例中,需要着重说明的是matrix = [[1] * n for _ in range(m)]
,这是列表推导式的写法,是 Python 用来“在一行里生成新列表”的语法糖,核心是把传统循环 + 条件过滤 + 结果收集浓缩成一条表达式。它兼具可读性、执行效率和函数式风格。下面从“能做什么 > 语法长什么样 > 怎么运行 > 进阶功能”四个维度展开说明。
能做什么
把任意可迭代对象(列表、元组、集合、字典、字符串、生成器、文件对象……)映射成新列表。
在映射过程中可以过滤元素、嵌套多重循环、原地拆包、调用函数、计算表达式。
结果一定是新的 list,不会修改原对象。
例子:把 0~9 中偶数的平方收集到新列表。
传统写法:
squares = []
for x in range(10):
if x % 2 == 0:
squares.append(x * x)
列表推导式:
squares = [x*x for x in range(10) if x % 2 == 0]
语法结构
[expression for item in iterable if condition] # 单循环
[expression for item1 in iter1 for item2 in iter2 ... if cond] # 多重循环
[expression1 if cond else expression2 for item in iterable] # 三元表达式
阅读顺序:先写“我要生成的元素长什么样(expression)”,再写“从哪拿元素(for … in …)”,最后写“留下哪些(if …)”。
执行流程
step1. 取外层可迭代对象的下一个元素 → 绑定给循环变量。
step2.如果有 if,先判断条件,False 就跳过。
step3.计算 expression,把结果收集到临时列表。
step4.回到 1,直到外层迭代完毕。
step5.返回收集好的完整列表。
进阶功能与示例
多种循环
[(x, y) for x in range(3) for y in 'ab']
# [(0,'a'),(0,'b'),(1,'a'),(1,'b'),(2,'a'),(2,'b')]
嵌套列表展开(flatten /ˈflætən/ )
matrix = [[1,2,3],[4,5,6]]
flat = [item for row in matrix for item in row]
# [1,2,3,4,5,6]
另外补充说明下 [[1]*n]*m
与 [[1]*n for _ in range(m)]
的区别。表面上看,它们的结果一样。如下:
a = [[0]*3]*4
b = [[0]*3 for _ in range(4)]
print(a)
print(b)
输出结果:
[[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
[[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
但实际上,它们有这本质上的区别:
[[1]*n]*m
只创建了一行[1,1,…]
,然后把这个同一个行对象的引用复制了 m 次;[[1]*n for _ in range(m)]
创建了 m 个彼此独立的新行对象
内存结构上的区别:
>>> a = [[0]*3]*4
>>> id(a[0]) == id(a[1]) == id(a[2]) == id(a[3])
True # 四行指向同一块内存
>>> b = [[0]*3 for _ in range(4)]
>>> id(b[0]) == id(b[1])
False # 每行是独立列表
修改上的区别:
>>> a[0][0] = 99
>>> a
[[99, 0, 0], [99, 0, 0], [99, 0, 0], [99, 0, 0]]
>>> b[0][0] = 99
>>> b
[[99, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
因此, 想得到一个真正的 m×n 矩阵,请用列表推导式 [[1]*n for _ in range(m)]
; [[1]*n]*m
只是“看似”正确,实则所有行共享同一内存,极易踩坑。
双端队列 deque
deque
是 collections
模块提供的双端队列,可以高效地在两端插入和删除元素。
评论