转算法-Python30分钟突击
目录
Python 数据结构基础
1. 数据结构初始化
空对象创建方式
python
# 列表(List)- 有序、可变
empty_list = [] # 推荐方式
empty_list = list() # 构造函数方式
# 字典(Dictionary)- 键值对、可变
empty_dict = {} # 推荐方式
empty_dict = dict() # 构造函数方式
# 元组(Tuple)- 有序、不可变
empty_tuple = () # 推荐方式
empty_tuple = tuple() # 构造函数方式
# 集合(Set)- 无序、不重复、可变
empty_set = set() # 只能用构造函数({} 是空字典)
# empty_set = {} # ❌ 错误!这是空字典
# 字符串(String)- 不可变
empty_string = "" # 推荐方式
empty_string = '' # 单引号也可以
empty_string = str() # 构造函数方式
为什么空集合不能用 {}?
历史原因:
- Python 2.3 之前只有字典,
{}表示空字典 - Python 2.4 引入集合,为了向后兼容,
{}仍表示空字典 - 空
{}无法区分是字典还是集合,所以空集合必须用set()
非空时的区别:
python
# 字典:有键值对(冒号)
person = {'name': '张三', 'age': 25} # 明显是字典
# 集合:只有值(没有冒号)
numbers = {1, 2, 3, 4, 5} # 明显是集合
fruits = {'apple', 'banana'} # 明显是集合
2. 数据结构特性对比
| 类型 | 有序 | 可变 | 重复 | 索引 | 主要用途 |
|---|---|---|---|---|---|
| List | ✅ | ✅ | ✅ | ✅ | 有序数据集合 |
| Tuple | ✅ | ❌ | ✅ | ✅ | 不可变有序数据 |
| Dict | ✅ (3.7+) | ✅ | ❌ (键) | ✅ (键) | 键值对映射 |
| Set | ❌ | ✅ | ❌ | ❌ | 去重、成员测试 |
| String | ✅ | ❌ | ✅ | ✅ | 文本数据 |
3. 数据结构详细说明
列表(List)
python
# 创建
my_list = [1, 2, 3]
my_list = list([1, 2, 3])
# 特性
my_list.append(4) # 添加元素
my_list.insert(0, 0) # 在指定位置插入
my_list.remove(2) # 删除元素
my_list.pop() # 删除并返回最后一个元素
my_list[0] # 访问元素
my_list[1:3] # 切片
# 列表推导式
squares = [x**2 for x in range(10)]
evens = [x for x in range(10) if x % 2 == 0]
元组(Tuple)
python
# 创建
my_tuple = (1, 2, 3)
my_tuple = tuple([1, 2, 3])
single_tuple = (5,) # 单元素元组(注意逗号!)
not_tuple = (5) # ❌ 这不是元组,是数字 5
# 特性
my_tuple[0] # 访问元素
my_tuple[1:3] # 切片
# my_tuple[0] = 10 # ❌ 错误!元组不可变
# 作为字典的键
locations = {
(0, 0): '原点',
(10, 20): 'A点'
}
# 解包
x, y, z = my_tuple
字典(Dictionary)
python
# 创建
my_dict = {'name': '张三', 'age': 25}
my_dict = dict(name='张三', age=25)
# 访问
my_dict['name'] # 直接访问(键不存在会报错)
my_dict.get('name') # 安全访问(键不存在返回 None)
my_dict.get('name', '默认值') # 带默认值
# 操作
my_dict['city'] = '北京' # 添加/更新
del my_dict['age'] # 删除
'name' in my_dict # 检查键是否存在
# 字典推导式
squares = {x: x**2 for x in range(5)}
集合(Set)
python
# 创建
my_set = {1, 2, 3}
my_set = set([1, 2, 3])
# 特性
my_set.add(4) # 添加元素
my_set.remove(2) # 删除元素(不存在会报错)
my_set.discard(2) # 删除元素(不存在不报错)
3 in my_set # 成员测试(O(1) 时间复杂度)
# 集合运算
set1 = {1, 2, 3}
set2 = {3, 4, 5}
set1 | set2 # 并集: {1, 2, 3, 4, 5}
set1 & set2 # 交集: {3}
set1 - set2 # 差集: {1, 2}
set1 ^ set2 # 对称差集: {1, 2, 4, 5}
# 集合推导式
evens = {x for x in range(10) if x % 2 == 0}
字符串(String)
python
# 创建
my_string = "Hello"
my_string = 'World'
my_string = """多行
字符串"""
# 特性
my_string[0] # 访问字符
my_string[1:4] # 切片
my_string.upper() # 转大写
my_string.lower() # 转小写
my_string.split(' ') # 分割
' '.join(['Hello', 'World']) # 连接
# 格式化
name = "张三"
age = 25
f"姓名: {name}, 年龄: {age}" # f-string (Python 3.6+)
"姓名: {}, 年龄: {}".format(name, age) # format
"姓名: %s, 年龄: %d" % (name, age) # % 格式化
Python 核心概念
1. 可变对象 vs 不可变对象
不可变对象(Immutable)
- 数字(int, float, complex)
- 字符串(str)
- 元组(tuple)
- 布尔值(bool)
- frozenset
python
# 不可变对象
x = 10
y = x
x = 20
print(y) # 10(y 不受影响)
# 字符串不可变
s = "Hello"
s[0] = 'h' # ❌ 错误!TypeError
可变对象(Mutable)
- 列表(list)
- 字典(dict)
- 集合(set)
- 自定义对象
python
# 可变对象
list1 = [1, 2, 3]
list2 = list1
list1.append(4)
print(list2) # [1, 2, 3, 4](list2 也被修改了!)
# 字典可变
dict1 = {'a': 1}
dict2 = dict1
dict1['b'] = 2
print(dict2) # {'a': 1, 'b': 2}
2. 参数传递:值传递 vs 引用传递
Python 中一切都是对象引用传递!
python
def modify_list(lst):
lst.append(4) # 修改原列表
my_list = [1, 2, 3]
modify_list(my_list)
print(my_list) # [1, 2, 3, 4] - 原列表被修改
def modify_int(x):
x = 10 # 创建新对象
my_int = 5
modify_int(my_int)
print(my_int) # 5 - 原值不变(因为整数不可变)
3. 深拷贝 vs 浅拷贝
python
import copy
# 浅拷贝
list1 = [[1, 2], [3, 4]]
list2 = copy.copy(list1) # 或 list1.copy()
list2[0].append(5)
print(list1) # [[1, 2, 5], [3, 4]] - 内部列表被修改!
# 深拷贝
list1 = [[1, 2], [3, 4]]
list2 = copy.deepcopy(list1)
list2[0].append(5)
print(list1) # [[1, 2], [3, 4]] - 原列表不变
4. 列表推导式 vs 生成器表达式
python
# 列表推导式 - 立即创建列表
squares = [x**2 for x in range(10)] # 占用内存
# 生成器表达式 - 延迟计算
squares_gen = (x**2 for x in range(10)) # 节省内存
print(list(squares_gen)) # 需要时才计算
5. 装饰器(Decorator)
python
def my_decorator(func):
def wrapper(*args, **kwargs):
print("函数执行前")
result = func(*args, **kwargs)
print("函数执行后")
return result
return wrapper
@my_decorator
def greet(name):
print(f"Hello, {name}")
greet("张三")
# 输出:
# 函数执行前
# Hello, 张三
# 函数执行后
6. 上下文管理器(Context Manager)
python
# with 语句
with open('file.txt', 'r') as f:
content = f.read()
# 文件自动关闭
# 自定义上下文管理器
class MyContext:
def __enter__(self):
print("进入上下文")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("退出上下文")
return False
with MyContext():
print("执行代码")
7. 生成器(Generator)
python
# 生成器函数
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
gen = fibonacci()
print(next(gen)) # 0
print(next(gen)) # 1
print(next(gen)) # 1
8. 协程(Coroutine)
核心概念
协程是 Python 语言层面的实现,在单线程内的任务切换;多线程是操作系统层面的实现。
- 协程:Python 语言层面,单线程内切换,用户态调度
- 多线程:操作系统层面,多线程间切换,内核态调度
基本语法
python
import asyncio
async def fetch_data(url):
# async def 定义协程函数
await asyncio.sleep(1) # await 等待异步操作
return f"数据来自 {url}"
async def main():
# 并发执行多个协程
tasks = [fetch_data(f"url{i}") for i in range(5)]
results = await asyncio.gather(*tasks)
return results
# 运行异步主函数
results = asyncio.run(main())
python
import asyncio
import time
async def fetch_data(url, delay):
print(f'开始获取{url}')
await asyncio.sleep(delay)
print(f'完成获取{url}')
return f"{url} 完成"
async def main():
print("顺序执行")
await fetch_data('test1.com', 1)
await fetch_data('test2.com', 1)
print("并发执行开始")
await asyncio.gather(fetch_data('test3.com', 1),
fetch_data('test4.com', 1),
fetch_data('test5.com', 1),
fetch_data('test6.com', 1),
)
print("并发执行结束")
asyncio.run(main())
python
import asyncio
import time
async def gather_data(url, delay):
print(f'开始请求{url}')
await asyncio.sleep(delay)
print(f'结束请求{url}')
async def main():
await gather_data('1.com', 1)
await gather_data('2.com', 1)
await asyncio.gather(gather_data('3.com', 1),
gather_data('4.com', 1),
gather_data('5.com', 1),
gather_data('6.com', 1))
task1 = asyncio.create_task(gather_data('7.com', 1))
task2 = asyncio.create_task(gather_data('8.com', 1))
await task1
await task2
asyncio.run(main())
协程 vs 多线程
| 特性 | 协程 | 多线程 |
|---|---|---|
| 实现层面 | Python 语言层面 | 操作系统层面 |
| 执行环境 | 单线程内 | 多线程间 |
| 内存占用 | 很小(几 KB) | 较大(1-8 MB) |
| 并发数量 | 几乎无限制(数万+) | 受限(几百到几千) |
| 切换开销 | 很小(纳秒级) | 较大(微秒级) |
| 适用场景 | I/O 密集型任务 | 少量并发 I/O |
为什么需要协程?
虽然多线程在 I/O 操作时会释放 GIL,但协程仍有明显优势:
- 资源开销更小:协程只需几 KB 内存,线程需要 1-8 MB
- 并发能力更强:可以轻松处理数万个并发,线程受系统限制
- 性能更好:切换开销纳秒级,线程切换需要微秒级
- 编程更简单:单线程模型,无需处理锁和同步
实际应用
python
import asyncio
import aiohttp
async def fetch_url(session, url):
async with session.get(url) as response:
return await response.status
async def main():
async with aiohttp.ClientSession() as session:
urls = ['https://api.example.com'] * 100
tasks = [fetch_url(session, url) for url in urls]
results = await asyncio.gather(*tasks) # 并发执行
return results
# 适合高并发 I/O 场景(Web 服务器、爬虫、API 客户端)
asyncio.run(main())
9. 异常处理
python
try:
result = 10 / 0
except ZeroDivisionError as e:
print(f"除零错误: {e}")
except Exception as e:
print(f"其他错误: {e}")
else:
print("没有错误")
finally:
print("总是执行")
常见面试问题
1. Python 基础
Q1: Python 中 is 和 == 的区别?
python
# == 比较值
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b) # True(值相同)
# is 比较对象标识(内存地址)
print(a is b) # False(不同对象)
# 小整数和字符串会被缓存
x = 256
y = 256
print(x is y) # True(小整数缓存)
x = 257
y = 257
print(x is y) # False(大整数不缓存)
Q2: *args 和 **kwargs 是什么?
python
def func(*args, **kwargs):
# *args: 接收任意数量的位置参数(元组)
# **kwargs: 接收任意数量的关键字参数(字典)
print(f"args: {args}")
print(f"kwargs: {kwargs}")
func(1, 2, 3, name='张三', age=25)
# args: (1, 2, 3)
# kwargs: {'name': '张三', 'age': 25}
def print_info(*args, **kwargs):
for arg in args:
print(f'{arg}')
for key, value in kwargs.items():
print(f'{key} {value}')
print_info(1,2,3,4, name='name', age='age')
def greet(name, greeting='hello'):
# 解包出来 name
# 解包出来 greeting
print(f'{name} {greeting}')
def wrapper(func, *args, **kwargs):
# args 被打包成 ('lili',)
# kwargs 被打包成 {greeting:'hello'}
func(*args, **kwargs)
wrapper(greet, "lili", greeting='hello')
Q3: Python 的 GIL(全局解释器锁)是什么?
- GIL 是 CPython 解释器的特性
- 同一时刻只有一个线程执行 Python 字节码
- 影响 CPU 密集型任务,但不影响 I/O 密集型任务
- 可以通过多进程(multiprocessing)绕过 GIL
Q4: __init__ 和 __new__ 的区别?
python
class MyClass:
def __new__(cls, *args, **kwargs):
# 创建实例(在 __init__ 之前调用)
print("创建实例")
return super().__new__(cls)
def __init__(self, value):
# 初始化实例
print("初始化实例")
self.value = value
2. 数据结构相关
Q5: 如何实现一个单例模式?
python
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
# 或使用装饰器
def singleton(cls):
instances = {}
def get_instance(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return get_instance
Q6: 如何合并两个字典?
python
dict1 = {'a': 1, 'b': 2}
dict2 = {'c': 3, 'd': 4}
# Python 3.5+
merged = {**dict1, **dict2}
# Python 3.9+
merged = dict1 | dict2
# 传统方式
merged = dict1.copy()
merged.update(dict2)
Q7: 如何去除列表中的重复元素?
python
my_list = [1, 2, 2, 3, 3, 3]
# 方法1: 使用集合(不保持顺序)
unique = list(set(my_list))
# 方法2: 使用字典(保持顺序,Python 3.7+)
unique = list(dict.fromkeys(my_list))
# 方法3: 列表推导式(保持顺序)
seen = set()
unique = [x for x in my_list if x not in seen and not seen.add(x)]
3. 算法和性能
Q8: 列表和集合的查找时间复杂度?
python
# 列表查找: O(n) - 线性查找
my_list = [1, 2, 3, 4, 5]
if 3 in my_list: # 需要遍历
pass
# 集合查找: O(1) - 哈希表查找
my_set = {1, 2, 3, 4, 5}
if 3 in my_set: # 直接查找
pass
Q9: 如何反转一个列表?
python
my_list = [1, 2, 3, 4, 5]
# 方法1: 切片
reversed_list = my_list[::-1]
# 方法2: reverse()(原地修改)
my_list.reverse()
# 方法3: reversed()(返回迭代器)
reversed_list = list(reversed(my_list))
Q10: 如何找到列表中的最大值和最小值?
python
my_list = [3, 1, 4, 1, 5, 9, 2, 6]
# 内置函数
max_value = max(my_list)
min_value = min(my_list)
# 手动实现
max_value = my_list[0]
for x in my_list:
if x > max_value:
max_value = x
4. 面向对象
Q11: 类变量和实例变量的区别?
python
class MyClass:
class_var = "类变量" # 所有实例共享
def __init__(self, value):
self.instance_var = value # 每个实例独有
obj1 = MyClass(1)
obj2 = MyClass(2)
print(obj1.class_var) # "类变量"
print(obj2.class_var) # "类变量"
print(obj1.instance_var) # 1
print(obj2.instance_var) # 2
Q12: 私有变量和受保护变量?
python
class MyClass:
def __init__(self):
self.public = "公开" # 公开
self._protected = "受保护" # 约定(单下划线)
self.__private = "私有" # 名称修饰(双下划线)
obj = MyClass()
print(obj.public) # ✅ 可以访问
print(obj._protected) # ⚠️ 可以访问(但不推荐)
print(obj.__private) # ❌ 错误!实际名称是 _MyClass__private
Q13: 多继承的 MRO(方法解析顺序)?
python
class A:
def method(self):
print("A")
class B(A):
def method(self):
print("B")
class C(A):
def method(self):
print("C")
class D(B, C):
pass
d = D()
d.method() # 输出: B(按 MRO 顺序)
print(D.__mro__) # 查看方法解析顺序
5. 高级特性
Q14: 什么是闭包?
python
def outer_function(x):
def inner_function(y):
return x + y
return inner_function
closure = outer_function(10)
print(closure(5)) # 15(闭包记住了 x=10)
Q15: 如何实现一个迭代器?
python
class MyIterator:
def __init__(self, data):
self.data = data
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index >= len(self.data):
raise StopIteration
value = self.data[self.index]
self.index += 1
return value
my_iter = MyIterator([1, 2, 3])
for item in my_iter:
print(item)
Q16: 描述符(Descriptor)是什么?
python
class Descriptor:
def __init__(self, name):
self.name = name
def __get__(self, obj, objtype=None):
return obj.__dict__.get(self.name)
def __set__(self, obj, value):
obj.__dict__[self.name] = value
class MyClass:
x = Descriptor('x')
obj = MyClass()
obj.x = 10
print(obj.x) # 10
6. 并发和异步
Q17: 多线程 vs 多进程?
python
# 多线程(适合 I/O 密集型)
import threading
def worker():
print("线程执行")
thread = threading.Thread(target=worker)
thread.start()
# 多进程(适合 CPU 密集型)
import multiprocessing
def worker():
print("进程执行")
process = multiprocessing.Process(target=worker)
process.start()
Q18: 如何使用 asyncio?
python
import asyncio
async def fetch_data():
await asyncio.sleep(1)
return "数据"
async def main():
tasks = [fetch_data() for _ in range(5)]
results = await asyncio.gather(*tasks)
return results
results = asyncio.run(main())
import asyncio
async def fetch_data():
await asyncio.sleep(1)
return "数据"
async def main():
tasks = [fetch_data() for _ in range(5)]
results = await asyncio.gather(*tasks)
return results
results = asyncio.run(main())
代码实践题
1. 实现一个 LRU 缓存
python
from collections import OrderedDict
class LRUCache:
def __init__(self, capacity) -> None:
if capacity <= 0:
raise ValueError("capacity must be > 0")
self.capacity = capacity
self.cache = OrderedDict()
def get(self, key: int) -> int | None:
if key in self.cache:
self.cache.move_to_end(key)
return self.cache[key]
return None
def put(self, key : int, value : int) -> None:
self.cache[key] = value
self.cache.move_to_end(key)
if len(self.cache) > self.capacity:
self.cache.popitem(last=False)
2. 实现一个装饰器来测量函数执行时间
python
import time
from functools import wraps
def timing_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} 执行时间: {end - start:.4f} 秒")
return result
return wrapper
@timing_decorator
def slow_function():
time.sleep(1)
return "完成"
3. 实现一个单例模式的线程安全版本
python
import threading
class Singleton:
_instance = None
_lock = threading.Lock()
def __new__(cls, *args, **kwargs):
if cls._instance is None:
with cls._lock:
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self, *args, **kwargs):
if getattr(self, "_singleton_initialized", False):
return
self._singleton_initialized = True
# 仅首次构造执行:把需要初始化的成员写在这里```
### 4. 实现一个简单的上下文管理器
```python
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()
return False
# 使用
with FileManager('test.txt', 'w') as f:
f.write('Hello')
5. 实现一个生成器来生成斐波那契数列
python
def fibonacci(n):
a, b = 0, 1
count = 0
while count < n:
yield a
a, b = b, a + b
count += 1
# 使用
for num in fibonacci(10):
print(num)
################
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a+b
fib = fibonacci()
print(next(fib))
print(next(fib))
面试技巧
1. 准备阶段
- 熟悉 Python 官方文档
- 练习 LeetCode Python 题目
- 了解常用库(requests, pandas, numpy 等)
- 准备项目经验和技术难点
2. 回答问题时
- 先说思路,再写代码
- 考虑边界情况
- 讨论时间复杂度和空间复杂度
- 提出优化方案
3. 常见陷阱
- 可变默认参数问题
- 列表推导式的作用域
- 闭包变量绑定
- 深拷贝和浅拷贝
4. 推荐学习资源
- Python 官方文档
- 《流畅的 Python》
- 《Python Cookbook》
- LeetCode
- GitHub 上的 Python 项目
总结
这份文档涵盖了 Python 面试的核心知识点:
- ✅ 数据结构基础(初始化、特性、使用场景)
- ✅ Python 核心概念(可变/不可变、参数传递、拷贝等)
- ✅ 常见面试问题(基础、数据结构、算法、面向对象等)
- ✅ 代码实践题(LRU、装饰器、单例等)
记住:理解原理比死记硬背更重要!