大唐面试-国企

1、数据结构知识汇总

1.1 python的数据类型

当然可以!以下是一个完整的关于 Python 数据类型“可变与不可变”的对比表格,清晰明了地展示了常见类型的分类与特性:


🧾 Python 数据类型:可变 vs 不可变

类别 数据类型 示例 是否可变 修改后对象 id 是否变化 说明
不可变类型 int x = 10 数值变化生成新对象
不可变类型 float x = 3.14 浮点数变化生成新对象
不可变类型 str s = "hello" 字符串内容变动会生成新字符串对象
不可变类型 tuple t = (1, 2, 3) 元组不可增删改
不可变类型 bool flag = True True/False 为常量
不可变类型 frozenset fs = frozenset([1, 2]) 冻结集合,不能添加元素
可变类型 list lst = [1, 2, 3] 添加/删除元素不改变对象本身
可变类型 dict d = {"a": 1} 可以增删键值对
可变类型 set s = {1, 2, 3} 可以添加/删除元素
可变类型 自定义对象 class MyClass: pass 属性可变

  1. 不可变类型的值在对象创建之后不能被修改,任何对该对象的修改都会生成一个新的对象。
    • 不可变类型作为参数传入函数时,函数内部修改不会影响原对象。
  2. 可变类型的值在对象创建之后可以修改,修改不会改变对象的 id。
    • 可变类型作为参数传入函数时,函数内部修改会影响原对象。

1.2 深浅拷贝

你的代码有一些问题,我来解释并修正:

  1. print(c.append(10))print(d.append(10)) 会打印 None,因为 list.append() 方法返回 None
  2. 你创建了两个独立的对象 ab,这不是展示拷贝效果的好方式

代码及解释

import copy

# 原始列表(包含嵌套的可变对象)
original = [1, 2, [1, 2, 2], 8, 9]

# 浅拷贝
shallow_copy = copy.copy(original)
# 深拷贝
deep_copy = copy.deepcopy(original)

# 修改拷贝后的列表
shallow_copy.append(10)  # 添加元素到浅拷贝
deep_copy.append(10)     # 添加元素到深拷贝

print("原始列表:", original)         # [1, 2, [1, 2, 2], 8, 9]
print("浅拷贝:", shallow_copy)      # [1, 2, [1, 2, 2], 8, 9, 10]
print("深拷贝:", deep_copy)         # [1, 2, [1, 2, 2], 8, 9, 10]

# 现在修改嵌套列表来展示关键区别
shallow_copy[2].append(3)  # 会影响原始列表
deep_copy[2].append(3)     # 不会影响原始列表

print("\n修改嵌套列表后:")
print("原始列表:", original)         # [1, 2, [1, 2, 2, 3], 8, 9] ← 被浅拷贝修改影响了
print("浅拷贝:", shallow_copy)      # [1, 2, [1, 2, 2, 3], 8, 9, 10]
print("深拷贝:", deep_copy)         # [1, 2, [1, 2, 2, 3], 8, 9, 10]

关键点说明

  1. 浅拷贝 (copy.copy()):

    • 创建了新列表对象
    • 但内部的嵌套列表仍然是原始列表的引用
    • 修改嵌套列表会影响原始列表
  2. 深拷贝 (copy.deepcopy()):

    • 完全递归复制了整个对象结构
    • 创建了全新的嵌套列表对象
    • 修改不会影响原始列表
  3. -对于不可变类型(如元组、字符串),深浅拷贝几乎没有实际区别

1.3 Python中的可迭代类型

可迭代类型是Python中非常重要的概念,它是指能够一次返回一个元素的对象。以下是关于Python可迭代类型的详细说明:

什么是可迭代对象?

可迭代对象(Iterable)是指实现了__iter__()方法的对象,或者实现了__getitem__()方法且可以从0开始索引的对象。可迭代对象可以被用于for循环、解包等操作。

常见的可迭代类型

1. 基础可迭代类型

  • 序列类型

    • 列表(list):[1, 2, 3]
    • 元组(tuple):(1, 2, 3)
    • 字符串(str):"hello"
    • 范围(range):range(5)
  • 集合类型

    • 集合(set):{1, 2, 3}
    • 冻结集合(frozenset):frozenset([1, 2, 3])
  • 映射类型

    • 字典(dict):{"a": 1, "b": 2}(迭代键)

