Python 面试题及答案(高频考点整理)

一、Python 基础

1. Python 是解释型语言还是编译型语言?

Python 是解释型语言。Python 代码在执行前会先被编译成字节码(.pyc 文件),然后由 Python 虚拟机(PVM)解释执行。这种“先编译后解释”的方式使其兼具一定的编译特性和解释的灵活性。

2. 列表(list)和元组(tuple)有什么区别?

特性列表(list)元组(tuple)
可变性可变(mutable)不可变(immutable)
语法使用方括号 []使用圆括号 ()
性能增删改操作较多时,内存开销稍大不可变,创建和访问速度更快
使用场景存储需要动态变化的数据集合存储不应被修改的数据(如配置项、字典键)

3. 深拷贝(deep copy)和浅拷贝(shallow copy)的区别?

  • 浅拷贝:只拷贝对象本身,不拷贝对象内部的子对象。子对象仍然是原对象子对象的引用。使用 copy.copy()list.copy()
  • 深拷贝:递归拷贝对象及其所有子对象,生成一个完全独立的新对象。使用 copy.deepcopy()

示例

import copy

original = [[1, 2], [3, 4]]
shallow = copy.copy(original)
deep = copy.deepcopy(original)

original[0][0] = 99
print(shallow)   # [[99, 2], [3, 4]]  子列表被修改
print(deep)      # [[1, 2], [3, 4]]   完全独立,不受影响

4. *args**kwargs 的作用是什么?

  • *args:用于接收任意数量的位置参数,在函数内部作为一个元组处理。
  • **kwargs:用于接收任意数量的关键字参数,在函数内部作为一个字典处理。

示例

def example_func(a, b, *args, **kwargs):
    print(f"a={a}, b={b}")
    print(f"args (tuple): {args}")
    print(f"kwargs (dict): {kwargs}")

example_func(1, 2, 3, 4, 5, name="Alice", age=25)
# 输出:
# a=1, b=2
# args (tuple): (3, 4, 5)
# kwargs (dict): {'name': 'Alice', 'age': 25}

5. Python 中的 is== 有什么区别?

  • ==值相等比较,检查两个对象的值是否相同。
  • is身份相等比较,检查两个变量是否指向内存中的同一个对象(即 id 是否相同)。

示例

a = [1, 2, 3]
b = [1, 2, 3]
c = a

print(a == b)  # True,值相同
print(a is b)  # False,不是同一个对象
print(a is c)  # True,c 是 a 的引用,指向同一个对象

二、数据结构与算法

6. 字典(dict)的底层实现原理是什么?

Python 字典使用哈希表实现。主要特点:

  1. 键(key)必须可哈希(即不可变类型,如字符串、数字、元组)。
  2. 通过哈希函数计算键的哈希值,映射到哈希表的某个“桶”(bucket)。
  3. 解决哈希冲突常用开放定址法(Python 使用的一种变体)。
  4. 字典的查找、插入、删除操作平均时间复杂度为 O(1),最坏情况(大量冲突)为 O(n)。

7. 如何实现一个 LRU(最近最少使用)缓存?

可以使用 collections.OrderedDict 或自己结合字典与双向链表实现。Python 3.2+ 提供了 functools.lru_cache 装饰器。

手动实现示例(简化版)

from collections import OrderedDict

class LRUCache:
    def __init__(self, capacity: int):
        self.cache = OrderedDict()
        self.capacity = capacity

    def get(self, key: int) -> int:
        if key not in self.cache:
            return -1
        # 访问后移动到末尾(表示最近使用)
        self.cache.move_to_end(key)
        return self.cache[key]

    def put(self, key: int, value: int) -> None:
        if key in self.cache:
            self.cache.move_to_end(key)
        self.cache[key] = value
        if len(self.cache) > self.capacity:
            # 弹出最久未使用的项(头部)
            self.cache.popitem(last=False)

8. 写一个快速排序(Quick Sort)的实现。

