Python知识

我体会到的变化

  • 没有 i++、i-- 的语法糖
  • 严格缩进,:和缩进代表作用域
  • 变量不用也不能初始化类型声明(动态类型)
    • 变量本身没有固定类型,类型属于值(对象),而不是变量名。
    • 同一个变量可以在程序运行过程中绑定到不同类型的对象。
  • 不用写;()
  • 没有 NULL 而是 None
  • 无穷不是 INT_MIN 和 INT_MAX 而是 inf 和 -inf(infinite)
  • 三目运算符<结果1> if <条件> else <结果2>条件为真返回结果 1
  • 没有!来取反,而是not
  • 交换没有swap()函数,而是a, b = b, a用元组整体赋值,交换元组内部元素指向
  • 没有else if,而是elif
  • 可以连等赋初值a = b = 1
  • python 的除法默认浮点计算,用//才是整除
  • 排序方法为sorted()
  • nums1 = nums2不能给外部数组整个赋值,要用切片nums[:] = res
  • 0 <= x < n and 0 <= y < m可以直接写范围,不用分开写
  • 没有->,都是直接用.来访问成员,所有变量本质上都是对象的引用
  • 类中只有用self.val声明的val才能成为属性,所有属性调用也要加self.
  • 类的实例方法第一个参数必须是self
  • 在[a, b]中生成随机整数randint(a, b),引入from random import randint

基础知识

  • C++/Java 是静态类型语言 + 编译期绑定,先完整编译整个文件,然后运行。

  • Python 是动态类型 + 运行时绑定,逐行解释执行,执行到 def 才创建函数对象

  • Python 在初始化变量时不需要(也不能)显式声明类型。这是 Python 作为动态类型语言(dynamically typed language)的核心特性之一。Python 会在运行时自动推断并绑定变量的类型,你只需要写 变量名 = 值 即可。

    1
    2
    3
    a = 42          # a 是 int
    a = "now a string" # 现在 a 是 str
    a = [1, 2, 3] # 现在 a 是 list
  • Python 没有 C++ 的“值传递”或“引用传递”语法。所有参数传递都是 “传对象引用”(Pass-by-object-reference):

  • Python 中所有变量都是“引用”(类似 C++ 的指针,但更安全)。

  • 赋值 a = b:若 b 是可变对象(如 list),修改 a 会影响 b。

排序方法

1
2
3
4
5
# 给一个可迭代对象,返回一个排序后的列表(List),可选参数 key 指定排序规则,reverse 指定排序顺序
sorted(iterable, key=None, reverse=False)
sorted(s)
sorted(s, reverse=True) # 降序
sorted([-2, 3, -1], key=abs) # 按绝对值排序

列表(List)(栈)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
lst = [0] * n               # 创建长度为 n、元素全为 0 的列表
lst = [1 for _ in range(n)] # 推导式初始化
lst.append(x) # 末尾添加元素 O(1)
lst.pop() # 弹出末尾元素 O(1)
lst.pop(0) # 弹出首元素 O(n),慎用!
lst.insert(0, x) # 在开头插入 O(n)
lst.remove(x) # 删除第一个匹配的元素
lst.clear() # 清空列表
lst.index(x) # 返回第一个匹配的索引
lst[i] # 访问第 i 个元素
lst[-1] # 最后一个元素(等价于 C++ vec.back())(也等价于top())
lst[1:4] # 切片,返回一个新liset,子列表[1,4),从索引1开始,不包括索引4
len(lst) # 获取长度(等价于 .size())
lst.reverse() # 原地翻转(等价于 ranges::reverse)
lst.sort() # 原地排序(升序)
lst.sort(reverse=True) # 原地排序(降序)
lst.sort(key=lambda p: p[0])# 按照左端点从小到大排序
sorted(lst) # 返回新排序列表(不修改原列表)

二维列表

1
2
3
4
5
6
7
8
9
# 创建一个 m 行 n 列的二维矩阵

matrix = [[0] * n for _ in range(m)]

[
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
]

list可以直接充当栈

1
2
3
4
stack = []
stack.append(x1)
stack.append(x2)
x = stack.pop()

列表推导式

把“循环 + 条件 + 表达式”压缩成一行代码

1
2
3
4
5
6
7
8
9
10
# 基本写法
[表达式 for 变量 in 可迭代对象]
# 带条件
[表达式 for 变量 in 可迭代对象 if 条件]

# 等价于:
result = []
for 变量 in 可迭代对象:
if 条件: # (可选)
result.append(表达式)

