Python基础速通指南
一、Python 基础语法与数据结构
1.1 基础语法:不仅仅是“hello world”
Python 语法简洁,但企业级开发对代码的可读性和健壮性有更高要求。核心包括:
- 变量与数据类型:动态类型,但建议使用类型注解(Type Hints)提升代码可维护性。
- 控制流:
if/elif/else、for、while。 - 函数定义:使用
def,支持默认参数、关键字参数、可变参数*args和**kwargs(注*)。 - 异常处理:使用
try...except...finally优雅地处理错误,避免程序崩溃。
入门案例:带类型注解与异常处理的用户数据处理器
from typing import Dict, List, Optional
def process_user_data(raw_data: List[Dict[str, str]]) -> Optional[Dict[str, int]]:
"""
处理用户原始数据,统计活跃用户数量。
企业级实践:使用类型注解增强可读性,加入异常处理增强健壮性。
"""
try:
active_count = 0
for user in raw_data:
# 使用 .get() 安全地获取字段,避免 KeyError
if user.get('status') == 'active':
active_count += 1
return {"active_users": active_count}
except Exception as e:
# 记录异常日志(实际项目中会用 logging 模块)
print(f"An error occurred: {e}")
return None # 优雅降级
# 测试数据
users = [{"name": "Alice", "status": "active"}, {"name": "Bob", "status": "inactive"}]
result = process_user_data(users)
print(result) # 输出: {'active_users': 1}
注: """ 是跨行注释吗?
1.2 核心数据结构:选型决定性能
| 数据结构 | 特点 | 适用场景 | 时间复杂度(平均) |
|---|---|---|---|
| 列表(List) | 有序、可变 ,用方括号[]表示,如 fruits =["apple", "banana"] |
频繁增删元素、需要保持顺序的集合 | 索引:O(1), 追加:O(1), 插入/删除:O(n) |
| 元组(Tuple) | 有序、不可变,用圆括号()表示,如 colors = ("red", "green") |
作为字典的键、函数返回多个值、不需要修改的数据 | 索引:O(1) |
| 字典(Dict) | 键值对集合、无序(Python 3.7+ 保留插入顺序)、可变,用花括号{}表示,如 person = {"name": "Bob", "age": 25} |
快速查找、关联数据 | 查找/插入/删除:O(1) |
| 集合(Set) | 无序、元素唯一,用花括号{}表示,如 unique_nums = {1, 2, 3} |
去重、快速成员测试、集合运算(交集、并集) | 查找/插入/删除:O(1) |
入门案例:利用集合高效去重与交集运算
# 场景:分析两个数据源的客户ID,找出共同客户和所有唯一客户
customers_a = [101, 102, 103, 104]
customers_b = [103, 104, 105, 106]
set_a = set(customers_a)
set_b = set(customers_b)
# 共同客户(交集)
common_customers = set_a & set_b # 或 set_a.intersection(set_b)
print(f"共同客户: {common_customers}") # 输出: {103, 104}
# 所有唯一客户(并集)
all_customers = set_a | set_b # 或 set_a.union(set_b)
print(f"所有客户: {all_customers}") # 输出: {101, 102, 103, 104, 105, 106}
企业最佳实践:数据结构选型与优化(详解*)
- 优先使用元组代替列表:若数据无需修改,使用元组更安全、性能略优。元组可作为字典键,而列表不行。
- 用集合/字典进行成员判断:需要频繁检查元素是否存在时(如
if x in container),使用集合或字典,其O(1)的查找速度远优于列表的O(n)。 - 列表头部操作频繁时用
deque:collections.deque(双端队列)在列表两端进行插入/删除操作时性能远优于list。 - 善用推导式:列表、字典、集合推导式不仅简洁,且通常比显式循环更快。
二、文件操作 (File I/O)
2.1 基础回顾:安全读写
文件操作的核心是open()函数,务必使用with语句自动管理资源。
入门案例:安全地读取配置文件
# ✅ 最佳实践:使用 with 语句,文件会在代码块结束后自动关闭
try:
with open('config.txt', 'r', encoding='utf-8') as file:
content = file.read()
print(content)
except FileNotFoundError:
print("配置文件未找到,请检查路径。")
except Exception as e:
print(f"读取文件时发生未知错误: {e}")
2.2 进阶技巧与最佳实践
- 处理大文件:永远不要一次性读取大文件(如几GB的日志)。应逐行读取或分块读取。
- 批量操作:减少I/O次数,一次性写入大量数据比多次小量写入快得多。
- 使用
pathlib:Python 3.4+ 推荐的面向对象的路径操作库,比os.path更优雅。
企业最佳实践:高效处理大型日志文件
from pathlib import Path
import logging
# 配置日志(实际项目中会更复杂)
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
def process_large_log(file_path: Path, keyword: str) -> int:
"""
逐行读取大型日志文件,统计包含特定关键字的行数。
企业级实践:使用 pathlib,逐行处理避免内存溢出。
"""
if not file_path.exists():
logging.error(f"文件不存在: {file_path}")
return 0
count = 0
try:
# 使用 with 和 pathlib 打开文件
with file_path.open('r', encoding='utf-8') as file:
for line in file: # 逐行迭代,内存友好
if keyword in line:
count += 1
logging.info(f"在 {file_path.name} 中找到 {count} 行包含 '{keyword}'")
return count
except Exception as e:
logging.error(f"处理文件 {file_path} 时出错: {e}")
return 0
# 使用示例
log_dir = Path("/var/log/myapp/")
# Path 对象支持 / 运算符拼接路径
process_large_log(log_dir / "app.log", "ERROR")
三、异步IO (asyncio)
3.1 基础概念与入门
asyncio 是处理 I/O 密集型任务(网络请求、数据库查询、文件读写)的利器。它通过单线程事件循环(Event Loop) 在等待 I/O 时切换到其他任务,实现并发。核心是 async/await 语法。
入门案例:并发执行多个模拟I/O任务
import asyncio
import time
async def task(name: str, duration: int):
print(f"任务 {name} 开始,预计耗时 {duration} 秒")
await asyncio.sleep(duration) # 模拟 I/O 操作,如网络请求
print(f"任务 {name} 完成")
return f"{name} 的结果"
async def main():
start = time.time()
# asyncio.gather 并发执行所有协程
results = await asyncio.gather(
task("A", 2),
task("B", 1),
task("C", 3)
)
print(f"所有结果: {results}")
print(f"总耗时: {time.time() - start:.2f} 秒") # 约3秒,而非6秒
# 运行异步主函数
asyncio.run(main())
gather()的核心作用是并发执行多个异步任务,并等待全部完成后,将结果按原始顺序汇总。这种设计保证了结果与输入的一一对应关系,方便在后续处理时知道哪个结果来自哪个任务。那如何按任务完成的顺序获取结果?
如果业务逻辑需要在任务一完成就立刻处理其结果,而不是等待所有任务结束,可以使用
asyncio.as_completed()。
asyncio.as_completed()返回一个迭代器,你可以异步地遍历它。每次迭代会await出一个任务的结果,这个结果的顺序就是任务实际完成的顺序。import asyncio async def task(name, delay): await asyncio.sleep(delay) return f"{name} (耗时{delay}s)" async def main(): tasks = [ task("A", 2), task("B", 1), task("C", 3) ] # as_completed 按完成顺序产出结果 for coro in asyncio.as_completed(tasks): result = await coro print(f"完成: {result}") # 可能的输出顺序(任务B先完成,接着是A,最后是C): # 完成: B (耗时1s) # 完成: A (耗时2s) # 完成: C (耗时3s) asyncio.run(main())
3.2 企业级最佳实践
- 区分I/O密集与CPU密集:
asyncio对 I/O 密集型任务效果显著,但对 CPU 密集型任务无效,甚至会因阻塞事件循环而拖慢程序。 - 控制并发数量:使用
asyncio.Semaphore(信号量)限制同时运行的协程数量,避免压垮下游服务。 - 设置超时:使用
asyncio.wait_for或asyncio.timeout(Python 3.11+) 为任务设置超时,防止无限等待。 - 使用
TaskGroup(Python 3.11+):更现代的结构化并发管理方式,能自动处理任务组的错误传播。 - 分层架构:将异步层(I/O)与同步业务逻辑层分离,便于测试和维护。
企业最佳实践:带流量控制和超时的异步HTTP客户端
import asyncio
import httpx # 支持异步的 HTTP 客户端,比 aiohttp 更易用
from typing import List
async def fetch_url(client: httpx.AsyncClient, url: str, semaphore: asyncio.Semaphore):
"""使用信号量控制并发,并设置超时"""
async with semaphore: # 获取信号量,控制并发数
try:
# 设置单次请求超时 10 秒
response = await client.get(url, timeout=10.0)
return {"url": url, "status": response.status_code}
except httpx.TimeoutException:
return {"url": url, "error": "Timeout"}
except Exception as e:
return {"url": url, "error": str(e)}
async def fetch_all(urls: List[str], max_concurrent: int = 5):
"""并发获取多个 URL,控制最大并发数"""
semaphore = asyncio.Semaphore(max_concurrent)
async with httpx.AsyncClient() as client:
tasks = [fetch_url(client, url, semaphore) for url in urls]
# asyncio.gather 并发执行所有任务
results = await asyncio.gather(*tasks)
return results
# 使用示例
urls = ["https://httpbin.org/delay/1"] * 20 # 模拟20个耗时1秒的请求
results = asyncio.run(fetch_all(urls, max_concurrent=10))
success_count = sum(1 for r in results if 'error' not in r)
print(f"成功请求: {success_count}/{len(urls)}")
四、Requests 库
4.1 基础用法回顾
Requests 是同步 HTTP 客户端库的首选。
入门案例:发送GET请求并处理JSON响应
import requests
try:
response = requests.get('https://api.github.com/events', timeout=5)
response.raise_for_status() # 如果状态码不是 2xx,会抛出 HTTPError
data = response.json() # 自动解析 JSON
print(f"获取到 {len(data)} 条事件")
except requests.exceptions.RequestException as e:
print(f"请求失败: {e}")
4.2 企业级最佳实践
- 使用 Session 对象复用连接:
requests.Session会复用底层的 TCP 连接,显著提升性能(减少30%+连接耗时)。同时保持 Cookies 和 Headers 状态。 - 强制设置超时:永远不要使用没有超时的请求,否则程序可能永久挂起。应分别设置
connect和read超时。 - 处理重试:使用
requests.adapters.HTTPAdapter配置重试策略,应对网络抖动。 - 安全加固:
- 令牌管理:从环境变量或密钥管理服务(如 Vault)读取,禁止硬编码。
- SSL验证:生产环境必须启用 SSL 证书验证(
verify=True),这是默认行为。
- 日志与可观测性:记录请求ID、耗时、状态码等,便于问题追踪。
企业最佳实践:封装一个生产级的API客户端
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
import logging
import os
from uuid import uuid4
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class EnterpriseAPIClient:
"""企业级 API 客户端封装"""
def __init__(self, base_url: str):
self.base_url = base_url.rstrip('/')
self.session = requests.Session()
# 1. 配置重试策略
retry_strategy = Retry(
total=3, # 总重试次数
backoff_factor=1, # 重试间隔
status_forcelist=[429, 500, 502, 503, 504], # 需要重试的HTTP状态码
)
adapter = HTTPAdapter(max_retries=retry_strategy)
self.session.mount("http://", adapter)
self.session.mount("https://", adapter)
# 2. 设置默认 Headers
self.session.headers.update({
'User-Agent': 'MyApp/1.0',
'X-Request-Id': self._generate_request_id()
})
# 3. 从环境变量读取令牌(安全实践)
self.token = os.environ.get('API_TOKEN')
if self.token:
self.session.headers.update({'Authorization': f'Bearer {self.token}'})
def _generate_request_id(self) -> str:
"""生成全局唯一的请求ID,用于链路追踪"""
return f"req-{uuid4().hex[:16]}"
def _log_request(self, method: str, url: str, **kwargs):
"""记录请求日志(脱敏处理)"""
# 实际中可记录更详细的信息
logger.info(f"Request: {method} {url}")
def get(self, endpoint: str, params=None, **kwargs):
"""发送 GET 请求"""
url = f"{self.base_url}/{endpoint.lstrip('/')}"
self._log_request('GET', url)
try:
# 4. 强制设置超时 (连接超时5秒,读取超时30秒)
response = self.session.get(url, params=params, timeout=(5, 30), **kwargs)
response.raise_for_status() # 抛出HTTP错误
return response.json()
except requests.exceptions.RequestException as e:
logger.error(f"API请求失败: {e}", exc_info=True)
# 可在这里实现更细粒度的错误处理,如401时刷新令牌
raise
def close(self):
"""清理资源"""
self.session.close()
# 使用示例
if __name__ == "__main__":
client = EnterpriseAPIClient("https://api.example.com")
try:
data = client.get("/users", params={"page": 1})
print(data)
finally:
client.close() # 确保会话被关闭
五、NumPy 库
5.1 基础回顾:向量化操作
NumPy 的核心是 ndarray,其精髓在于向量化操作——对整个数组执行运算,避免慢速的 Python 循环。
入门案例:向量化 vs. 循环的性能对比
import numpy as np
import time
# 创建一个大数组
size = 10_000_000
data = np.random.rand(size)
# 1. 使用 Python 循环
start = time.time()
result_loop = [x**2 for x in data]
print(f"循环耗时: {time.time() - start:.4f} 秒")
# 2. 使用 NumPy 向量化操作
start = time.time()
result_vectorized = data ** 2
print(f"向量化耗时: {time.time() - start:.4f} 秒")
# 向量化通常比循环快几十到几百倍
5.2 企业级性能最佳实践
- 优先使用内置函数 (ufunc):NumPy 的通用函数(如
np.sum,np.mean,np.dot)都是高度优化的 C 代码实现。 - 利用广播 (Broadcasting):对不同形状的数组进行运算时,善用广播机制,避免创建不必要的中间数组。
- 避免不必要的数组复制:理解视图(View) 与副本(Copy) 的区别。切片通常返回视图,修改视图会影响原数组,这能节省内存;需要独立数据时再显式使用
.copy()。 - 选择合适的数据类型 (
dtype):使用float32而非默认的float64可以节省一半内存,在精度允许的情况下提升性能。 - 考虑内存布局:根据操作是行优先还是列优先,选择
order='C'或order='F'。 - 使用
np.memmap处理超大数据集:对于无法完全加载到内存的数据,使用内存映射文件。 - 利用
Numba或NumExpr加速:对于极度复杂的计算,可以使用 Numba 的 JIT 编译或 NumExpr 加速。
企业最佳实践:优化的数据预处理管道
import numpy as np
from typing import Tuple
def preprocess_data(data: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
"""
企业级数据预处理:标准化 + 特征工程
实践:向量化操作、视图、选择合适dtype
"""
# 1. 确保数据是 float32 以节省内存
if data.dtype != np.float32:
data = data.astype(np.float32)
# 2. 计算均值和标准差(使用内置函数)
mean = np.mean(data, axis=0)
std = np.std(data, axis=0)
# 3. 标准化 (使用广播)
# 注意:这里 data - mean 会创建一个新数组,但这是必要的
normalized = (data - mean) / (std + 1e-8) # 避免除以零
# 4. 特征工程:创建新特征(使用视图和向量化操作)
# 假设 data 的最后一列是目标值,我们基于前两列创建交互特征
# 使用视图避免复制
feature1 = normalized[:, 0]
feature2 = normalized[:, 1]
interaction = feature1 * feature2 # 向量化乘法
# 5. 合并特征 (使用 column_stack 而非多次 concatenate)
# 注意:interaction 需要 reshape 成列向量
features = np.column_stack([normalized, interaction.reshape(-1, 1)])
return features, mean, std
# 生成模拟数据: 100万行,5列
data = np.random.rand(1_000_000, 5).astype(np.float32)
features, mean, std = preprocess_data(data)
print(f"处理后的特征形状: {features.shape}") # (1000000, 6)
六、Pandas 库(企业级数据处理)
6.1 基础回顾:DataFrame 核心操作
Pandas 的 DataFrame 是数据分析的核心。
入门案例:数据加载、清洗与分组聚合
import pandas as pd
# 1. 创建示例数据
df = pd.DataFrame({
'Name': ['Alice', 'Bob', 'Charlie', 'David', 'Eve'],
'Department': ['HR', 'Engineering', 'Engineering', 'HR', 'Marketing'],
'Salary': [70000, 85000, 90000, 72000, 65000]
})
# 2. 数据查看
print(df.head())
print(df.info())
# 3. 分组聚合:计算每个部门的平均薪资
avg_salary_by_dept = df.groupby('Department')['Salary'].mean()
print(avg_salary_by_dept)
6.2 企业级最佳实践
- 按需读取与类型优化:读取大型CSV时,指定
usecols只读取需要的列,用dtype参数指定列类型以节省内存。 - 链式操作:使用
.query()进行过滤,用.assign()创建新列,使代码更清晰。 - 使用
category类型:将低基数的类别型列(如性别、地区)转为category类型,可大幅节省内存并提升groupby等操作的速度。 - 避免在循环中修改DataFrame:使用向量化操作或
apply(),但apply()本质上仍是循环,应优先使用内置的向量化方法。 - 建立数据管道:将清洗、转换、聚合等步骤封装成函数,形成可重复、可测试的数据处理管道。
- 从数据库读取时使用
read_sql_query:明确指定SQL语句,比read_sql更安全,可避免 SQL 注入风险。
企业最佳实践:构建可复用的数据清洗与报告管道
import pandas as pd
from pathlib import Path
from typing import Optional
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class DataPipeline:
"""企业级数据处理管道"""
def __init__(self, raw_data_path: Path):
self.raw_data_path = raw_data_path
self.df: Optional[pd.DataFrame] = None
def load_data(self) -> 'DataPipeline':
"""加载数据,并进行类型优化"""
logger.info(f"Loading data from {self.raw_data_path}")
# 企业实践:指定 dtype 和 usecols 优化内存
dtypes = {
'user_id': 'int32',
'gender': 'category', # 低基数列转为 category
'age': 'int8',
}
self.df = pd.read_csv(
self.raw_data_path,
dtype=dtypes,
parse_dates=['signup_date'], # 自动解析日期
encoding='utf-8-sig' # 处理带 BOM 的 CSV
)
logger.info(f"Loaded {len(self.df)} rows, {len(self.df.columns)} columns")
return self # 支持链式调用
def clean_data(self) -> 'DataPipeline':
"""清洗数据:处理缺失值、重复值、异常值"""
if self.df is None:
raise ValueError("Data not loaded. Call load_data() first.")
logger.info("Cleaning data...")
# 1. 删除完全重复的行
initial_len = len(self.df)
self.df.drop_duplicates(inplace=True)
logger.info(f"Removed {initial_len - len(self.df)} duplicate rows")
# 2. 处理缺失值:数值列用中位数,类别列用众数
for col in self.df.select_dtypes(include=['number']).columns:
self.df[col].fillna(self.df[col].median(), inplace=True)
for col in self.df.select_dtypes(include=['category', 'object']).columns:
if not self.df[col].mode().empty:
self.df[col].fillna(self.df[col].mode()[0], inplace=True)
# 3. 异常值检测 (IQR方法)
for col in self.df.select_dtypes(include=['number']).columns:
Q1 = self.df[col].quantile(0.25)
Q3 = self.df[col].quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - 1.5 * IQR
upper = Q3 + 1.5 * IQR
# 将异常值截断到边界(或标记,这里选择截断)
self.df[col] = self.df[col].clip(lower=lower, upper=upper)
return self
def generate_report(self) -> pd.DataFrame:
"""生成聚合报告"""
if self.df is None:
raise ValueError("Data not loaded. Call load_data() first.")
logger.info("Generating report...")
# 使用 category 类型可加速 groupby
report = self.df.groupby('gender').agg({
'age': ['mean', 'std', 'count'],
'user_id': 'count'
})
report.columns = ['_'.join(col).strip() for col in report.columns.values]
return report
# 使用示例
if __name__ == "__main__":
pipeline = DataPipeline(Path("./data/raw/users.csv"))
# 链式调用,清晰直观
report = pipeline.load_data().clean_data().generate_report()
print(report)
# 保存报告
report.to_csv("./data/output/user_report.csv")
总结
| 模块 | 核心要点 | 企业级关键词 |
|---|---|---|
| 基础语法 | 类型注解、异常处理 | 可读性、健壮性 |
| 数据结构 | 列表、元组、字典、集合 | 选型、时间复杂度、内存优化 |
| 文件操作 | with 语句、逐行读取 |
pathlib、资源管理、大文件处理 |
| 异步IO | async/await、asyncio.gather |
信号量、超时、结构化并发 |
| Requests | Session、超时、异常处理 | 重试、安全令牌、可观测性 |
| NumPy | 向量化、广播、视图vs副本 | 内存布局、dtype、避免循环 |
| Pandas | DataFrame、groupby、链式操作 |
类型优化、数据管道、category |
学习建议:
- 循序渐进:确保基础语法和数据结构扎实,这是所有高级库的基石。
- 实践驱动:为每个模块编写小型项目,如用
asyncio+httpx写一个并发爬虫,用Pandas分析一份真实的公开数据集(如Kaggle数据)。 - 性能意识:在编写代码时,时刻思考时间复杂度和内存占用,尤其是在处理大规模数据时。
- 参考官方文档:上述库的官方文档是最权威、最详尽的学习资源(https://docs.python.org/zh-cn/3.14/)。