def quick_sort(arr):
    if len(arr) <= 1:
        return arr
    pivot = arr[len(arr) // 2]  # 选择中间元素作为基准
    left = [x for x in arr if x < pivot]
    middle = [x for x in arr if x == pivot]
    right = [x for x in arr if x > pivot]
    return quick_sort(left) + middle + quick_sort(right)

# 测试
print(quick_sort([3, 6, 8, 10, 1, 2, 1]))  # [1, 1, 2, 3, 6, 8, 10]

三、面向对象编程(OOP)

9. Python 中的 __new____init__ 有什么区别?

  • __new__:是一个静态方法,负责创建并返回一个新的实例对象。在 __init__ 之前调用。
  • __init__:是一个实例方法,负责初始化新创建的对象(设置初始属性)。在 __new__ 返回实例后调用。

示例

class MyClass:
    def __new__(cls, *args, **kwargs):
        print("__new__ called")
        instance = super().__new__(cls)  # 调用父类的 __new__ 创建实例
        return instance

    def __init__(self, value):
        print("__init__ called")
        self.value = value

obj = MyClass(10)
# 输出:
# __new__ called
# __init__ called

10. 什么是元类(metaclass)?它有什么作用?

元类是“类的类”,用于控制类的创建行为。type 是 Python 内置的元类。自定义元类需要继承 type 并重写 __new____init__ 方法。

作用

  • 自动为类添加属性或方法。
  • 强制类遵循某种规范(如所有方法必须有文档字符串)。
  • 实现单例模式、注册表模式等。

示例:使用元类自动添加一个 class_name 属性。

class MyMeta(type):
    def __new__(cls, name, bases, attrs):
        attrs['class_name'] = name  # 为类添加属性
        return super().__new__(cls, name, bases, attrs)

class MyClass(metaclass=MyMeta):
    pass

print(MyClass.class_name)  # 输出:MyClass

11. 什么是 MRO(方法解析顺序)?Python 如何确定?

MRO(Method Resolution Order)指在多继承中,Python 查找方法的顺序。Python 3 使用 C3 线性化算法 来确定 MRO,可通过 类名.__mro__类名.mro() 查看。

示例

class A:
    def show(self):
        print("A")

class B(A):
    def show(self):
        print("B")

class C(A):
    def show(self):
        print("C")

class D(B, C):
    pass

print(D.__mro__)
# 输出:(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)

d = D()
d.show()  # 输出:B(按 MRO 顺序先在 B 中找到)

四、并发与异步

12. GIL(全局解释器锁)是什么?它对多线程有什么影响?

GIL 是 CPython 解释器中的一个互斥锁,它确保同一时刻只有一个线程执行 Python 字节码。这意味着:

  • CPU 密集型任务:多线程无法利用多核优势,性能可能不如单线程。
  • I/O 密集型任务:线程在等待 I/O 时会释放 GIL,因此多线程仍能提升效率。

应对策略

  • 使用多进程(multiprocessing)绕过 GIL,充分利用多核。
  • 使用异步编程(asyncio)处理高并发 I/O。
  • 将计算密集型代码用 C 扩展实现(如 NumPy)。

13. 协程(coroutine)和线程(thread)有什么区别?

维度协程线程
调度方式由程序员/事件循环显式控制(协作式)由操作系统内核调度(抢占式)
切换开销极小(上下文保存在用户空间)较大(涉及内核态/用户态切换)
并发数量可轻松创建成千上万个受限于内核线程数(通常数百)
数据共享共享同一线程内存,无需加锁需通过锁机制同步
适用场景I/O 密集型、高并发网络应用CPU 密集型、需要利用多核

14. 写一个使用 asyncio 并发下载多个 URL 的示例。

import asyncio
import aiohttp

async def fetch_url(session, url):
    async with session.get(url) as response:
        return await response.text()

async def download_all(urls):
    async with aiohttp.ClientSession() as session:
        tasks = [fetch_url(session, url) for url in urls]
        results = await asyncio.gather(*tasks)
        return results

# 使用示例
urls = ["https://httpbin.org/get", "https://api.github.com"]
results = asyncio.run(download_all(urls))
for url, content in zip(urls, results):
    print(f"{url}: {len(content)} chars")

五、高级特性与常用模块

15. 装饰器(decorator)的原理是什么?写一个记录函数执行时间的装饰器。

装饰器本质上是一个接收函数作为参数并返回一个新函数的高阶函数。它使用 @ 语法糖,在函数定义时立即执行。

计时装饰器示例

import time
from functools import wraps

def timer(func):
    @wraps(func)  # 保留原函数的元信息
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        end = time.perf_counter()
        print(f"{func.__name__} 执行耗时: {end - start:.4f} 秒")
        return result
    return wrapper

@timer
def slow_function():
    time.sleep(1)
    return "Done"

slow_function()  # 输出:slow_function 执行耗时: 1.0012 秒

16. 上下文管理器(context manager)是什么?如何实现?

上下文管理器用于管理资源(如文件、锁、数据库连接)的获取和释放,确保资源被正确清理。使用 with 语句调用。

实现方式

  1. 类实现:定义 __enter____exit__ 方法。
  2. 生成器 + contextlib.contextmanager:更简洁。

类实现示例

class FileManager:
    def __init__(self, filename, mode):
        self.filename = filename
        self.mode = mode
        self.file = None

    def __enter__(self):
        self.file = open(self.filename, self.mode)
        return self.file

    def __exit__(self, exc_type, exc_val, exc_tb):
        if self.file:
            self.file.close()

# 使用
with FileManager("test.txt", "w") as f:
    f.write("Hello, Context Manager!")

17. collections 模块中 defaultdictCounter 的常用场景?

  • defaultdict:为字典提供默认值工厂,避免键不存在时的 KeyError
    from collections import defaultdict
    dd = defaultdict(list)
    dd["key1"].append(1)  # 无需先判断 "key1" 是否存在
    
  • Counter:用于计数可哈希对象。
    from collections import Counter
    words = ["apple", "banana", "apple", "orange", "banana", "apple"]
    word_count = Counter(words)
    print(word_count)  # Counter({'apple': 3, 'banana': 2, 'orange': 1})
    print(word_count.most_common(2))  # [('apple', 3), ('banana', 2)]
    

六、Web 与数据库

18. WSGI 和 ASGI 的区别是什么?

  • WSGI:同步 Web 服务器网关接口,适用于传统的同步框架(如 Flask、Django 的同步视图)。一个请求对应一个线程/进程。
  • ASGI:异步服务器网关接口,支持异步、长连接(WebSocket)。适用于异步框架(如 FastAPI、Starlette、Django Channels)。

趋势:现代 Python Web 开发中,ASGI 因其更好的并发性能和 WebSocket 支持而越来越流行。

19. 使用 SQLAlchemy 定义一个简单的 ORM 模型。

from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

Base = declarative_base()

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String(50))
    email = Column(String(100), unique=True)