for 循环

  1. 遍历列表

    1
    2
    3
    nums = [1, 2, 3]
    for x in nums:
    print(x) # 1, 2, 3
  2. 遍历字符串

    1
    2
    3
    s = "abc"
    for c in s:
    print(c) # 'a', 'b', 'c'
  3. 遍历范围(range)

    类似 C++ 的 for (int i = 0; i < n; i++)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    # for (int i = 0; i < 5; ++i)
    for i in range(5):
    print(i) # 0,1,2,3,4

    # for (int i = 1; i <= 5; ++i)
    for i in range(1, 6):
    print(i) # 1,2,3,4,5

    # for (int i = 0; i < 10; i += 2)
    for i in range(0, 10, 2):
    print(i) # 0,2,4,6,8

    # 如果不需要索引,只注重循环次数可以用`_`
    # 循环 n 次
    for _ in range(n):
    print(i)
  4. 用索引的方式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    for i in range(len(nums)):
    nums[i] = 0

    # 倒序遍历
    # range()就相当于 for (int i = n - 1, i != -1. i--)
    # 第二个参数是到 -1 时停止,第三个是每次 i += -1
    for i in range(len(nums) - 1, -1 ,-1):
    if nums[i] == val:
    nums.pop(i)
    return len(nums)
  5. 同时获取索引和值enumerate()(枚举)

    1
    2
    3
    4
    5
    6
    7
    8
    nums = [10, 20, 30]
    for i, x in enumerate(nums):
    print(i, x)
    # 输出: 0 10, 1 20, 2 30

    # 从指定索引开始
    for i, x in enumerate(nums, start=1):
    print(i, x) # 1 10, 2 20, 3 30
  6. 多维列表遍历

    1
    2
    3
    4
    5
    6
    7
    8
    matrix = [[1,2], [3,4]]
    # 展开所有元素
    for row in matrix:
    for x in row:
    print(x) # 1,2,3,4

    # 列表推导式写法
    [x for row in matrix for x in row]
  7. 多维列表创建

    1
    2
    3
    # for _ in 的意义就是只强调循环次数,不强调索引
    # 这就是标准创建多维数组方式,重复创建m次长为n的数组,
    matrix = [[0]*n for _ in range(m)]

元组

也叫只读列表,不像对(pair)只能存两个,元组可以存多个元素,比如 a = (1, 2, 3, 5)
不可变序列,常用于坐标、键值对等

1
2
t = (x, y)
x, y = t # 解包(类似 C++ 结构化绑定)

字典(哈希表)

键必须是可哈希的(不可变类型)
值可以是任意类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
d = {}
d[key] = value # 插入或更新
del d[key] # 删除
val = d.get(key, default) # 安全访问,不存在返回 default(不会插入!)
if key in d: # 判断是否存在(推荐!)
...
for k, v in d.items(): # 遍历键值对(类似 C++ for (auto& [k, v] : map))
print(k, v)

d.keys() # 获取所有键
d.values() # 获取所有值
d.items() # 获取所有键值对
for k in d: # 遍历键
for v in d.values(): # 遍历值
for k, v in d.items(): # 遍历键值对
return list(d.values()) # 哈希表的 value 保存分组后的结果
d = defaultdict(list) # 设置默认值,如果键不存在,则创建一个空列表作为值
d = defaultdict(int) # 默认值为0
d = defaultdict(lambda: None)# 如果想访问不存在的key时返回默认值

cnt_p = Counter(p) # 返回一个统计了 p 中的每种字母出现频率的字典
cnt_s = Counter() # 创建一个空字典,用于统计,Counter对象在比较频率时会自动忽略值为0的键 cnt_s == cnt_p

例子

1
2
3
4
5
6
7
8
student = {
"name": "张三",
"age": 18,
"grades": [85, 92, 78],
"is_enrolled": True
}
print(student["name"]) # 输出: 张三
print(student["grades"]) # 输出: [85, 92, 78]

集合(set)

自动去重(唯一性)、无序

1
2
3
4
5
6
7
8
9
s = {1, 2, 2, 3}    # set也是花括号,但存的不是键值对,{}永远都是空字典,只有{1}才表示一个空集合
print(s) # 输出:{1, 2, 3}
s = set() # 只有set()能创建一个空集合
s = set(nums) # 将列表转为哈希集合
s.add(x) # 添加
s.discard(x) # 删除(不存在也不报错)
s.remove(x) # 删除(不存在会报错)
if x in s: # O(1) 查找
...

队列(queue)

1
2
3
4
5
6
7
8
9
import queue
# 创建普通队列
q = queue.Queue()
# 入队
q.put(1)
q.put(2)
# 出队 (FIFO: 先入先出)
item = q.get()
print(item) # 输出: 1

双端队列(deque)