2. 高级可迭代类型

  • 生成器generator):

    (x for x in range(5))  # 生成器表达式
    
  • 文件对象

    with open('file.txt') as f:  # f是可迭代的
        for line in f:
            print(line)
    
  • 自定义可迭代类

    class MyIterable:
        def __iter__(self):
            return iter([1, 2, 3])
    

如何判断对象是否可迭代?

  1. 使用collections.abc.Iterable

    from collections.abc import Iterable
    
    isinstance([1, 2, 3], Iterable)  # True
    isinstance(123, Iterable)        # False
    
  2. 检查是否有__iter__方法:

    hasattr([1, 2, 3], '__iter__')  # True
    

可迭代对象的特点

  1. 惰性求值:某些可迭代对象(如生成器)只在需要时计算值
  2. 一次性使用:某些可迭代对象(如生成器、文件对象)只能迭代一次
  3. 内存高效:不需要一次性加载所有数据到内存

可迭代对象 vs 迭代器

特性 可迭代对象 (Iterable) 迭代器 (Iterator)
定义 实现了__iter__() 实现了__iter__()__next__()
用途 可以用于for循环 可以用于for循环,且可以手动调用next()
状态 无状态 有状态,记住迭代位置
示例 [1, 2, 3], range(5) iter([1, 2, 3]), 生成器

实际应用

  1. for循环

    for item in [1, 2, 3]:  # 列表是可迭代的
        print(item)
    
  2. 解包操作

    a, b, c = [1, 2, 3]  # 列表解包
    
  3. 内置函数

    list('hello')  # ['h', 'e', 'l', 'l', 'o']
    sum([1, 2, 3])  # 6
    
  4. 生成器表达式

    squares = (x**2 for x in range(5))  # 惰性计算
    

2、Python的优点

2.1 python的应用和常用的包

  • 文件或者操作系统:os,pathlib
  • 数据与数学:math,random,datetime、statistics
  • 数据序列化与压缩:json.dumps({key,value}),zipfile,pickle.dumps
  • 网络与并发:requests.get(),socket-底层网络通讯,threading.Thread(target=func).start(),multiprocessing.Process(target=func).start()
  • 调试与测试:logging
  • 正则表达式:re
  • CSV文件:csv

2.1.1 Web开发:

  1. flask 轻量级Web框架 pip install flask 快速构建REST API
  2. django 全功能Web框架(ORM、Admin) pip install django 企业级Web应用开发
  3. requests HTTP请求库(人性化API) pip install requests 爬虫、调用第三方API
  4. beautifulsoup4 HTML/XML解析(爬虫) pip install beautifulsoup4 网页数据抓取
  5. fastapi 高性能API框架(异步支持) pip install fastapi 微服务后端开发

2.1.2 机器学习与AI:

  1. scikit-learn 机器学习算法(分类/回归/聚类) pip install scikit-learn 训练预测模型(如SVM、随机森林)
  2. tensorflow 深度学习框架(Google开发) pip install tensorflow 神经网络、图像识别
  3. pytorch 动态深度学习框架(Meta开发) pip install torch NLP、自定义模型训练
    4.keras 高层神经网络API(简化TensorFlow) pip install keras 快速搭建深度学习模型
    5.opencv-python 计算机视觉(图像处理) pip install opencv-python 人脸检测、视频分析

2.1.3数据库与ORM:

  1. sqlalchemy 数据库ORM(支持多种数据库) pip install sqlalchemy Python对象映射到SQL表
  2. pymysql MySQL连接驱动 pip install pymysql 操作MySQL数据库
  3. redis Redis数据库客户端 pip install redis 缓存、消息队列
  4. pymongo MongoDB官方驱动 pip install pymongo NoSQL文档存储

2.1.4 数据科学分析:

包名用途安装命令示例场景

  1. numpy 高性能数值计算(多维数组) pip install numpy 矩阵运算、线性代数
  2. pandas 数据处理与分析(DataFrame) pip install pandas CSV清洗、时间序列分析
  3. matplotlib 数据可视化(2D/3D绘图) pip install matplotlib 绘制折线图、柱状图
  4. seaborn 统计可视化(基于matplotlib) pip install seaborn 热力图、分布图
  5. scipy 科学计算(优化、信号处理) pip install scipy 傅里叶变换、数值积分

2.1.5自动化与运维:

  1. selenium 浏览器自动化(Web测试/爬虫) pip install selenium 模拟用户点击、表单提交
  2. paramiko SSH协议库(远程操作服务器) pip install paramiko 批量执行Linux命令
  3. psutil 系统监控(CPU/内存/磁盘) pip install psutil 服务器性能分析
  4. fabric 自动化部署工具 pip install fabric 远程任务执行

2.2 Python相比其他语言的优势

以下是Python与Java、C、Go的对比表格,突出Python的主要优点:

特性 Python Java C Go
语法简洁性 ✅ 代码简洁,接近自然语言,学习成本低 ❗ 需要显式类型声明,代码冗长 ❗ 语法复杂,手动内存管理 ⚠️ 比Java/C简单,但不如Python灵活
开发效率 ✅ 动态类型,快速原型开发,适合敏捷 ⚠️ 需编译,类型严格,开发周期较长 ❗ 开发效率低,需处理底层细节 ⚠️ 编译型,但语法简单,编译速度快
性能 ❗ 解释执行,速度较慢(但可用Cython优化) ⚠️ JIT优化,性能中等 ✅ 原生编译,性能最高 ✅ 编译型,性能接近C,优于Python/Java
跨平台性 ✅ 解释器跨平台,一次编写到处运行 ✅ JVM跨平台 ❗ 需重新编译适配不同平台 ✅ 编译为二进制,跨平台支持好
内存管理 ✅ 自动垃圾回收 ✅ 自动GC(但可能有STW问题) ❗ 手动管理内存(易泄漏/溢出) ✅ 自动GC,侧重轻量级并发
并发模型 ⚠️ GIL限制多线程,推荐多进程/协程(asyncio) ✅ 多线程支持(但线程重量级) ❗ 需手动管理线程/同步 ✅ 原生goroutine,高并发轻量级
标准库/生态 ✅ 覆盖广泛(Web、AI、科学计算等) ✅ 企业级库成熟(Spring/Hibernate等) ❗ 标准库功能有限,依赖第三方 ⚠️ 标准库实用,但生态较新(如Web框架较少)
动态特性 ✅ 动态类型、元编程、反射支持灵活 ⚠️ 静态类型,反射有限 ❗ 无动态特性 ❗ 静态类型,无继承/泛型支持弱
适用领域 脚本、AI、数据分析、Web后端、自动化 企业应用、Android、大数据(Hadoop) 操作系统、嵌入式、高性能计算 云原生、微服务、CLI工具、高并发后端
典型应用 Django、NumPy、TensorFlow Spring、Hadoop、Android SDK Linux内核、Redis、Nginx Docker、Kubernetes、Prometheus

关键结论

  1. 选Python:追求开发效率、快速迭代、数据科学/AI。
  2. 选Java/C/Go:需要高性能(C)、强类型安全(Java)、高并发(Go)。
    ✅ Python是动态类型 + 强类型语言,而非弱类型。

2.3 以下是关于Python语言特性及其类型系统的详细说明:


一、Python的核心特性

特性 说明
动态类型 变量无需声明类型,运行时自动推断(如 x = 10 后可改为 x = "str")。
强类型 类型检查严格,不支持隐式类型转换(如 "10" + 1 会报错,需显式转换)。
解释执行 代码逐行解释运行,无需编译(但生成字节码缓存)。
自动内存管理 引用计数 + 垃圾回收(GC)机制,无需手动释放内存。
多范式支持 支持面向对象(OOP)、函数式编程(如 map()lambda)、过程式编程。
丰富的标准库 内置模块覆盖文件I/O、网络、正则表达式等(如 osjsonre)。
动态元编程 支持反射、装饰器、元类等运行时修改代码的行为。
跨平台 通过解释器实现跨平台运行(Windows/Linux/macOS)。
胶水语言 可轻松调用C/C++(通过Cython)、Java(JPype)、.NET(Python.NET)等代码。

