参考资料
一、基础与进阶概念
1. *args 和 **kwargs
功能:用于在函数定义时接收不定长度的参数。
*args:接收非键值对的可变数量参数列表,以元组形式存储。
**kwargs:接收不定长度的键值对参数,以**字典**形式存储。
注意:
args 和 kwargs 只是习惯命名,可以替换成其他单词。
- 可以在函数定义时同时使用两者,
*args 必须在 **kwargs 之前。
在函数调用时的使用:
1
2
3
4
5
|
args = ("two", 3, 5)
test_args_kwargs(*args) # 解包元组
kwargs = {"arg3": 3, "arg2": "two", "arg1": 5}
test_args_kwargs(**kwargs) # 解包字典
|
2. self 关键字
- 在类的方法定义中,
self 作为第一个参数,指代新创建的实例化对象本身。
- 可以随便起其他名字,但一般统一写作
self,并放在参数的最前面。
- 相当于 C++ 的
self 指针,Java 的 this 关键字。
调用本质:
1
2
3
4
|
a = Student('小明', 95)
a.get_score()
# 解释器翻译为:
# Student.get_score(a)
|
- 调用实例方法时不需要也不能传入参数
self,解释器会自动传入。
3. 实例属性和方法
- 本质上都可以认为是对象。
- 属性:相当于 class 中的变量。
- 方法:相当于 class 中的函数。
- class 实例化后,可以调用这些属性和方法。
4. 函数 vs. 方法
| 对比项 |
函数 |
方法 |
| 本质 |
完成功能的语句块 |
完成功能的语句块 |
| 定义位置 |
类外部 |
类内部 |
| 调用方式 |
直接调用 func() |
通过 obj.method() 调用 |
| 参数要求 |
无需 self |
必须包含 self(或 cls) |
| 从属性 |
独立存在 |
从属于特定实例对象 |
5. 类中的不同类型方法
| 类型 |
装饰器 |
第一个参数 |
调用方式 |
特点 |
| 实例方法 |
无 |
self |
只能通过实例调用 |
可访问实例属性和类属性 |
| 类方法 |
@classmethod |
cls |
类或实例调用,推荐类调用 |
只能访问类属性和其他类方法 |
| 静态方法 |
@staticmethod |
无 |
类或实例调用 |
不能访问类/实例属性,相当于普通函数 |
注意:
- Python 不支持方法重载,一个类中定义多个同名的方法只有最后一个生效。
- 可以动态地为类添加新方法:
class.method = new_method。
6. 私有化与封装
- 双下划线开头的属性/方法(如
__attr)被约定为私有的,实现封装。
- 若需要在类外部访问私有属性:
_类名__私有属性方法名(名称修饰)。
- 方法本质上也是属性。
7. @property 装饰器
作用:将一个方法的调用转换成属性的调用。
1
2
3
4
5
6
7
8
9
10
|
class Employee:
@property
def salary(self):
return 30000
emp1 = Employee()
print(emp1.salary) # 30000(像属性一样访问,无需括号)
print(type(emp1.salary)) # <class 'int'>
print(emp1.salary()) # 报错:'int' object is not callable
emp1.salary = 20000 # 报错:can't set attribute
|
8. 变量与作用域
LEGB 搜索顺序:
- Local:本地作用域
- Enclosing:当前作用域被嵌入的本地作用域(嵌套函数)
- Global:全局作用域(模块级别)
- Built-in:内置作用域
global 和 nonlocal:
| 关键字 |
使用范围 |
功能 |
global |
任何地方,包括最上层函数和嵌套函数 |
标识变量是全局变量,修改会影响全局 |
nonlocal |
仅限嵌套函数中,且外层函数必须定义该变量 |
标识变量是上一级函数的局部变量 |
注意:减少 global 变量的使用,减少资源消耗。
9. __name__ 变量
- 直接执行:当
.py 文件被直接运行时,__name__ 的值为 '__main__'。
- 作为模块导入:当文件被其他模块导入时,
__name__ 的值是该模块的文件名(不含 .py)。
- 用途:常用于
if __name__ == '__main__': 编写测试代码,在导入时不会被执行。
10. __str__ 和 __repr__
print() 会优先调用 __str__,若未定义则调用 __repr__。
__repr__:主要用于调试和开发,输出包括对象的类型、值等信息。
__str__:输出更加直观和易读,面向用户。
11. __call__() 方法
当需要额外运行某些内容时,可以重写 __call__() 函数:
1
2
3
4
5
6
7
8
9
10
|
class A:
def __call__(self, name):
print(f'我的名字是:{name}')
return self.forward(name)
def forward(self, input_):
print('forward 函数被调用了')
return input_
a = A()
result = a('python') # 实例对象可以像函数一样被调用
|
12. 对象操作相关函数
dir(obj):获取对象的属性、方法列表。
obj.__dict__:以字典形式返回对象的属性。
isinstance(obj, type):判断对象是否是某个类型。
hasattr(obj, attr):判断对象是否包含指定名称的属性或方法。
getattr(obj, attr):获取对象中指定属性的值。
setattr(obj, attr, value):修改属性值或动态添加属性/方法。
13. 模块、包、库、utils
| 概念 |
定义 |
特点 |
| 模块 (Module) |
一个 .py 文件 |
包含函数和变量,规模较小 |
| 包 (Package) |
包含 __init__.py 的文件夹 |
组织多个模块,包本身也是模块 |
| 库 (Library) |
模块或包的集合 |
无严格定义,是通俗说法 |
| utils 文件夹 |
存放工具函数的文件夹 |
提高代码复用性 |
__init__.py 的作用:
- 将一个文件夹变为 Python 模块。
- 可包含初始化代码,在包被 import 时自动执行。
14. PYTHONPATH 环境变量
作用:告诉 Python 解释器在哪些目录下搜索模块。
Python 模块搜索路径顺序:
- 当前工作目录
PYTHONPATH 中的所有目录
- Python 默认安装目录
设置方法:
- Windows (cmd):
set PYTHONPATH=项目根目录 && python run.py
- Windows (PowerShell):
$env:PYTHONPATH="项目根目录" && python run.py
- Linux/Mac:
export PYTHONPATH=项目根目录; python run.py
最佳实践:将项目根目录添加到 PYTHONPATH,便于使用绝对导入,提高代码可靠性。
15. sys.path vs os.path
sys.path:Python 解释器的系统环境参数,包含模块搜索路径列表。
os.path:系统路径文件操作模块(如 join、exists、dirname 等)。
16. if __name__ == '__main__' 详解
每一个模块(.py 文件)都有 __name__ 属性:
- 当模块被直接执行时,
__name__ == '__main__'。
- 当作为外部模块被其他模块导入时,
__name__ 为模块文件名。
注意:被导入时,程序会执行被导入模块的所有代码,因此测试代码需要放在 if __name__ == '__main__': 条件下。
二、高级技巧与常用操作
1. 两次排序获取元素排名
1
2
3
4
5
6
7
8
9
10
11
|
import torch
a = torch.rand(2, 5)
# 第一次排序:获取排序后的值和原始索引
a1, a11 = a.sort(dim=1, descending=True) # 降序排列
# a1: 排序后的数组
# a11: 各元素原本的下标值
# 第二次排序:对索引数组排序
b1, b11 = a11.sort(dim=1) # 默认升序
# b11: 原始数组中每个元素在降序排列后的位置(排名)
|
NumPy 版本:
1
2
3
4
|
import numpy as np
a = np.array([4, 2, 1, 100, -4])
b = a.argsort() # 直接得到排序后对应的下标
sorted_a = a[b] # 排好序的数组
|
带 enumerate 的排序(保留原索引):
1
2
3
4
|
a = ['ab', 'abcde', 'abc', 'abcd', 'a']
temp = sorted(enumerate(a), key=lambda x: len(x[1]), reverse=True)
idx = [i[0] for i in temp] # 原索引
problem = [i[1] for i in temp] # 排序后的数组
|
2. 递归与回溯
递归:
- 函数内部调用函数自身。
- 包含两个部分:
- Base Case(基本情况):问题划分到最小规模,可直接解决。
- Recursive Case(递归情况):将问题分解为更小规模的子问题,通过调用自身解决。
调用栈:每个递归调用在内存中形成栈帧,存储局部变量、返回地址等。递归结束时,栈帧从栈顶依次弹出。
回溯思想:在递归过程中,当某条路径走不通时,撤销之前的决策,尝试其他路径。常与递归结合。
典型应用:
汉诺塔示例:
1
2
3
4
5
|
def TowersOfHanoi(numberOfDisks, startPeg=1, endPeg=3):
if numberOfDisks > 0:
TowersOfHanoi(numberOfDisks-1, startPeg, 6-startPeg-endPeg)
print(f"Move disk {numberOfDisks} from peg {startPeg} to peg {endPeg}")
TowersOfHanoi(numberOfDisks-1, 6-startPeg-endPeg, endPeg)
|
八皇后示例(yield 生成器版本):
1
2
3
4
5
6
7
8
|
def play(row=0, solution=[]):
if row == 8: # Base case
yield solution
else:
for col in range(8):
if not is_conflict(solution, (row, col)):
for sol in play(row+1, solution+[(row, col)]):
yield sol
|
3. yield 与生成器
执行逻辑:
- 函数中有
yield 关键字时,函数不会立即执行,而是返回一个生成器对象。
- 调用
next() 时,函数开始执行,遇到 yield 时暂停并返回值。
- 再次调用
next() 时,从暂停处继续执行。
send(value) 可以向生成器发送值,作为 yield 表达式的返回值。
1
2
3
4
5
6
7
8
9
|
def foo():
print("starting...")
while True:
res = yield 4
print("res:", res)
g = foo()
print(next(g)) # starting... \n 4
print(g.send(7)) # res: 7 \n 4
|
优势:
- 实现惰性计算,按需生成数据。
- 极大节省内存,尤其适合处理大数据序列。
4. is vs. ==
| 操作符 |
比较内容 |
说明 |
is |
对象身份(内存地址) |
id(a) == id(b) |
== |
对象值 |
a 与 b 的值相等 |
注意:
- Python 启动时,
[-5, 256] 范围的整数被预先创建为单例对象。
- 空元组
() 也是单例,因为不可变;空列表 [] 不是,因为可变。
- 对于字符串,CPython 有 string interning 优化,会复用某些字符串对象。
1
2
3
4
5
6
|
a = 256; b = 256; a is b # True
a = 257; b = 257; a is b # False(交互式环境)
a, b = 257, 257; a is b # True(同一行赋值,编译时优化)
() is () # True
[] is [] # False
|
5. 浅拷贝 vs. 深拷贝
| 操作 |
方法 |
特点 |
| 赋值 |
b = a |
只是引用,指向同一对象 |
| 浅拷贝 |
b = a[:] / b = copy.copy(a) / b = list(a) |
创建新对象,但内部元素是原对象的引用 |
| 深拷贝 |
b = copy.deepcopy(a) |
完全独立的新对象,递归复制所有子对象 |
注意:
- 数字、字符串等非容器类型,没有拷贝一说(不可变)。
- 浅拷贝时,若原对象包含可变对象(如嵌套列表),修改该可变对象会影响浅拷贝后的对象。
1
2
3
4
5
6
|
# 经典坑:创建二维列表
board = [[''] * 3] * 3 # 浅拷贝,三行指向同一列表
board[0][0] = 'X' # 结果:[['X','',''], ['X','',''], ['X','','']]
# 正确方式
board = [[''] * 3 for _ in range(3)]
|
6. 装饰器
定义:本质是一个函数,接收一个函数作为参数,并返回一个新函数(或可调用对象)。
语法:
1
2
3
4
5
6
|
@decorator_name
def function_name():
pass
# 等价于
function_name = decorator_name(function_name)
|
多个装饰器执行顺序:
1
2
3
4
5
6
|
@funA
@funB
@funC
def fun():
pass
# 等价于:fun = funA(funB(funC(fun)))
|
常见用途:
- 插入日志
- 性能测试(计时)
- 事务处理
- 缓存(如
@lru_cache)
- 权限校验
- 类方法定义(
@classmethod、@staticmethod、@property)
7. 字符串与时间处理
strptime vs. strftime:
| 函数 |
含义 |
输入 |
输出 |
strptime |
parse(分析) |
字符串 + 格式 |
时间对象 |
strftime |
format(格式化) |
时间对象 + 格式 |
字符串 |
1
2
3
4
5
6
7
|
from datetime import datetime
# 字符串 → 时间对象
time_obj = datetime.strptime('2025-02-21 09:55:00', '%Y-%m-%d %H:%M:%S')
# 时间对象 → 字符串
time_str = datetime.strftime(time_obj, '%Y/%m/%d %H:%M')
|
8. 海象操作符(Walrus Operator):=
- 引入于 Python 3.8。
- 允许在表达式中同时进行变量赋值和表达式求值。
- 注意:两边的圆括号不能少。
1
2
3
4
5
6
7
|
(a := 123) # 123
(a := [2, 4, 6]) # [2, 4, 6]
(a := 'hello') # 'hello'
# 实用场景:while 循环中避免重复调用
while (line := file.readline()) != "":
process(line)
|
9. np.where() 函数
1
2
3
4
|
import numpy as np
nums = np.arange(1, 11)[::-1] # [10, 9, 8, ...]
index = np.where(nums > 3) # 返回满足条件的下标(元组)
index[0] # 取下标数组
|
10. zip() 函数
1
2
3
4
5
6
7
8
9
|
a = [1, 2, 3]
b = ['hello', 'world']
c = [a, b]
list(zip(c)) # [([1,2,3],), (['hello','world'],)]
list(zip(*c)) # [(1, 'hello'), (2, 'world')] # 解压
# 矩阵逆时针旋转
list(zip(*matrix)) # 相当于转置
|
11. np.swapaxes()
1
2
3
4
5
6
|
import numpy as np
arr = np.arange(24).reshape((2, 3, 4))
arr.swapaxes(0, 1) # (3, 2, 4)
arr.swapaxes(0, 2) # (4, 3, 2)
arr.swapaxes(1, 2) # (2, 4, 3)
|
12. 独热编码生成方式
1
2
3
4
5
6
|
# 方法一:sklearn
from sklearn.preprocessing import OneHotEncoder
# 方法二:pandas
import pandas as pd
pd.get_dummies(df['column'])
|
13. 奇异值分解(SVD)与特征值分解
1
2
3
4
5
6
7
8
9
10
|
import numpy as np
A = np.array([[0, 1], [1, 1], [1, 0]])
# 直接 SVD
u, s, vT = np.linalg.svd(A)
# 手动推导
A1 = A.T @ A # 右奇异矩阵
eval1, evec1 = np.linalg.eig(A1) # 特征值为奇异值的平方
evec2 = A @ evec1 / np.sqrt(eval1) # 计算左奇异向量
|
关键关系:
- 奇异值 = 特征值的平方根
A = U·Σ·V^T
三、包、模块与项目组织
1. 模块导入方式
| 导入方式 |
语法 |
特点 |
| 直接导入模块 |
import module |
使用 module.func() |
| 导入并别名 |
import module as alias |
使用 alias.func() |
| 导入特定函数 |
from module import func |
直接使用 func() |
| 导入所有 |
from module import * |
不推荐,可能命名冲突 |
2. 包的 __init__.py
- 将一个文件夹变为 Python 模块。
- 可包含初始化代码(如批量导入模块)。
- 即使为空文件也必须存在(Python 3.3+ 中可省略,但仍推荐保留)。
3. 动态添加类方法
1
2
3
4
5
6
7
|
class MyClass:
pass
def new_method(self):
return "Hello"
MyClass.new_method = new_method
|
四、并发与并行编程
1. 核心概念对比
| 概念 |
定义 |
资源 |
适用场景 |
| 进程 |
程序的一次执行,独立内存空间 |
内存不共享 |
CPU 密集型 |
| 线程 |
CPU 调度最小单位,共享进程资源 |
内存共享 |
I/O 密集型 |
| 协程 |
用户态轻量级线程,程序控制切换 |
共享且开销小 |
大量并发 I/O |
2. GIL(全局解释器锁)
- 仅存在于 CPython 解释器。
- 确保同一时刻只有一个线程执行 Python 字节码。
- 影响:多线程无法实现真正的并行计算。
- 释放时机:时间片结束或遇到 I/O 操作。
执行流程:
- 线程获取 GIL
- 执行 Python 字节码
- 释放 GIL(时间片结束或 I/O 等待)
- 其他线程竞争获取 GIL
- 重复上述过程
3. 全双工通信示例(socket + threading)
服务器端:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
import socket, threading
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('0.0.0.0', 8000))
server_socket.listen()
connection, address = server_socket.accept()
def receive_data():
while True:
data = connection.recv(1024).decode()
if not data: break
print(f'Received: {data}')
def send_data():
while True:
data = input('Enter message: ')
connection.sendall(data.encode())
if data == 'quit': break
threading.Thread(target=receive_data).start()
threading.Thread(target=send_data).start()
|
4. 异步编程示例(aiohttp / aiofiles)
异步网络请求:
1
2
3
4
5
6
7
8
9
10
11
|
import aiohttp, asyncio
async def fetch_url(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
return await response.text()
async def main():
urls = ["https://example.com", ...]
tasks = [fetch_url(url) for url in urls]
responses = await asyncio.gather(*tasks)
|
异步文件操作:
1
2
3
4
5
|
import aiofiles, asyncio
async def write_to_file(filename, data):
async with aiofiles.open(filename, 'w') as f:
await f.write(data)
|
五、内存管理与性能优化
1. 可变 vs. 不可变类型
| 类型 |
特点 |
包含类型 |
| 不可变 |
值改变时,内存地址改变 |
数值、字符串、元组 |
| 可变 |
值改变时,内存地址不变 |
列表、字典、集合 |
关键点:
- 字典的键必须是不可变类型。
- 对不可变类型做深浅拷贝无效,仍指向同一地址。
1
2
3
4
|
a = 5
print(id(a)) # 某个地址
a = 6
print(id(a)) # 新地址(不可变)
|
2. Python 内存管理三层架构
- 第 0 层:C 语言的
malloc / free 函数
- 第 1-2 层:内存池(256 字节以下的小对象)
- 第 3 层:Python 对象直接操作
内存池机制:
- 小于等于 256 字节的小对象在内存池分配。
- 大于 256 字节的对象直接调用
malloc。
- 可通过修改源码更改 256 这个阈值。
3. 垃圾回收机制
| 机制 |
说明 |
| 引用计数 |
每个对象记录被引用次数,为 0 时立即回收。缺点:无法处理循环引用 |
| 标记-清除 |
解决循环引用问题,标记活动对象,清除不可达对象 |
| 分代回收 |
将对象分为三代,新对象在 0 代,回收次数多则晋升。每隔一定时间回收较老一代 |
获取引用计数:
1
2
|
import sys
sys.getrefcount(object)
|
4. 内存泄漏与预防
- 定义:程序未能释放已不再使用的内存。
- 后果:运行速度减慢,系统崩溃。
- 预防:
- 使用
del object 删除不再使用的对象引用。
- 使用
sys.getrefcount() 监控引用计数。
- 避免循环引用(或使用
weakref 模块)。
5. 性能优化建议
| 场景 |
建议 |
| CPU 密集型 |
使用多进程 |
| I/O 密集型 |
使用多线程或协程 |
| 频繁操作 |
尽量使用内置函数(如 sum(), map()) |
| 变量访问 |
多使用局部变量(访问速度快于全局) |
| 循环优化 |
避免在循环内重复计算不变量 |
六、常见坑点与特殊行为
1. 字符串保留(String Interning)
CPython 为优化内存,会尝试复用某些不可变对象。
保留规则:
- 所有长度为 0 和长度为 1 的字符串。
- 字符串在编译时被保留(如
"hello")。
- 不是由 ASCII 字母、数字或下划线组成的字符串不会被保留(如
"hello!")。
1
2
3
4
5
6
7
8
9
10
11
|
a = "hello"
b = "hello"
a is b # True
c = "hello!"
d = "hello!"
c is d # False(交互式环境)
# 同一行声明
a, b = "hello!", "hello!"
a is b # True(编译时优化)
|
常数折叠:
1
2
|
'a' * 20 is 'aaaaaaaaaaaaaaaaaaaa' # True(长度 < 21)
'a' * 21 is 'aaaaaaaaaaaaaaaaaaaaa' # False(长度 >= 21)
|
2. 链式操作
1
2
3
4
5
6
7
|
1 > 0 < 1
# 等价于
(1 > 0) and (0 < 1) # True
(1 > 0) < 2
# 等价于
True < 2 # True(True 被当作 1)
|
3. 哈希与字典键
1
2
3
4
5
6
7
|
some_dict = {}
some_dict[5.5] = "JavaScript"
some_dict[5.0] = "Python"
some_dict[5] = "Ruby"
some_dict[5 + 0j] = "C"
print(some_dict) # {5.5: 'JavaScript', 5.0: 'C'}
|
解释:
- 字典键的唯一性基于等价性(
==),而非同一性(is)。
5 == 5.0 == 5+0j 为 True。
- 值相等的对象有相同的哈希值(
hash(5) == hash(5.0))。
- 注意:哈希值相同的对象值不一定相同(哈希冲突时)。
4. try...finally 中的 break / return
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
def one_more_func():
try:
for i in range(3):
try:
1 / i
except ZeroDivisionError:
raise ZeroDivisionError("error")
finally:
print("Iteration", i)
break # 注意这里的 break
except ZeroDivisionError as e:
print("Error:", e)
one_more_func() # 输出: Iteration 0(异常被丢弃)
|
规则:
finally 子句总会执行。
- 函数的返回值由最后执行的
return 决定。
- 如果
finally 子句执行 return 或 break,临时保存的异常将被丢弃。
5. for 循环中的变量赋值
1
2
3
4
|
for i in range(4):
print(i)
i = 10
# 输出: 0, 1, 2, 3
|
原因:每次迭代开始时,迭代器提供的下一项被解包并赋给目标变量,循环体内的赋值不影响下次迭代。
6. 生成器表达式的绑定时机
1
2
3
4
5
6
7
8
9
|
array_1 = [1, 2, 3, 4]
gen_1 = (x for x in array_1)
array_1 = [1, 2, 3, 4, 5]
list(gen_1) # [1, 2, 3, 4](仍引用旧对象)
array_2 = [1, 2, 3, 4]
gen_2 = (x for x in array_2)
array_2[:] = [1, 2, 3, 4, 5] # 原地修改
list(gen_2) # [1, 2, 3, 4, 5](引用被更新的对象)
|
注意:只有最外层的 for 表达式会立即计算,其他表达式推迟到运行时计算。
7. 类变量与继承
1
2
3
4
5
6
7
8
|
class A: x = 1
class B(A): pass
class C(A): pass
B.x = 2
print(A.x, B.x, C.x) # 1, 2, 1
A.x = 3
print(A.x, B.x, C.x) # 3, 2, 3
|
规则:类变量在内部以字典形式存储。当前类字典中找不到变量名时,在父类中搜索。
8. is 与类方法比较
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
class SomeClass:
def method(self): pass
@classmethod
def classm(cls): pass
@staticmethod
def staticm(): pass
o1, o2 = SomeClass(), SomeClass()
o1.method is o1.method # False(每次访问创建新方法对象)
o1.method == o1.method # True
o1.classm is o1.classm # False
o1.classm == o2.classm # True(类方法相同)
o1.staticm is SomeClass.staticm # True(静态方法相同)
|
解释:函数是描述符。每次访问属性时,描述符被调用并创建一个新的方法对象,这就是为什么 o1.method is o1.method 为 False。
9. NaN 的特殊行为
1
2
3
4
5
6
7
|
float('inf') == float('inf') # True
float('nan') != float('nan') # True(NaN 不等于自身)
None == None # True
import numpy as np
np.NaN is np.NaN # True(同一对象)
np.NaN == np.NaN # False
|
10. 转义字符
1
2
3
4
5
|
# 普通字符串:反斜杠转义
"hello\"hi" # 'hello"hi'
# 原始字符串:r 前缀,反斜杠按原样传递
r'hello\"hi' # 'hello\\"hi'
|
11. 运算符优先级
1
2
|
not x == y # 等价于 not (x == y)
x == not y # 报错(语法错误)
|
12. 变量的下划线约定
| 命名 |
含义 |
_something |
前置单下划线:提示供内部使用,from module import * 不会导入 |
something_ |
后置单下划线:防止与 Python 关键字重名 |
__something |
前置双下划线:名称修饰,避免子类命名冲突 |
__something__ |
前后双下划线:Python 特殊方法(如 __init__),一般不自定义 |
_ |
单独下划线:表示用不到的变量(如 for _ in range(n)) |
七、实用代码片段
1. 打字效果
1
2
3
4
5
6
7
8
9
|
import time, random
text = 'hello world'
for ch in text:
print('|', end='', flush=True)
time.sleep(0.2)
print('\b', end='', flush=True)
time.sleep(random.randrange(1, 30) / 100)
print(ch, end='', flush=True)
|
2. 绘图相关
Matplotlib 多子图:
1
2
|
fig, axes = plt.subplots(2, 2)
axes[0, 0].plot(x1, y1)
|
Seaborn FacetGrid:
1
2
|
grid = sns.FacetGrid(data, col='category', row='subcategory')
grid.map(plt.plot, 'x', 'y')
|
Plotly 多子图:
1
2
|
fig = make_subplots(rows=2, cols=2)
fig.add_trace(go.Scatter(x=x1, y=y1), row=1, col=1)
|
Color bar 设置:
1
2
3
|
sns.heatmap(a, annot=True, cbar=False)
mycbar = ax.figure.colorbar(ax.collections[0])
mycbar.ax.tick_params(labelsize=15)
|
3. pd.DataFrame 操作
1
2
3
4
5
6
7
8
9
10
11
12
|
import pandas as pd
# 删除行
df.drop(index=df.loc[df['table'] == 'sc'].index, inplace=False)
# 删除列
df.drop(columns=['table'])
# a.empty, a.all(), a.any()
a.empty # 判断是否为空
a.all() # 判断是否全为 True
a.any() # 判断是否存在 True
|
1
|
a, b, c = map(int, input().split(','))
|
5. URL 参数提取
1
2
3
4
5
|
from urllib.parse import urlparse, parse_qs
url = 'https://www.google.com/search?newwindow=1&biw=1091'
params = parse_qs(urlparse(url).query)
print(params['newwindow']) # ['1']
|
注意:导入特定函数比导入整个模块更高效,减少内存使用和加载时间。
6. subprocess 使用
1
2
3
4
5
|
import subprocess
p = subprocess.Popen("df -h", shell=True, stdout=subprocess.PIPE)
out = p.stdout.readlines()
p.communicate() # 推荐使用 communicate() 避免死锁
|
7. 脚本参数传入(argparse)
1
2
3
4
5
6
|
import argparse
parser = argparse.ArgumentParser(description='My script')
parser.add_argument('--path', type=str, default=None, help='图片路径')
parser.add_argument('--resize', type=str, default='512x512', help='修改大小')
args = parser.parse_args()
|
8. 文件打包下载(Jupyter)
1
2
3
|
!zip -q -r output.zip "文件夹路径/*"
from IPython.display import FileLink
FileLink('output.zip')
|
9. mp4 转 gif
1
2
3
4
|
from moviepy.editor import *
clip = VideoFileClip("input.mp4").subclip(1, 2).resize(0.5)
clip.write_gif("output.gif", fps=15)
|
10. @lru_cache 缓存
1
2
3
4
5
6
7
8
9
|
from functools import lru_cache
@lru_cache(maxsize=None) # 等价于 @cache
def test(a, b):
print('计算中...')
return a + b
test(1, 2) # 计算中...
test(1, 2) # 直接返回缓存结果
|
11. zip(*nums) 矩阵旋转
1
2
|
nums = [[7,2,1], [6,4,2], [6,5,3], [3,2,1]]
list(zip(*nums)) # 逆时针旋转(转置)
|
1
2
3
4
5
6
7
8
9
|
from ipywidgets import interact, fixed
def plot_3D(elev=20, azim=50, x=x, y=y, z=z):
fig = plt.figure(figsize=(10, 8))
ax = plt.subplot(projection='3d')
ax.plot_surface(x, y, z, cmap='RdBu_r')
ax.view_init(elev=elev, azim=azim)
interact(plot_3D, elev=[20, 90], azim=(-180,180), x=fixed(x), y=fixed(y), z=fixed(z))
|
八、面试常考题汇总
1. Python 简介
- 解释型语言:无需编译,需要解释器,运行速度慢于编译型语言。
- 动态类型语言:声明变量时无需说明类型。
- 强类型语言:不允许不同类型相加(如
'1' + 1 报错)。
- 适合面向对象编程:支持组合与继承。
2. 赋值、浅拷贝、深拷贝
| 操作 |
内存 |
嵌套对象 |
赋值 b = a |
指向同一对象 |
共享 |
浅拷贝 copy.copy(a) |
新对象 |
共享引用 |
深拷贝 copy.deepcopy(a) |
新对象 |
完全独立 |
3. Python 自省
程序运行时检查对象类型和属性的能力。
| 函数 |
功能 |
dir(obj) |
查看对象属性名称 |
type(obj) |
查看对象类型 |
getattr(obj, attr) |
查看对象属性 |
hasattr(obj, attr) |
判断是否有某属性 |
isinstance(obj, type) |
判断是否为某类型 |
4. Python 提升性能的手段
- 使用多进程处理 CPU 密集型任务
- I/O 阻塞采用 I/O 多路复用
- 尽量使用 Python 内置函数
- 尽量使用局部变量
- 对性能影响较大的部分用 C/C++ 编写
5. 字典的 items() vs iteritems()
| Python 2 |
Python 3 |
items() 返回列表 |
items() 返回视图(可迭代) |
iteritems() 返回迭代器 |
无,直接用 items() |
6. 字典 vs JSON
| 对比 |
字典 |
JSON |
| 本质 |
数据结构 |
数据表现形式 |
| 要求 |
value 需要可哈希 |
value 必须是字符串 |
| 引号 |
单双引号均可 |
必须用双引号 |
7. Python 2 vs Python 3 区别
| 项目 |
Python 2 |
Python 3 |
print |
语句(不需要括号) |
函数(需要括号) |
input() |
返回 int(raw_input 返回 str) |
返回 str |
除法 / |
整数除法(取整) |
真除法(返回浮点数) |
地板除 // |
取整 |
取整 |
range() |
返回列表 |
返回迭代器 |
xrange() |
创建迭代对象 |
移除,用 range() |
8. 编码解码
1
2
3
|
s = "hello world"
b = s.encode("gbk") # 编码为字节序列
s1 = b.decode("gbk") # 解码为字符串
|
9. 回调函数
把函数的指针(引用)作为参数传递给另一个函数。简单讲:一个函数作为另一个函数的参数被传入。
10. 方法重写(Override)vs 方法重载(Overload)
| 对比 |
方法重写 |
方法重载 |
| 发生位置 |
子类和父类之间 |
同一个类中 |
| 方法名 |
相同 |
相同 |
| 参数列表 |
相同 |
不同(类型、个数) |
| 返回类型 |
相同或是其子类 |
可以不同 |
| Python 支持 |
✅ 支持 |
❌ 不支持(只有最后一个生效) |
11. GET vs POST
| 对比 |
GET |
POST |
| 用途 |
获取数据 |
提交数据 |
| 参数位置 |
URL 中 |
请求体中 |
| 安全性 |
较低(暴露在 URL) |
较高 |
| 长度限制 |
有(浏览器/服务器限制) |
无 |
| 缓存 |
可以 |
不可以 |
| 书签 |
可以收藏 |
不可以 |
12. 生成器 vs 迭代器
| 对比 |
迭代器 |
生成器 |
| 定义 |
实现 __iter__() 和 __next__() 的类 |
使用 yield 的函数或生成器表达式 |
| 创建方式 |
iter(container) |
(x for x in range(10)) 或函数含 yield |
| 特点 |
逐个访问容器元素 |
更简洁、高效、节省内存 |