Python面试问题汇总
Python 面试题
数据操作
如何将字符串 “ilovechina” 进行反转
1 | s = 'ilovechina' |
如何将 “1,2,3” 分割成 [“1”,”2”,”3”]?
1 | s = "1,2,3" |
Python 中的字符串格式化方式你知道哪些?
Python3.6之后的版本提供了三种字符串格式化的方式
1 | # 1. %s占位符 |
列表元素怎么去重
自己动手写函数实现(保留原顺序)
1 | old_list = [2, 3, 4, 5, 1, 2, 3] |
用字典dict去重(保留原顺序)
1 | # 使用list元素值创建一个dict,这将自动删除任何重复项 |
使用sort函数来去重(保留原顺序)
1 | lst1 = [2, 1, 3, 4, 1] |
使用内置set方法(不保证原顺序)
1 | # 将list转化为set再转化为list |
两个列表怎么转成字典
- 动手写一个函数
1 | list1 = ['name', 'age', 'sex'] |
- 使用内置函数zip
1 | list1 = ['name', 'age', 'sex'] |
如何合并两个列表
1 | a = [1,5,7,9] |
举例 sort 和 sorted 的区别
1 | sort()与sorted()的不同在于,sort是在原位重新排列列表,而sorted()是产生一个新的列表 |
如何把 元组 (“a”,”b”) 、(1,2),变为字典{“a”:1,”b”:2}?
1 | a = ('a','b') |
字典操作中 del 和 pop 有什么区别?
1 | del 操作删除键值对,不返回值; |
如何合并两个字典
1 | # python3合并字典有三种方式 |
文件处理
在读文件操作的时候会使用 read、readline 或者 readlines,简述它们各自的作用
- read():一次读取文本全部内容为字符串(可以指定size去读取),占用空间大
- readline():读取文本为一个生成器,支持遍历和迭代,占用空间小
- readlines():一次读取文本全部内容为列表,占用空间大
Python 字典和 json 字符串相互转化方法
1 | json.dumps() 将Python中的对象转换为JSON中的字符串对象 |
语言特性
Python 中的基本数据结构
基本结构通常指列表、元组、字典、集合,而字符串可能作为序列类型单独列出
数据结构 | 特点 | 实现原理 | 应用场景 |
---|---|---|---|
列表 | 有序、可变、允许重复元素 | 动态数组存储,支持自动扩容 | 动态数据读取/更新、栈/队列实现 |
元组 | 有序、不可变、允许重复元素 | 紧凑数组存储 | 存储固定数据(如函数参数传递、配置参数),比列表更节省内存 |
字典 | 无序(Python 3.7+ 有序)、可变、键值对 | 哈希表 | 快速查询、JSON 数据处理 |
集合 | 无序、可变、唯一性(自动去重) | 哈希表(仅键) | 去重、集合运算(交集、并集)、成员判断 |
字符串 | 有序、不可变、字符序列 | 动态编码机制存储字符,如UTF-8、GBK | 文本处理、数据序列化(JSON/XML格式) |
Python 函数参数的传递机制
Python的函数参数传递机制是对象引用传递(Pass by Object Reference),其核心是传递对象的引用,而非对象本身或对象的值。这一机制的行为取决于对象是否可变。
1. 如何理解对象引用传递
- 所有变量都是对象的引用:Python中变量名本质上是对象的引用(指针),即所有数据(如整数、字符串、列表等)都以对象形式存在,变量仅指向这些对象。
- 传递的是引用的拷贝:函数调用时,实参对象的引用(地址)会被拷贝给形参。因此,形参和实参指向同一个对象,但引用本身是独立的。
2. 不可变对象的传递
不可变对象(如 int
、str
、tuple
等)的值不能被修改,因此:
- 函数内部无法修改原对象:对形参的“修改”都会创建新对象(如重新赋值),外部变量不变,行为类似“值传递”
1 | def modify_immutable(x): |
3.可变对象的传递
可变对象(如 list
、dict
、set
等)的值可以被修改,因此:
- 函数内部可直接修改原对象:通过原地操作(如
append
、update
)会修改原对象,会影响外部变量,行为类似“引用传递” - 特殊情况:如果在函数内部重新赋值(如 lst = new_list),形参的引用会指向新对象,外部变量不变
1 | def modify_mutable(lst): |
Python 垃圾回收机制
Python的垃圾回收(Garbage Collection, GC)机制旨在自动管理内存,回收不再被使用的对象,避免内存泄漏。其核心机制结合了引用计数和分代回收(Generational Collection),同时通过gc模块提供手动控制的灵活性。
1. 核心机制:引用计数
基本原理
- 每个对象都有一个引用计数器:记录当前有多少变量或对象引用它。
- 引用计数的增减:
- 当对象被创建时,初始引用计数为1。
- 每次有新的变量或对象引用该对象时,计数器加1。
- 当引用失效(如变量被删除、超出作用域、重新赋值)时,计数器减1。
- 当计数器降为0时,Python立即释放该对象的内存。
示例
1 | a = [1, 2, 3] # 列表对象的引用计数为1(被a引用) |
引用计数的局限性
- 无法处理循环引用:当两个或多个对象互相引用时,引用计数永远不会为0,导致内存泄漏。
1 | a = [] |
- 分代回收(Generational Collection)
作用:解决循环引用问题,通过周期性扫描检测并回收被循环引用的对象。
分代回收的整体思想是:将系统中的所有内存块根据其存活时间划分为不同的集合,每个集合就成为一个“代”,垃圾收集频率随着“代”的存活时间的增大而减小,存活时间通常利用经过几次垃圾回收来度量。
函数装饰器有什么作用?请列举说明
函数装饰器可以在不修改原函数的条件下,为原函数添加额外的功能,例如记录日志,运行性能,缓存等
简述 @classmethod 和 @staticmethod 用法和区别
对比项 | @classmethod |
@staticmethod |
---|---|---|
参数 | 必须有 cls (类本身) |
无特殊参数,类似普通函数 |
访问权限 | 可访问/修改类属性 | 无法访问类或实例属性 |
依赖性 | 依赖类的上下文 | 与类或实例无关 |
典型用途 | 工厂方法(替代构造函数来创建对象)、操作类属性、多态场景 | 工具函数、逻辑相关但独立的功能 |
调用灵活性 | 推荐通过类名调用 | 推荐通过类名调用 |
你了解 Python 中的反射吗?
Python 的反射能力由一系列内置函数和模块支持,允许代码在运行时根据字符串名称操作对象,实现高度动态的编程。比如动态操作对象属性、调用方法、模块导入等
内置函数
hasattr(obj, name)
: 检查对象是否包含指定属性/方法getattr(obj, name, default)
: 获取对象的属性/方法,若不存在则返回default
或抛出错误setattr(obj, name, value)
: 动态设置对象的属性值delattr(obj, name)
: 删除对象中的属性/方法
应用场景
1、动态调用方法
1 | import requests |
2、动态属性操作
1 | class DynamicObject: |
3、动态加载模块或类
1 | module_name = "math" |
inspect 模块
inspect
模块提供更强大的内省功能。
严格来说,内省是反射的一部分,主要用于查看分析函数/类的元数据。简单一句就是运行时能够获得对象的类型.比如type(),dir(),getattr(),hasattr(),isinstance()。
1 | import inspect |
copy 和 deepcopy 的区别是什么?
浅拷贝: 创建新对象,赋值的时候非递归地复制子对象的引用。
深拷贝:创建新对象,赋值的时候递归地复制子对象的引用。即拷贝了对象的所有元素,包括多层嵌套的元素
1 | import copy |
进程与线程的区别
- 进程是CPU资源分配的基本单位,线程是独立运行与调度的基本单位(CPU上真正运行的是线程)
- 进程拥有自己的资源空间,同一个进程中的多个线程并发执行,这些线程共享进程所拥有的资源,但需处理线程安全问题(如锁机制)
- 线程的调度与切换比进程快很多
python中的GIL
CPython环境下,每个进程都拥有一个GIL(Global Interpreter Lock),单进程下的多线程,某个线程想要执行,必须先获取GIL,才可以进入CPU执行。因此,一个Python进程中的多个线程不能同时使用多个CPU核心(java中一个进程中的多线程可以利用多核cpu)。
GIL释放的情况:
- 执行的字节码行数到达一定阈值
- 通过时间片划分,到达一定时间阈值
- 在遇到IO操作时,主动释放
GIL(全局解释器锁)带来的影响
多线程:
- Python 的 GIL 会强制解释器在同一时刻只能执行一个线程的字节码。因此,多线程在 CPU 密集型任务中无法真正并行,反而可能因线程切换导致效率下降。
- 不过Python标准库中的所有阻塞型I/O函数都会释放GIL,等待 IO 时可以切换线程执行其他任务,因此多线程适合 IO 密集型任务(如网络请求、爬虫、文件读写)
多进程:
- 每个进程拥有独立的 Python 解释器和 GIL,因此多进程可以并行利用多核 CPU,适合 CPU 密集型任务(如科学计算、数据处理)
python 协程是什么
协程是一种比线程更加轻量级的存在,最重要的是,协程不被操作系统内核管理,协程是完全由程序控制的。
- 运行效率极高,协程的切换完全由程序控制,不像线程切换需要花费操作系统的开销,线程数量越多,协程的优势就越明显。
- 协程不需要多线程的锁机制,因为只有一个线程,不存在变量冲突。
- 对于多核CPU,利用多进程+协程的方式,能充分利用CPU,获得极高的性能。
Python里最常见的yield就是协程的思想!
什么是单例模式
单例模式的主要目的是保证在系统中,某个类只能有一个实例存在。比如保存系统基本配置信息的类,在很多地方都要用到,没有必要频繁创建实例与销毁实例,只需要保存一个全局的实例对象即可,这样可以减少对内存资源的占用。
1、可以在模块中定义单例类并实例化对象,在用的时候直接导入这个模块的实例对象即可
1 | class GlobalConfig: |
2、__new__()
是类的构造方法,在实例创建前被调用,它的作用就是创建实例对象。利用这个方法和类的属性的特点可以实现设计模式的单例模式。
1 | # 多线程下创建单例对象需要加锁 |