二、Python是弱类型语言吗?

1. 动态类型 vs 弱类型
  • 动态类型:变量类型在运行时确定,且可随时改变(Python属于此类)。
    x = 10      # x是整数
    x = "hello" # x变为字符串
    
  • 弱类型:允许隐式类型转换,类型规则宽松(如JavaScript、PHP)。
    // JavaScript(弱类型)
    console.log("10" + 1); // 输出 "101"(字符串拼接)
    
    # Python(强类型)
    print("10" + 1)  # 报错!需显式转换:print("10" + str(1))
    
2. Python的强类型表现
  • 类型检查严格:不同类型操作需显式转换。
    # 需手动转换类型
    print(int("10") + 1)   # 输出 11
    print(str(10) + "abc") # 输出 "10abc"
    
  • 对比真正弱类型语言
    • C语言(弱类型):printf("%d", "10"); 可能输出乱码(无类型检查)。
    • PHP:echo "10" + 1; 直接输出 11(自动转换)。
结论

Python是动态类型 + 强类型语言,而非弱类型。


三、与其他语言的类型系统对比

语言 类型系统 示例行为
Python 动态 + 强类型 "10" + 1 → 报错(需显式转换)
Java 静态 + 强类型 String x = 10; → 编译报错
C 静态 + 弱类型 int x = "10"; → 编译警告(可能运行)
Go 静态 + 强类型 x := "10" + 1 → 编译报错
JavaScript 动态 + 弱类型 "10" + 1 → "101"(自动转换)

四、Python类型系统的优缺点

优点
  • 灵活性高:快速开发时无需纠结类型声明。
  • 代码简洁:减少类型相关的样板代码。
  • 适合原型设计:轻松调整变量用途。

总结

  • Python特性:动态类型、强类型、解释型、多范式、自动内存管理。
  • 弱类型误区:Python不允许隐式类型转换,故不属于弱类型语言。
  • 适用场景:动态类型适合快速开发,强类型避免隐藏错误。

3、Flask 和 Django区别

(1)Flask

Flask确实很“轻”,不愧是Micro Framework,从Django转向Flask的开发者一定会如此感慨,除非二者均为深入使用过
Flask自由、灵活,可扩展性强,第三方库的选择面广,开发时可以结合自己最喜欢用的轮子,也能结合最流行最强大的Python库入门简单,即便没有多少web开发经验,也能很快做出网站
非常适用于小型网站
非常适用于开发web服务的API
开发大型网站无压力,但代码架构需要自己设计,开发成本取决于开发者的能力和经验
各方面性能均等于或优于Django
Django自带的或第三方的好评如潮的功能,Flask上总会找到与之类似第三方库
Flask灵活开发,Python高手基本都会喜欢Flask,但对Django却可能褒贬不一
Flask与关系型数据库的配合使用不弱于Django,而其与NoSQL数据库的配合远远优于Django
Flask比Django更加Pythonic,与Python的philosophy更加吻合
(2)Django

Django太重了,除了web框架,自带ORM和模板引擎,灵活和自由度不够高
Django能开发小应用,但总会有“杀鸡焉用牛刀”的感觉
Django的自带ORM非常优秀,综合评价略高于SQLAlchemy
Django自带的模板引擎简单好用,但其强大程度和综合评价略低于Jinja
Django自带ORM也使Django与关系型数据库耦合度过高,如果想使用MongoDB等NoSQL数据,需要选取合适的第三方库,且总感觉Django+SQL才是天生一对的搭配,Django+NoSQL砍掉了Django的半壁江山
Django目前支持Jinja等非官方模板引擎
Django自带的数据库管理app好评如潮
Django非常适合企业级网站的开发:快速、靠谱、稳定
Django成熟、稳定、完善,但相比于Flask,Django的整体生态相对封闭
Django是Python web框架的先驱,用户多,第三方库最丰富,最好的Python库,如果不能直接用到Django中,也一定能找到与之对应的移植
Django上手也比较容易,开发文档详细、完善,相关资料丰富

4、Python的进程、线程和协程

4.1 三者区别