1
2
3
4
5
6
7
from collections import deque
q = deque()
q.append(x) # 右端入队(O(1))
q.appendleft(x) # 左端入队(O(1))
q.pop() # 右端出队(O(1))
q.popleft() # 左端出队(O(1))
q[0], q[-1] # 访问首尾元素

字符串(str)

不可变!拼接大量字符串用 list + join

1
2
3
4
5
6
7
8
s = "hello"
s[0], s[-1] # 首尾字符
s[1:4] # 子串 [1,4)
s[::-1] # 反转字符串
s1 + s2 # 拼接字符串
''.join([s1, s2]) # 高效拼接
s.find(sub) # 返回子串首次出现的索引,未找到返回 -1
sorted(s) # 排序

堆(heap)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import heapq
hp = [] # 创建一个空堆(用的list)
heapq.heappush(hp, x) # 入堆(此时list会变成堆结构,默认最小堆)
heapq.heappush(hp, (k, v)) # 放入元组,按照 k 排序
x = heapq.heappop(hp) # 弹出最小值
x = hp[0] # 访问最小值(堆顶)数组是层序遍历
heapq.heappushpop(hp, num) # 放入一个元素,并弹出最小值,可以接收弹出的元素
hp[0] # 查看最小值(不弹出)

# 建立最大堆技巧:存负数
heapq.heappush(hp, -x)
max_val = -heapq.heappop(hp) # 取的时候取反

# 灵神直接用的最大堆函数
heappush_max(self.left, heappushpop(self.right, num)) # left是最小堆

函数

1
2
3
def func(x : int):
print(x + 1)
return x + 1

变量作用域

  • 外部声明的变量,内部是可以直接访问值的,但是不能修改
  • 函数内未被修饰的变量都是只属于这个函数的,假设内外部有一个变量m,如果内部再赋值m = 1,这会在内部创建一个新的变量,外部的m不会被修改
  • 想要修改外部变量,要么用 global 修饰,或者用 nonlocal 修饰,指定这个变量的作用域
  • 要么这个length改成一个可变对象,外部声明用length = [0],内部使用用length[0],可变对象比如列表,字典,set,dict,在函数中的修改也是全局的lst[0] = 1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution:
def diameterOfBinaryTree(self, root: Optional[TreeNode]) -> int:
length = 0 # 外部作用域
# 递归函数
def dfs(root) -> int:
# nonlocal length 加上这个就可以修改外部作用域的length
if root is None:
return 0
left = dfs(root.left)
right = dfs(root.right)
length = max(length, left + right) # 这个length就和外部没有联系
return 1 + max(left, right)
dfs(root)
return length # 因为没联系,所以最后返回的是0

如果要修改外部变量,用 global 现在函数里声明

1
2
3
4
5
6
7
8
DAY = 0

def func():
global DAY
print(DAY)
Day += 1

func()

不定长参数,如果一个函数不确定接收的参数个数,可用 *args
args 是 “arguments” 的缩写,意思是 “参数” 或 “实参”
还有一种包裹关键字的**kwargs

1
2
3
4
5
def func(*args):
for i in args:
print(i)

func(1, 2, 3)

特殊方法

被双下划线包围的方法__xxx__,被称为特殊方法,也常被叫做魔术方法
他是在特定场景下自动调用的钩子函数,用于定义类的行为

  1. 对象创建与初始化

    __new__(cls, …) 真正创建对象的静态方法(在 __init__ 之前调用),通常用于控制实例创建(如单例模式)。
    __init__(self, …) 初始化已创建的对象(最常用)。
    __del__(self) 析构方法,在对象被销毁前调用(不保证一定会被调用,依赖垃圾回收机制)。

    比如定义一个节点类,类似于 C++里面的结构体,python 的结构体和类都是用 class

    1
    2
    3
    4
    5
    6
    7
    class TreeNode:
    # 给每个属性赋初值
    def __init__(self, val=0, left=None, right=None):
    # python类的属性不需要声明,直接赋值就行
    self.val = val
    self.left = left
    self.right = right
  2. 别的等用到再看吧

DFS 模板(递归)

1
2
3
4
5
6
7
8
def dfs(i, j):
if not (0 <= i < m and 0 <= j < n): return
if grid[i][j] != '1': return
grid[i][j] = '2' # 标记已访问
dfs(i+1, j)
dfs(i-1, j)
dfs(i, j+1)
dfs(i, j-1)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution:
def maxProfit(self, prices: List[int]) -> int:
n = len(prices)
@cache # 缓存
def dfs(i : int, hold : bool) -> int: # i 为第 i 天结束时的状态,hold为是否持股
# 第 i - 1 天的结束是第 i 天的开始
if i < 0:
# -1 代表第 0 天的时候开始。此时不该持有股票,用负无穷代表不合法
return -inf if hold else 0
if hold:
# 如果今天结束时持有股票,就代表前一天本就有股票或今天买了股票
return max(dfs(i - 1, True), dfs(i - 1, False) - prices[i])
else:
# 如果今天结束时没有股票,就代表前一天本就没有股票或今天卖了股票
return max(dfs(i - 1, False), dfs(i - 1, True) + prices[i])
# 最后倒着递归
return dfs(n - 1, False)

