1、python 语言的基础特性
- python中一切皆对象:
- 在python中,无论函数、类、模块等等都是对象,他们都继承自object
- 所有变量的本质都是对象的引用(指针)
- 动态解释性语言:
- python是解释性语言,运行时需要一行一行解释执行,不会编译成二进制文件执行。
- python类型是动态的:变量无需声明类型,运行时动态决定。
- python的数据类型
- 内置数据类型: int float dict set list str bool tuple
- 可变数据类型: list dict set
- 不可变数据类型:int float tuple
- 内置数据类型: int float dict set list str bool tuple
- 函数式编程的支持:
- python支持高阶函数、lambda、map/filter/reduce、生成器和迭代器
- 主要功能节省内存
- 迭代器
- 定义:实现了__iter__和__next__方法的对象
- 特点:
- 按顺序逐个访问元素
- 只能向前遍历icing
- 节省内存,不会一次性加上所有元素
常见的迭代器: iter(list) 、 iter(dict)、open() 返回的文件对象
- 生成器
- 定义: 一种特殊的迭代器,用yield返回数据
- 特点:
- 惰性计算: 只在需要生成时生成值
- 语法简洁,比写完整的迭代器类跟更方便
- 自动实现__iter__() 和 next() 方法
- 什么时候用迭代器,什么使用用生成器
- 迭代器:
- 需要对自定义对象支持for…in … 遍历
- 比如设计一个树结果,希望for node in tree 能迭代所有节点
- 生成器:
- 数据量大,不相一次性加载数据到内存
- 流式处理(语音帧、日志流、网络数据流)
- 典型的场景:
1
2
3
4
5
6
7# 文件逐行读取
def read_file(path):
with open(path) as f:
for line in f:
yield line
无限序列(斐波那契、自然数流)
- 迭代器:
2、python的执行过程
python是一种解释性语言,这意味着python代码在执行时先转换成子字节码,然后由python虚拟机PVM执行
- 源代码::编写的Python文件
- 编译::Python解释器首先将源代码编译成字节码。这一过程涉及到语法分析和语义分析,确保代码符合Python的语法规则。字节码是一种低级、与平台无关的代码,保存在.pyc中
- 字节码::编译得到的字节码是一种中间表示形式,存储在内存中。字节码的执行比直接解释源代码要快。
- python虚拟机::Python虚拟机(PVM)是Python运行时环境的一部分,负责读取字节码,将其转换成机器码,然后执行。PVM是解释器的核心,它执行存储在pyc中
- 执行::PVM逐条执行字节码指令,进行函数调用、内存管理等
3、常见算法: 散列表、红黑树、二叉树,能否对应适合的应用场景(通用)
1 |
|
3.1 散列表(Hash Table /dict ,set)
定义
- 基于哈希函数,将key映射到内存地址存储值
- python的dict和set就是典型的实现
特性
- 无序存储(python3.7+ dict保存,但逻辑上还是哈希表)
- 支持快速查询、插入、删除
- 通过哈希碰撞处理(开发寻址或者链表)
时间复杂度
1 | | 操作 | 平均 | 最坏 | |
典型引用场景
- 去重:set存储已访问元素
- 频率统计:词频统计、日志统计
- 缓存/映射表:用户ID–> 用户信息
- 面试高频题目:
1、两数之和
1 |
|
2、统计词频 TopK
1 | # https://leetcode.cn/problems/word-frequency/ |
3.2 二叉树 & 二叉搜索树
定义
- 每个节点最多有两个子节点(左右)
- BST 左边 < 根 < 右
特性
- 树形结构 --> 支持分层、递归处理
- 中序遍历BST --> 得到有序列表
| 操作 | 普通二叉树 | BST(平均) | BST(最坏退化成链) |
|---|---|---|---|
| 查找 | O(n) | O(log n) | O(n) |
| 插入 | O(n) | O(log n) | O(n) |
| 删除 | O(n) | O(log n) | O(n) |
典型的应用场景
- 分层结构表示: 组织结构、文件目录
- 排序存储+ 范围查询
- 遍历恩替(DFS、BFS、回溯)
常见题
- 二叉树最大深度
1 |
|
- 验证BST
1 |
|
3.3 红黑树(自平衡二叉搜索树)
定义
- 自平衡二叉搜索树
特性:
-
根节点黑色
-
红节点不能连续
-
任意路径黑节点数量相同
-
保证 O(log n) 查找/增删
| 操作 | 时间 |
|---|---|
| 查找 | O(log n) |
| 插入 | O(log n) |
| 删除 | O(log n) |
| 典型应用场景 |
需要动态维护有序数据的场景
系统底层实现:
Java TreeMap / TreeSet
C++ map / set
高频题:动态排行榜、区间查询、调度系统
3.4 堆(Heap/优先队列)
定义
完全二叉树
父节点 ≥ 子节点(最大堆)或 ≤ 子节点(最小堆)
特性
查找最大/最小元素 O(1)
插入、删除 O(log n)
典型应用场景
Top K 问题(高频词、排行榜)
优先级任务调度(调度器、操作系统)
图算法(Dijkstra, Prim)
4、Pytorch框架的搭建
5、python闭包、装饰器、异常捕获、多线程用法?
5.1、python闭包
定义
函数内部定一函数,并且内部函数引用了外部函数的局部变量,就形成了闭包。
闭包能保存函数运行时的环境。
1 |
|
✅ 面试考点:
闭包常用于延迟计算、函数工厂、隐藏实现细节。
注意 Python 中闭包变量是 引用 而非拷贝。
5.2、装饰器(Decorator)
定义
本质是一个函数,接收一个函数作为参数,返回一个增强后的函数
常用语日志、权限检验、性能统计、缓存等等
装饰器本质上是一个函数,它接受一个函数作为参数并返回一个新的函数。
1 | def time(func): |
5.3、异常捕获
Python 用 try-except-finally 来处理异常,保证程序健壮性。
1 | try: |
5.4、多线程(Threading)
Python 提供 threading 模块,但要注意 GIL
限制:CPU 密集型任务不能真正并行,但 I/O 密集型任务(网络、磁盘)有明显加速。
1 | import Threading |
6、python中with语句的了解?
6.1、with语句是什么?
with是上下文管理器的语法糖,用来简化资源的获取和释放
核心的两个方法是:
- __enter__(self): 进入上下文时执行
- __exit__(self, exc_type, exc_val, exc_tb)): 退出上下文执行,无论是否发生异常
6.2、语法
1 |
|
6.3、自定义上下文管理器
1 |
|
7、python多线程的用法
Python 提供threading模块
7.1、启动方式
- 方式1: 直接创建Thread
1 | import threading |
- 方式2:继承Thread
1 | class MyThread(thread.Thread): |
7.2、现成同步(锁)
多线程同时访问共享数据时,容易出现竞态条件,需要用 锁 Lock。
1 |
|
7.3、线程池(推荐)ThreadPoolExecutor
1 |
|
8、python的深拷贝、浅拷贝
8.1 浅拷贝
浅拷贝会创建一个新的对象,但是它包含是对原对象地址的引用,而不是其副本。因此如果原始对象的项是可变的,修改
这些项会影响到拷贝后的对象。
- 创建浅拷贝的方法:
- 使用列表的list.copy()方法
- 切片操作,例如 new_list = old_list[:]
- 使用copy模块的copy函数: from copy import copy; new_list=copy(old_list);
1 | import copy |
8.2 深拷贝
深拷贝创建一个新的对象,并且递归拷贝原始对象中的所有项。这意味着它将创建所有项的副本,包括嵌套对象。
修改原始对象不会影响到新创建的副本。
- 创建深拷贝
1 | from copy import deepcopy |
9. GIL 限制
Python 有 GIL(全局解释器锁),同一时刻只有一个线程能执行 Python 字节码。
因此:
CPU 密集型任务(计算) → 适合用 multiprocessing(多进程)。
I/O 密集型任务(网络请求、文件读写) → 多线程能显著提升性能。
10、Python lambda匿名函数
在python中,lambda函数是一种轻量级的匿名函数,它通常用以编写简单的,一行的函数,无须定义正式的函数名
- 基本语法:
1 | lambda arguments: expression |
- 特点:
- 匿名: lambda函数通常是匿名的,没有函数名
- 一行表达式: 只能有一个表达式,不能包含其他的语句或者多个表达式
- 可随时使用: 可以在任何地方使用
- 灵活性: 常用简化代码,特别是在函数编程或者需要传递小函数作为参数的场合。
1 | # 使用map将每个元素加倍 |
11、如何提高python执行效率的方法用过哪些?
11.1. 算法与数据结构优化(最重要)
时间复杂度 > 语言性能
例:搜索用 set/dict(O(1))替代 list(O(n))。
例:用堆 (heapq) 实现 TopK,比排序快。
应用场景:语音识别时,查询词典用 dict 而不是 list。
11.2. 内置函数和库替代手写循环
Python 内置函数(sum, map, any, all)和 列表推导式 通常比手动 for 循环快。
NumPy/Pandas:矩阵、数据处理用 C 实现,比 Python 循环高效几个数量级。
import numpy as np
a = np.arange(1e6)
print(np.sum(a)) # 比 for 循环快很多
11.3. 并行与并发
多线程 (threading):适合 I/O 密集型(语音流读取、网络请求)。
多进程 (multiprocessing):适合 CPU 密集型(语音特征提取、模型推理)。
异步 (asyncio):高并发网络任务。
应用场景:车载语音系统中,多线程负责多个音频流并发录音;多进程负责特征提取。
12. JIT 编译 & C 扩展
PyPy:带 JIT 编译,比 CPython 快。
Cython:将 Python 代码编译为 C,提高数倍性能。
Numba:用 @jit 装饰器,加速数值计算。
from numba import jit
@jit(nopython=True)
def add(a, b):
return a + b
13. 内存管理优化
生成器 (yield) 替代一次性加载(节省内存)。
迭代器 代替列表,避免存储全部数据。
应用场景:逐帧读取语音文件,而不是一次性加载整个音频。
14. 减少不必要的开销
避免重复计算(缓存结果,用 functools.lru_cache)。
局部变量比全局变量访问快。
避免频繁的字符串拼接,用 str.join()。
from functools import lru_cache
@lru_cache(maxsize=128)
def fib(n):
if n < 2: return n
return fib(n-1) + fib(n-2)
15. 使用合适的解释器 & 框架
CPython:默认实现。
PyPy:JIT 加速。
C/C++ 扩展:关键部分用 C 实现(如 torch, numpy)。
应用场景:深度学习框架 PyTorch 底层就是 C++/CUDA。
16. 分布式与缓存
多机并行(Ray, Dask, Spark)。
缓存:热点数据放 Redis,减少重复计算。
17、*args和**kwargs的区别是什么?
17.1. *args
作用:接收 任意数量的位置参数,会打包成一个 元组。
适合参数个数不确定的函数。
def foo(*args):
print(args)
foo(1, 2, 3)
输出: (1, 2, 3)
17.2. **kwargs
作用:接收 任意数量的关键字参数,会打包成一个 字典。
适合需要灵活传递键值对参数的场景。
def bar(**kwargs):
print(kwargs)
bar(a=1, b=2, c=3)
输出: {‘a’: 1, ‘b’: 2, ‘c’: 3}
17.3. *args 和 **kwargs 一起用
一般写法:def func(*args, **kwargs)
*args 处理位置参数
**kwargs 处理关键字参数
def func(*args, **kwargs):
print(“args:”, args)
print(“kwargs:”, kwargs)
func(1, 2, x=10, y=20)
args: (1, 2)
kwargs: {‘x’: 10, ‘y’: 20}
- 区别总结
特性 *args **kwargs
参数形式 位置参数 关键字参数
数据类型 元组 (tuple) 字典 (dict)
应用场景 参数数量不定(如加法函数) 配置灵活(如传递参数给API) - 面试延伸问题(华为 OD 常问)
❓ 为什么要用 *args 和 **kwargs?
👉 提高函数的通用性和可扩展性,适用于接口设计、装饰器开发。
❓ 如果调用函数时用 * 或 ** 会怎样?
👉 会进行 参数解包:
def add(x, y): return x + y
nums = (1, 2)
print(add(*nums)) # 3
params = {“x”: 3, “y”: 4}
print(add(**params)) # 7
❓ 在装饰器里为什么常见 *args, **kwargs?
👉 因为装饰器要支持 任意函数签名,保证通用性。
⚡ 记忆口诀:
*args → tuple → 位置参数
**kwargs → dict → 关键字参数
18、Django、Flask、Tornado三大框架各种应用的场景?
18.1 Django —— 重量级、全家桶
特点:
自带 ORM、模板系统、Admin 后台、用户认证等一整套“全家桶”。
遵循 MTV 模式,强调快速开发、约定大于配置。
有较多内置组件,适合开发复杂的大型 Web 应用。
适用场景:
大型企业应用、内容管理系统(CMS)、电商平台、新闻门户网站。
对数据一致性、权限管理要求高的系统。
典型案例: Instagram、知乎早期。
18.2. Flask —— 轻量级、自由度高
特点:
只有最核心的功能(路由 + WSGI 支持),其余功能通过扩展实现。
灵活度高,开发者可以自由选择 ORM(SQLAlchemy、Peewee 等)、模板引擎等。
学习曲线较低,适合快速搭建原型。
适用场景:
中小型 Web 应用、原型验证、API 服务、微服务。
对框架约束少,希望灵活度高的项目。
典型案例: Pinterest、国内很多中小型 SaaS。
18.3. Tornado —— 高性能异步框架
特点:
内置高性能异步非阻塞 I/O,适合处理长连接和高并发请求。
自带 Web Server,不依赖 Gunicorn/uWSGI。
学习成本稍高,生态不如 Django/Flask 丰富。
适用场景:
即时通讯(IM)、WebSocket、长轮询、实时推送系统。
对高并发、低延迟有要求的后台服务。
典型案例: FriendFeed(Facebook 收购的实时社交网站)。
✅ 一句话总结:
Django:全家桶,适合 大型复杂系统。
Flask:轻量自由,适合 中小项目、微服务、原型。
Tornado:异步高并发,适合 实时系统。
19、什么是元编程?python中如何实现元编程?
1、什么是元编程?
元编程(Metaprogramming)是指编写能够操作、生成或修改其他程序代码的程序。换句话说,元编程允许程序在运行时动态地创建或改变代码结构,从而实现更高层次的抽象和灵活性。
简单的说,当你的程序存在大量的重复代码时候,你就需要考虑是否有更好的解决方案
2、python中如何实现元编程?
Python 提供了多种实现元编程的方式,主要包括以下几种:
2.1 使用装饰器(Decorators)
装饰器是一种特殊的函数,可以在不修改原函数代码的情况下,动态地增强或修改函数的行为。装饰器本质上是一个高阶函数,接受一个函数作为参数,并返回一个新的函数。
1 | def log_decorator(func): |
2.2 使用元类(Metaclasses)
元类是用于创建类的类。通过定义元类,可以在类创建时动态地修改类的属性和方法。元类通常继承自 type 类。
1 | class MyMeta(type): |
2.3 使用反射(Reflection)
反射允许程序在运行时动态地检查和修改对象的属性和方法。Python 提供了内置函数如 getattr、setattr、hasattr 和 dir 来实现反射。
1 | class Person: |
2.4 动态代码生成
Python 允许在运行时动态地生成和执行代码。可以使用 exec 和 eval 函数来实现动态代码生成。
1 | code = """ |
2.5 使用描述符(Descriptors)
描述符是一种特殊的对象属性,可以通过定义 get、set 和 delete 方法来控制属性的访问和修改。
1 | class Descriptor: |
16、python中的反射机制是什么?如何使用反射机制?
1、什么是反射机制?
反射机制(Reflection)是指程序在运行时能够动态地检查和修改自身结构和行为的能力。通过反射,程序可以在运行时获取对象的类型信息、属性、方法等,并且可以动态地调用方法或修改属性值。反射机制使得程序具有更高的灵活性和动态性,能够适应不同的运行环境和需求。
2、python中的反射机制
Python 提供了丰富的内置函数和方法来实现反射机制,主要包括以下几个方面:
2.1 获取对象的属性和方法
Python 提供了内置函数 getattr、setattr、hasattr 和 dir 来获取对象的属性和方法。
1 | class Person: |
2.2 动态调用方法
可以使用 getattr 函数动态地获取并调用对象的方法。
1 | class Calculator: |
2.3 动态创建类和对象
可以使用 type 函数动态地创建类,并使用反射机制创建对象。
1 | # 动态创建类 |
2.4 使用反射加载模块
可以使用 importlib 模块动态地加载模块。
1 | import importlib |
2.5 使用反射实现插件机制
可以通过反射机制动态地加载和使用插件。
1 | class PluginBase: |
17、python中的反射机制在实际开发中有哪些应用场景?
1、动态加载模块和类
在大型应用中,可能需要根据配置或用户输入动态加载不同的模块或类。反射机制允许程序在运行时根据字符串名称加载模块或类,从而实现插件化设计。
1 | import importlib |
2、动态调用方法
在某些情况下,程序需要根据用户输入或配置动态调用不同的方法。反射机制允许程序在运行时根据方法名称字符串获取并调用方法。
1 |
|
3、对象序列化与反序列化
在对象序列化与反序列化过程中,反射机制可以用来动态地获取对象的属性和值,从而实现通用的序列化逻辑。
1 |
|
4、ORM(对象关系映射)
ORM 框架通常使用反射机制动态地将数据库表映射为类,并将表字段映射为类属性。
1 |
|
__END__