特性 进程 (Process) 线程 (Thread) 协程 (Coroutine)
隔离性 完全隔离,独立内存空间 共享内存,需同步机制 共享内存,但通过协作式调度避免竞争
创建开销 大(需复制资源) 较小 极小(仅需保存上下文)
切换成本 高(需切换内存空间) 中(需内核调度) 极低(用户态切换)
并行性 真并行(多核CPU) 受GIL限制(CPython中伪并行) 单线程内并发
通信方式 管道、队列、共享内存等IPC 共享变量(需锁) 直接共享变量(无锁)
适用场景 CPU密集型任务、需要隔离的任务 I/O密集型、需要共享状态的并发 高并发I/O操作、异步编程

技术实现细节

1. 进程 (Process)

from multiprocessing import Process

def task():
    print("Process running")

p = Process(target=task)
p.start()
p.join()
  • 优点:绕过GIL限制,利用多核CPU
  • 缺点:通信成本高,资源占用大

2. 线程 (Thread)

from threading import Thread

def task():
    print("Thread running")

t = Thread(target=task)
t.start()
t.join()
  • 优点:共享内存方便,创建成本低
  • 缺点:受GIL限制,调试困难(竞态条件)

3. 协程 (Coroutine)

import asyncio

async def task():
    print("Coroutine running")

asyncio.run(task())
  • 优点:超高并发能力,资源消耗极低
  • 缺点:需要异步编程范式,不能利用多核

选择决策树

  1. 是否需要真并行(利用多核CPU)?

    • 是 → 选择进程
    • 否 → 进入下一步
  2. 是否主要是I/O密集型任务?

    • 是 → 进入下一步
    • 否(CPU密集型)→ 在Python中只能选择进程(因GIL限制)
  3. 是否需要超高并发(>1000连接)?

    • 是 → 选择协程
    • 否 → 选择线程

典型应用场景

进程的最佳场景

  • 数学计算(NumPy/Pandas运算)
  • 科学计算/机器学习训练
  • 需要强隔离的安全敏感任务
  • 长时间运行的独立后台服务

线程的最佳场景

  • 网络请求(HTTP API调用)
  • 数据库操作
  • 文件I/O操作
  • GUI应用(保持界面响应)

协程的最佳场景

  • 高并发网络服务(Web服务器)
  • 爬虫程序
  • 实时数据处理管道
  • 微服务通信

性能考量

指标 进程 线程 协程
创建1000个 极快
上下文切换 极快
内存占用 极低
CPU利用率

混合使用建议

在实际项目中,可以组合使用三种技术:

  1. 进程池:处理CPU密集型任务

    from concurrent.futures import ProcessPoolExecutor
    
  2. 线程池:处理阻塞I/O操作

    from concurrent.futures import ThreadPoolExecutor
    
  3. 协程:处理高并发网络I/O

    async def fetch(url):
        async with aiohttp.ClientSession() as session:
            async with session.get(url) as response:
                return await response.text()
    

特别提醒:Python的GIL问题

在CPython中,全局解释器锁(GIL)导致:

  • 线程:同一时间只有一个线程执行Python字节码
  • 进程:每个进程有独立的GIL,可实现真并行
  • 协程:完全不受GIL影响(因为运行在单线程)
    这是Python多线程在CPU密集型任务中表现不佳的根本原因。

4.2 只有一个线程在执行,concurrent.futures为什么可以实现多线程呢

在Python中,虽然全局解释器锁(GIL)确实导致同一时刻只有一个线程可以执行Python字节码,但concurrent.futuresThreadPoolExecutor仍然能实现有效的多线程并发,原因如下:


1. I/O密集型任务的本质

当线程执行I/O操作(如网络请求、文件读写、数据库查询)时:

  • 线程会主动释放GIL,进入阻塞等待状态
  • 其他线程可以立即获取GIL并执行
  • 这种切换由操作系统调度,完全透明
# 示例:多线程高效处理I/O
with ThreadPoolExecutor(max_workers=5) as executor:
    executor.map(requests.get, urls)  # 每个线程在等待网络响应时都会释放GIL

效果:虽然Python字节码是串行执行的,但I/O等待时间被完美重叠利用,实现伪并行


2. C扩展的规避能力