BFS 模板(用 deque)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from collections import deque

def level_order(root):
"""二叉树层序遍历,返回每层节点值的列表"""
if not root:
return []

res = []
q = deque([root])

while q:
level_size = len(q) # 当前层的节点数
level = []

for _ in range(level_size):
node = q.popleft()
level.append(node.val)

if node.left:
q.append(node.left)
if node.right:
q.append(node.right)

res.append(level)

return res
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
def solution(tasks):
# 整理tasks
q = deque() # 用队列用来存储所有入度为0的任务,之后用来循环遍历
ans = [] # 存储任务执行顺序
lst_in = defaultdict(int) # 目前所有任务的入度
mapper = defaultdict(list) # 任务反向依赖关系,用来在执行完任务后将依赖他的任务入度减一

for task in tasks:
# 依赖的任务数量就是入度,id作为key
lst_in[task[0]] = len(task[1])
for t in task[1]:
mapper[t].append(task[0])
# 如果入度为0就可以直接执行
if len(task[1]) == 0:
q.append(task[0])
ans.append(task[0])

# 然后遍历队列,直到队列为空
while q:
n = len(q)
for _ in range(n):
task_id = q.popleft()
# 给依赖当前任务的任务入度-1
for t in mapper[task_id]:
lst_in[t] -= 1
# 如果入度为0就可以执行了,加入队列
if lst_in[t] == 0:
q.append(t)
ans.append(t)

return ans if len(ans) == len(tasks) else []

定义链表节点类

1
2
3
4
5
6
7
8
class ListNode:
# 构造方法
def __init__(self, val=0, next=None):# 这里面设置的为默认值
self.val = val # 右面的val就是传进来的参数
self.next = next

ListNode(1) # 创建一个节点,next为None
ListNode(1, None) # 手动传一个节点

双向链表节点类

1
2
3
4
5
6
# 定义双向链表节点
class Node:
def __init__(self, val=0, next=None, prev=None):
self.val = val
self.next = next
self.prev = prev

acm模式

  • map转成int列表nums = list(map(int, line.split()))
  • 去掉换行符,开头结尾的换行符s.strip()
  • 按,分割s.split(',')

a+b(1)

模板,输入a和b,输出a+b

1
2
3
4
5
6
7
8
9
10
11
12
13
import sys

# 循环检测所有输入输出流
for line in sys.stdin:
# 以空格为分隔符,将字符串分割成一个列表
a = line.split()
# 打印每一行的结果
print(int(a[0]) + int(a[1]))

# 输入数据
# 1 2
# 10 20
# 100 200

提取成函数

1
2
3
4
5
6
7
8
9
10
11
import sys

# 1. 只提取核心计算逻辑,保持最简
def add(a, b):
return a + b

# 2. 主循环保持原本的简洁风格
for line in sys.stdin:
parts = line.split()
# 假设输入绝对合法,直接转换并调用函数
print(add(int(parts[0]), int(parts[1])))

a+b(2)

这次第一个输入的是组数,写下来输入才是之前的数据
用readline先把第一个数读走,然后依这个数作为循环次数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import sys

# 1. 先读取第一行,获取测试用例的组数 t
# input() 读取一行并去掉末尾换行符,int() 转为整数
t = int(sys.stdin.readline())

# 2. 循环 t 次
for _ in range(t):
# 读取每一行的两个数
line = sys.stdin.readline()
a, b = map(int, line.split())

# 打印结果
print(a + b)

python项目

py环境配置

去py官网下一个最新的稳定版,然后设置系统变量,pycharm的py解释器也指定为这个就行
pip会绑定一个py版本,之后换的时候要重新设置,不然用的还是旧版
系统变量里面旧的也要更新

venv文件夹

每个项目运行前都该创建venv文件夹,作为虚拟环境,这是根据python版本来的
python -m venv venv

打包

【使用 PyInstaller】

py -m PyInstaller --onefile --name bili-music-list --paths src src\bili_music_list\cli.py
打包GUI
py -m PyInstaller --onefile --name bili-music-list-gui --paths src src\bili_music_list\gui.py


Python知识
http://www.981928.xyz/2026/01/01/Python知识/
作者
981928
发布于
2026年1月1日
许可协议