# 创建数据库连接和表
engine = create_engine('sqlite:///example.db')
Base.metadata.create_all(engine)

# 创建会话并操作
Session = sessionmaker(bind=engine)
session = Session()

new_user = User(name="Alice", email="alice@example.com")
session.add(new_user)
session.commit()

七、调试与性能优化

20. 如何分析 Python 程序的性能瓶颈?

  1. 使用 cProfile / profile 进行性能分析
    python -m cProfile -o output.pstats your_script.py
    
  2. 使用 snakeviz 可视化分析结果
    snakeviz output.pstats
    
  3. 使用 line_profiler 进行逐行分析(需要装饰器标记)。
  4. 使用 memory_profiler 分析内存使用
  5. 使用 timeit 模块对小段代码进行精确计时

21. 有哪些常见的 Python 代码优化技巧?

  • 使用局部变量:局部变量比全局变量查找更快。
  • 使用 join() 拼接字符串:避免在循环中使用 +
  • 使用列表推导式:通常比普通 for 循环更快、更简洁。
  • 使用 if item in set:集合(set)的成员检测是 O(1)。
  • 避免不必要的属性访问:在循环中将属性赋值给局部变量。
  • 使用 functools.lru_cache 缓存函数结果(适用于纯函数)。

示例

# 优化前
result = []
for i in range(1000):
    result.append(str(i))
output = "".join(result)

# 优化后(使用列表推导式)
output = "".join([str(i) for i in range(1000)])

八、综合场景题

22. 设计一个简单的装饰器,实现函数调用的单例模式(相同参数只执行一次)。

from functools import wraps

def singleton_cache(func):
    cache = {}
    @wraps(func)
    def wrapper(*args, **kwargs):
        # 以参数元组和关键字参数字典的排序项作为键
        key = (args, frozenset(kwargs.items()))
        if key not in cache:
            cache[key] = func(*args, **kwargs)
        return cache[key]
    return wrapper

@singleton_cache
def expensive_computation(x, y):
    print(f"Computing {x} + {y}...")
    return x + y

print(expensive_computation(1, 2))  # 输出 Computing... 然后 3
print(expensive_computation(1, 2))  # 直接输出 3(缓存命中)

23. 实现一个线程安全的简单队列(使用 threading 模块)。

import threading

class ThreadSafeQueue:
    def __init__(self):
        self.queue = []
        self.lock = threading.Lock()
        self.condition = threading.Condition(self.lock)

    def put(self, item):
        with self.lock:
            self.queue.append(item)
            self.condition.notify()  # 通知等待的消费者

    def get(self):
        with self.lock:
            while not self.queue:
                self.condition.wait()  # 队列空则等待
            return self.queue.pop(0)

# 测试
def producer(q):
    for i in range(5):
        q.put(i)
        print(f"Produced: {i}")
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值