当线程调用C语言编写的扩展模块(如NumPy、zlib、加密库)时:

  • 这些扩展可以在不持有GIL的情况下运行
  • 计算密集型任务也能获得真正的并行加速
# 示例:使用C扩展绕过GIL
import numpy as np

def compute():
    arr = np.random.rand(1000, 1000)  # NumPy运算会释放GIL
    return np.linalg.inv(arr)         # 矩阵求逆在C层并行

3. 操作系统线程的调度

ThreadPoolExecutor底层使用原生操作系统线程

  • Python线程 ≈ 操作系统线程
  • 虽然GIL限制Python字节码执行,但操作系统仍会公平调度所有线程
  • 当某个线程因I/O阻塞时,操作系统会立即切换上下文

4. 与多进程的对比

特性 ThreadPoolExecutor ProcessPoolExecutor
GIL影响 受限制 完全绕过(每个进程独立GIL)
适合任务类型 I/O密集型 CPU密集型
内存共享 直接共享 需IPC(队列/共享内存)
切换开销 较低(~1µs) 较高(~100µs)

何时选择多线程?

  1. 任务主要是等待I/O(网络/磁盘/数据库)
  2. 需要共享内存状态(如全局计数器)
  3. 创建大量并发单元(线程比进程轻量)
# 典型用例:Web爬虫
def fetch(url):
    resp = requests.get(url)  # 每个请求都会释放GIL
    return resp.text

with ThreadPoolExecutor(50) as executor:  # 50个线程
    executor.map(fetch, urls)            # 并发处理URL列表

关键结论

  • GIL不阻止多线程:它只限制Python字节码的并行执行,不影响I/O并发
  • 高效并发秘诀:利用I/O等待时间切换线程(类似协程原理)
  • 真正瓶颈:纯Python的CPU计算(此时应换用多进程)

通过合理利用I/O阻塞和C扩展,ThreadPoolExecutor能在GIL限制下仍提供出色的并发性能。

4.3 如何选择协程还是线程

特性 协程 (Coroutine) 线程 (Thread)
并发模型 协作式(单线程内切换) 抢占式(操作系统调度)
并发量 轻松支持数万级并发(如10K+连接) 通常数百到数千(受线程栈内存限制)
切换开销 极低(用户态切换,约100ns) 较高(内核态切换,约1µs)
内存占用 每个协程约几KB 每个线程约8MB(默认栈大小)
编程复杂度 需要async/await语法,避免阻塞调用 传统同步代码,但需处理线程安全
调试难度 较难(异步调用链复杂) 中等(需处理竞态条件)
适用场景 I/O密集型高并发(网络/磁盘) I/O密集型中低并发、需兼容旧代码

优先协程:如果是纯I/O密集型且能控制代码(如新项目)

妥协用线程:如需兼容旧代码或使用不支持异步的库

绝不混用:避免在同一应用中随意混用协程和线程(增加复杂度)

4.4 如何保证多线程安全

常见的处理方法包括使用互斥锁、可重入锁、信号量、线程局部存储、线程安全队列、线程池以及避免共享状态等。

线性池如何保证多线程安全

特性 手动线程管理 线程池
任务分配 需自行实现安全队列 内置线程安全队列
异常处理 需手动捕获传播 自动封装在Future中
资源回收 需自行管理线程生命周期 自动重用线程
结果收集 需实现同步机制 通过Future自动同步
最大并发控制 需自行实现 内置max_workers参数控制

4.5 多线程 + 多机器场景下的GIL

  1. 单机多线程:
    即使有多个线程,GIL 会强制同一时刻仅一个线程运行Python代码(CPU密集型任务无法充分利用多核)。
    I/O密集型任务(如网络请求、文件读写)可以绕过GIL,因为线程在等待I/O时会释放GIL。
  2. 多机器(或多进程):
    如果任务分布在多台机器或多个Python进程上(每个机器/进程独立运行CPython),每个进程有自己的GIL,此时GIL不会跨进程/机器影响。
    这种情况下,真正的并行成为可能(因为GIL是进程级的锁)。
posted @ 2025-04-08 16:57  XieBuWan  阅读(39)  评论(0)    收藏  举报