八、标准库核心模块(按功能分类)
阶段定位 :Python 的强大不仅在于语法,更在于丰富的标准库。”batteries included”意味着你不需要为常见任务去安装第三方包。本阶段按功能分类梳理核心标准库,帮你建立”标准库优先”的工程思维。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 ┌──────────────────────────────────────────────────────────────────┐ │ Python 标准库知识图谱 │ ├──────────────────────────────────────────────────────────────────┤ │ │ │ 文本处理 │ │ ├── re(正则表达式) │ │ ├── string(字符串常量) │ │ └── textwrap(文本格式化) │ │ │ │ 数据结构 │ │ ├── collections(namedtuple/deque/Counter/defaultdict等) │ │ ├── heapq(堆队列) │ │ └── bisect(二分查找) │ │ │ │ 函数式工具 │ │ ├── functools(partial/reduce/lru_cache/wraps) │ │ ├── itertools(无限迭代/组合生成/分组) │ │ └── operator(操作符函数化) │ │ │ │ 文件与路径 │ │ ├── os(操作系统接口) │ │ ├── os.path(路径操作) │ │ ├── pathlib(面向对象路径) │ │ └── shutil(高级文件操作) │ │ │ │ 网络与通信 │ │ ├── urllib(HTTP客户端) │ │ ├── socket(网络编程) │ │ └── http(HTTP协议) │ │ │ │ 并发与异步 │ │ ├── threading(线程) │ │ ├── multiprocessing(进程) │ │ ├── concurrent.futures(并发接口) │ │ └── asyncio(异步IO) │ │ │ │ 数据持久化 │ │ ├── json(JSON序列化) │ │ ├── pickle(对象序列化) │ │ └── sqlite3(SQLite数据库) │ │ │ │ 时间与日期 │ │ ├── datetime(日期时间处理) │ │ ├── time(时间基础) │ │ └── calendar(日历操作) │ │ │ │ 其他工具 │ │ ├── logging(日志) │ │ ├── argparse(命令行参数) │ │ ├── configparser(配置文件) │ │ └── hashlib(哈希算法) │ │ │ └──────────────────────────────────────────────────────────────────┘
1. 文本与字符串 1.1 re 正则表达式 正则表达式是处理文本的利器,用于模式匹配、查找、替换和分割。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 import retext = "Email: alice@example.com, Phone: 138-1234-5678" match = re.search(r"\w+@\w+\.\w+" , text)if match : print (match .group()) print (match .span()) emails = re.findall(r"\w+@\w+\.\w+" , text) phones = re.findall(r"\d{3}-\d{4}-\d{4}" , text) for match in re.finditer(r"\d+" , text): print (f"数字: {match .group()} , 位置: {match .span()} " ) redacted = re.sub(r"\w+@\w+\.\w+" , "[EMAIL]" , text) parts = re.split(r"[,;]\s*" , "a, b; c, d" ) EMAIL_PATTERN = re.compile (r"^[\w.-]+@[\w.-]+\.\w+$" ) print (EMAIL_PATTERN.match ("test@example.com" )) print (EMAIL_PATTERN.match ("invalid" )) pattern = re.compile (r"(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})" ) match = pattern.match ("2024-07-20" )print (match .groupdict())
常用正则模式速查 :
模式
含义
.
任意字符(除换行)
\d
数字 [0-9]
\w
单词字符 [a-zA-Z0-9_]
\s
空白字符
*
0 次或多次
+
1 次或多次
?
0 次或 1 次
{n,m}
n 到 m 次
^ / $
字符串开头/结尾
()
捕获组
(?:...)
非捕获组
(?P<name>...)
命名捕获组
\b
单词边界
正则表达式工作流程 :
1 2 3 4 5 6 文本输入 → 正则模式 → 匹配引擎 → 匹配结果/None │ │ │ │ └→ 编译正则 │ │ │ └───────────────────────┘ 重复使用
工程实践建议 :
复杂正则使用 re.compile() 编译后复用,提升性能
使用命名捕获组 (?P<name>...) 让代码更易读
正则表达式调试困难,尽量保持简洁
对于简单模式,优先使用字符串方法(str.startswith(), str.find() 等)
1.2 string 与 textwrap 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import stringimport textwrapprint (string.ascii_letters) print (string.digits) print (string.punctuation) long_text = "这是一个非常长的段落,需要自动换行和缩进处理。" wrapped = textwrap.fill(long_text, width=20 ) print (wrapped)indented = textwrap.indent(long_text, " " ) print (indented)dedented = textwrap.dedent(""" 第一行 第二行 第三行 """ )truncated = textwrap.shorten("这是一段很长的文本需要截断显示。" , width=20 )
应用场景 :
string.digits + string.ascii_letters:生成验证码
textwrap.fill():格式化日志输出
textwrap.dedent():去除多行字符串的公共缩进
2. 数据结构增强 2.1 collections collections 模块提供了高性能的容器类型,是对内置数据结构的扩展。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 from collections import namedtuple, deque, Counter, OrderedDict, defaultdict, ChainMapPoint = namedtuple("Point" , ["x" , "y" ]) p = Point(3 , 4 ) print (p.x, p.y) print (p._asdict()) dq = deque([1 , 2 , 3 ]) dq.append(4 ) dq.appendleft(0 ) dq.pop() dq.popleft() dq.rotate(1 ) c = Counter(["a" , "b" , "a" , "c" , "a" , "b" ]) print (c) print (c.most_common(2 )) char_count = Counter("mississippi" ) print (char_count.most_common(3 ))word_groups = defaultdict(list ) for word in ["apple" , "apricot" , "banana" , "blueberry" ]: word_groups[word[0 ]].append(word) print (dict (word_groups)) od = OrderedDict([("a" , 1 ), ("b" , 2 )]) od.move_to_end("a" ) defaults = {"theme" : "default" , "language" : "en" } user_prefs = {"theme" : "dark" } combined = ChainMap(user_prefs, defaults) print (combined["theme" ]) print (combined["language" ])
collections 各类型对比 :
类型
特点
适用场景
namedtuple
不可变,字段可通过属性访问
轻量级数据结构、记录
deque
双端 O(1) 操作
队列、栈、滑动窗口
Counter
自动计数
词频统计、投票计数
defaultdict
访问不存在键自动创建默认值
分组聚合
OrderedDict
保持插入顺序,支持移动
LRU 缓存、有序配置
ChainMap
链式查找,不复制
配置优先级、多字典合并
2.2 heapq 堆队列 堆是一种特殊的树形数据结构,最小堆的堆顶元素始终是最小的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import heapqnums = [5 , 2 , 8 , 1 , 9 , 3 ] heapq.heapify(nums) print (heapq.heappop(nums)) heapq.heappush(nums, 0 ) largest = heapq.nlargest(3 , [5 , 2 , 8 , 1 , 9 , 3 ]) smallest = heapq.nsmallest(3 , [5 , 2 , 8 , 1 , 9 , 3 ]) class PriorityQueue : def __init__ (self ): self ._queue = [] self ._index = 0 def push (self, item, priority ): heapq.heappush(self ._queue, (priority, self ._index, item)) self ._index += 1 def pop (self ): return heapq.heappop(self ._queue)[-1 ] pq = PriorityQueue() pq.push("task1" , 2 ) pq.push("task2" , 1 ) pq.push("task3" , 1 ) print (pq.pop()) print (pq.pop())
堆操作时间复杂度 :
操作
时间复杂度
heapify()
O(n)
heappush()
O(log n)
heappop()
O(log n)
nlargest(n)
O(n log n)
nsmallest(n)
O(n log n)
应用场景 :
任务调度(优先队列)
Top-K 问题(获取最大/最小的 N 个元素)
数据流中位数
2.3 bisect 二分查找 bisect 模块提供了在有序列表中进行二分查找和插入的功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import bisectsorted_list = [1 , 3 , 5 , 7 , 9 ] pos = bisect.bisect_left(sorted_list, 6 ) pos = bisect.bisect_right(sorted_list, 5 ) bisect.insort(sorted_list, 6 ) idx = bisect.bisect_left(sorted_list, 7 ) if idx < len (sorted_list) and sorted_list[idx] == 7 : print ("找到 7" ) def binary_search (arr, target ): left, right = 0 , len (arr) - 1 while left <= right: mid = (left + right) // 2 if arr[mid] == target: return mid elif arr[mid] < target: left = mid + 1 else : right = mid - 1 return -1
bisect 函数对比 :
函数
行为
返回值含义
bisect_left()
查找第一个大于等于 target 的位置
插入位置(左侧)
bisect_right()
查找第一个大于 target 的位置
插入位置(右侧)
insort_left()
在 bisect_left 位置插入
无
insort_right()
在 bisect_right 位置插入
无
应用场景 :
3. 函数式工具 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 from functools import partial, reduce, lru_cache, wraps, cmp_to_key, total_orderingbase_two = partial(int , base=2 ) print (base_two("1010" )) @lru_cache(maxsize=128 ) def fibonacci (n ): if n < 2 : return n return fibonacci(n - 1 ) + fibonacci(n - 2 ) @total_ordering class Version : def __init__ (self, major, minor, patch ): self .version = (major, minor, patch) def __eq__ (self, other ): return self .version == other.version def __lt__ (self, other ): return self .version < other.version def compare_length (a, b ): return len (a) - len (b) words = ["apple" , "pie" , "banana" ] words.sort(key=cmp_to_key(compare_length)) def my_decorator (func ): @wraps(func ) def wrapper (*args, **kwargs ): print ("Before" ) result = func(*args, **kwargs) print ("After" ) return result return wrapper
functools 常用工具对比 :
工具
作用
场景
partial
固定函数部分参数
简化函数调用
reduce
累积操作
归约计算(求和、乘积等)
lru_cache
函数结果缓存
避免重复计算
wraps
保留函数元信息
装饰器中使用
cmp_to_key
比较函数转 key
自定义排序
total_ordering
自动生成比较方法
类定义
itertools 提供了高效的循环工具,用于创建各种迭代器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 import itertoolscount = itertools.count(start=10 , step=2 ) cycle = itertools.cycle(["A" , "B" , "C" ]) repeat = itertools.repeat("X" , 5 ) items = ["A" , "B" , "C" ] print (list (itertools.permutations(items, 2 ))) print (list (itertools.combinations(items, 2 ))) print (list (itertools.combinations_with_replacement(items, 2 )))cumulative = list (itertools.accumulate([1 , 2 , 3 , 4 , 5 ])) data = [("A" , 1 ), ("A" , 2 ), ("B" , 3 ), ("B" , 4 )] sorted_data = sorted (data, key=lambda x: x[0 ]) for key, group in itertools.groupby(sorted_data, key=lambda x: x[0 ]): print (key, list (group)) combined = itertools.chain([1 , 2 ], [3 , 4 ], [5 , 6 ]) print (list (combined)) names = ["Alice" , "Bob" ] ages = [30 , 25 , 35 ] for name, age in itertools.zip_longest(names, ages, fillvalue="N/A" ): print (name, age) nums = iter (range (10 )) sliced = itertools.islice(nums, 2 , 8 , 2 ) it1, it2 = itertools.tee(range (5 )) print (list (it1)) print (list (it2))
itertools 分类速查 :
类别
函数
功能
无限迭代
count, cycle, repeat
生成无限序列
终止迭代
accumulate, chain, islice, tee
处理有限序列
组合生成
permutations, combinations, product
排列组合
分组筛选
groupby, filterfalse, compress
分组和过滤
3.3 operator operator 模块提供了Python内置操作符的函数化版本。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 from operator import itemgetter, attrgetter, methodcaller, add, mul, lt, eqdata = [{"name" : "Alice" , "age" : 30 }, {"name" : "Bob" , "age" : 25 }] sorted_data = sorted (data, key=itemgetter("age" )) sorted_by_age_name = sorted (data, key=itemgetter("age" , "name" )) get_name = itemgetter("name" ) print (list (map (get_name, data))) class Person : def __init__ (self, name, age ): self .name = name self .age = age people = [Person("Alice" , 30 ), Person("Bob" , 25 )] sorted_people = sorted (people, key=attrgetter("age" )) upper = methodcaller("upper" ) print (upper("hello" )) replace = methodcaller("replace" , "l" , "x" ) print (replace("hello" )) print (add(3 , 5 )) print (mul(3 , 5 )) print (lt(3 , 5 ))
operator 常用函数 :
类别
函数
对应操作
算术
add, sub, mul, truediv, floordiv
+, -, *, /, //
比较
eq, ne, lt, le, gt, ge
==, !=, <, <=, >, >=
逻辑
not_, truth, is_, is_not
not, bool(), is, is not
序列
itemgetter, attrgetter, methodcaller
索引、属性、方法调用
4. 文件与路径 4.1 os 模块 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import osprint (os.getcwd()) os.chdir("/path/to/dir" ) os.makedirs("dir/subdir" , exist_ok=True ) os.rmdir("empty_dir" ) os.rename("old.txt" , "new.txt" ) os.remove("file.txt" ) os.path.join("dir" , "file.txt" ) os.path.abspath("relative/path" ) os.path.exists("path/to/file" ) os.path.isfile("file.txt" ) os.path.isdir("dir" ) os.path.getsize("file.txt" ) print (os.environ.get("PATH" ))os.environ["MY_VAR" ] = "value" result = os.system("ls -l" ) for root, dirs, files in os.walk("directory" ): for file in files: print (os.path.join(root, file))
4.2 pathlib(面向对象路径) Python 3.4+ 引入的面向对象路径处理方式,比 os.path 更优雅。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 from pathlib import Pathp = Path("docs" ) / "guide" / "readme.txt" print (p) print (p.resolve()) print (p.exists()) print (p.is_file()) print (p.is_dir()) print (p.name) print (p.stem) print (p.suffix) print (p.parent) print (p.parents[0 ]) p.mkdir(parents=True , exist_ok=True ) content = p.read_text(encoding="utf-8" ) p.write_text("Hello, World!" , encoding="utf-8" ) for item in Path("." ).glob("**/*.py" ): print (item) p1 = Path("docs/readme.txt" ) p2 = Path("docs" ) / "readme.txt" print (p1 == p2)
pathlib vs os.path :
操作
os.path
pathlib
连接路径
os.path.join(a, b)
a / b
绝对路径
os.path.abspath(p)
p.resolve()
判断存在
os.path.exists(p)
p.exists()
读取文本
open(p).read()
p.read_text()
写入文本
open(p, 'w').write()
p.write_text()
4.3 shutil(高级文件操作) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import shutilshutil.copy("src.txt" , "dst.txt" ) shutil.copy2("src.txt" , "dst.txt" ) shutil.copytree("src_dir" , "dst_dir" , dirs_exist_ok=True ) shutil.move("src" , "dst" ) shutil.rmtree("dir_to_delete" ) shutil.make_archive("archive" , "zip" , "source_dir" ) shutil.unpack_archive("archive.zip" , "target_dir" ) disk_usage = shutil.disk_usage("/" ) print (f"总空间: {disk_usage.total / 1e9 :.2 f} GB" )print (f"已使用: {disk_usage.used / 1e9 :.2 f} GB" )print (f"可用空间: {disk_usage.free / 1e9 :.2 f} GB" )
5. 网络与通信 5.1 urllib(HTTP 客户端) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 from urllib import request, parse, errorresponse = request.urlopen("https://api.example.com/data" ) data = response.read().decode("utf-8" ) data = {"key" : "value" } encoded_data = parse.urlencode(data).encode("utf-8" ) response = request.urlopen("https://api.example.com/submit" , data=encoded_data) headers = {"User-Agent" : "Mozilla/5.0" } req = request.Request("https://example.com" , headers=headers) response = request.urlopen(req) try : response = request.urlopen("https://nonexistent.example.com" ) except error.HTTPError as e: print (f"HTTP Error: {e.code} " ) except error.URLError as e: print (f"URL Error: {e.reason} " ) request.urlretrieve("https://example.com/image.jpg" , "local_image.jpg" )
5.2 http.server(内置 Web 服务器) 1 2 3 4 5 6 7 8 9 10 11 12 import http.serverimport socketserverPORT = 8000 Handler = http.server.SimpleHTTPRequestHandler with socketserver.TCPServer(("" , PORT), Handler) as httpd: print (f"Serving at port {PORT} " ) httpd.serve_forever()
6. 并发与异步 6.1 threading(线程) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 import threadingimport timedef worker (name, delay ): for i in range (5 ): print (f"{name} : {i} " ) time.sleep(delay) t1 = threading.Thread(target=worker, args=("Thread-1" , 0.5 )) t2 = threading.Thread(target=worker, args=("Thread-2" , 0.5 )) t1.start() t2.start() t1.join() t2.join() lock = threading.Lock() shared_counter = 0 def increment (): global shared_counter with lock: shared_counter += 1 local_data = threading.local() local_data.user_id = "123"
6.2 multiprocessing(进程) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import multiprocessingdef worker (num ): return num * num with multiprocessing.Pool(processes=4 ) as pool: results = pool.map (worker, [1 , 2 , 3 , 4 , 5 ]) print (results) def producer (queue ): for item in ["A" , "B" , "C" ]: queue.put(item) def consumer (queue ): while True : item = queue.get() if item is None : break print (f"Consumed: {item} " ) if __name__ == "__main__" : queue = multiprocessing.Queue() p1 = multiprocessing.Process(target=producer, args=(queue,)) p2 = multiprocessing.Process(target=consumer, args=(queue,)) p1.start() p2.start() p1.join() queue.put(None ) p2.join()
6.3 concurrent.futures(高级并发接口) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutorwith ThreadPoolExecutor(max_workers=4 ) as executor: future = executor.submit(worker, "task1" ) result = future.result() results = list (executor.map (worker, ["task1" , "task2" , "task3" ])) with ProcessPoolExecutor(max_workers=4 ) as executor: results = executor.map (intensive_computation, data) futures = [executor.submit(task, arg) for arg in args] for future in concurrent.futures.as_completed(futures): result = future.result()
7. 数据持久化 7.1 json(JSON 序列化) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import jsondata = {"name" : "Alice" , "age" : 30 , "hobbies" : ["reading" , "coding" ]} json_str = json.dumps(data, indent=2 , ensure_ascii=False ) with open ("data.json" , "w" , encoding="utf-8" ) as f: json.dump(data, f, indent=2 , ensure_ascii=False ) json_str = '{"name": "Alice", "age": 30}' data = json.loads(json_str) with open ("data.json" , "r" , encoding="utf-8" ) as f: data = json.load(f) class PersonEncoder (json.JSONEncoder): def default (self, obj ): if isinstance (obj, Person): return {"name" : obj.name, "age" : obj.age} return super ().default(obj) json.dumps(person, cls=PersonEncoder)
7.2 pickle(对象序列化) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import pickledata = {"name" : "Alice" , "age" : 30 } pickled = pickle.dumps(data) with open ("data.pkl" , "wb" ) as f: pickle.dump(data, f) data = pickle.loads(pickled) with open ("data.pkl" , "rb" ) as f: data = pickle.load(f)
JSON vs Pickle :
特性
JSON
Pickle
可读性
人类可读
二进制格式
兼容性
跨语言
仅 Python
支持类型
基础类型
几乎所有 Python 对象
安全性
安全
有安全风险(不要加载不可信数据)
文件大小
较大
较小
7.3 sqlite3(轻量级数据库) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 import sqlite3conn = sqlite3.connect("example.db" ) cursor = conn.cursor() cursor.execute(""" CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, age INTEGER ) """ )cursor.execute("INSERT INTO users (name, age) VALUES (?, ?)" , ("Alice" , 30 )) conn.commit() cursor.execute("SELECT * FROM users" ) rows = cursor.fetchall() for row in rows: print (row) cursor.execute("SELECT * FROM users WHERE age > ?" , (25 ,)) with sqlite3.connect("example.db" ) as conn: cursor = conn.cursor() cursor.execute("SELECT * FROM users" ) conn.close()
8. 时间与日期 8.1 time 模块 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import timenow = time.time() print (now) print (time.strftime("%Y-%m-%d %H:%M:%S" )) print (time.strftime("%Y-%m-%d %H:%M:%S" , time.localtime()))parsed = time.strptime("2024-07-20" , "%Y-%m-%d" ) print (parsed) print ("开始" )time.sleep(1 ) print ("1秒后" )start = time.perf_counter() elapsed = time.perf_counter() - start
8.2 datetime 模块(最常用) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 from datetime import datetime, date, time, timedelta, timezonefrom zoneinfo import ZoneInfo now = datetime.now() utc_now = datetime.now(timezone.utc) dt = datetime(2024 , 7 , 20 , 10 , 30 , 0 ) print (dt.strftime("%Y-%m-%d %H:%M:%S" ))print (dt.isoformat()) parsed = datetime.fromisoformat("2024-07-20T10:30:00" ) future = dt + timedelta(days=7 , hours=3 ) diff = future - dt print (diff.days) nyc = ZoneInfo("America/New_York" ) shanghai = ZoneInfo("Asia/Shanghai" ) dt_shanghai = datetime(2024 , 7 , 20 , 10 , 30 , tzinfo=shanghai) dt_nyc = dt_shanghai.astimezone(nyc) print (f"上海: {dt_shanghai} " )print (f"纽约: {dt_nyc} " )timestamp = dt.timestamp() print (datetime.fromtimestamp(timestamp))
strftime 格式代码 :
代码
含义
示例
%Y
4 位年份
2024
%m
2 位月份
07
%d
2 位日期
20
%H
24 小时制
15
%M
分钟
30
%S
秒
00
%A
星期全称
Saturday
%a
星期缩写
Sat
8.3 calendar 1 2 3 4 5 6 7 8 9 10 11 import calendarprint (calendar.month(2024 , 7 ))print (calendar.isleap(2024 )) first_weekday, days_in_month = calendar.monthrange(2024 , 7 ) print (f"7月有{days_in_month} 天,第一天是星期{first_weekday} " )
9. 数学与随机 9.1 math 与 cmath 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import mathprint (math.sqrt(16 )) print (math.pow (2 , 10 )) print (math.log(100 , 10 )) print (math.log2(1024 )) print (math.exp(1 )) print (math.factorial(5 )) print (math.gcd(48 , 18 )) print (math.pi, math.e) print (math.floor(3.7 )) print (math.ceil(3.2 )) print (math.trunc(3.9 )) print (math.sin(math.pi / 2 )) print (math.degrees(math.pi)) print (math.sinh(1 )) print (math.erf(1 )) print (math.gamma(5 ))
9.2 random 与 secrets 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import randomimport secretsprint (random.random()) print (random.randint(1 , 100 )) print (random.choice(["A" , "B" , "C" ]))print (random.choices(["A" , "B" , "C" ], k=10 , weights=[5 , 3 , 2 ]))print (random.shuffle([1 , 2 , 3 , 4 , 5 ])) print (random.sample([1 , 2 , 3 , 4 , 5 ], 3 )) token = secrets.token_hex(16 ) url_safe = secrets.token_urlsafe(16 ) secure_choice = secrets.choice(["A" , "B" , "C" ])
random vs secrets 对比 :
特性
random
secrets
随机性
伪随机(可复现)
密码学安全(不可预测)
用途
游戏、模拟、测试
Token、密码、验证码
可重复性
可通过 seed 复现
不可复现
9.3 statistics 与 decimal 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import statisticsfrom decimal import Decimal, getcontextdata = [1 , 2 , 2 , 3 , 4 , 5 , 5 , 5 ] print (statistics.mean(data)) print (statistics.median(data)) print (statistics.mode(data)) print (statistics.stdev(data)) print (statistics.quantiles(data)) getcontext().prec = 6 a = Decimal("0.1" ) b = Decimal("0.2" ) print (a + b) print (Decimal(0.1 )) print (Decimal("0.1" )) getcontext().prec = 20 result = Decimal("1" ) / Decimal("7" ) print (result)
浮点精度问题 :
1 2 3 4 5 6 print (0.1 + 0.2 ) from decimal import Decimalprint (Decimal("0.1" ) + Decimal("0.2" ))
10. 并发与并行 10.1 threading 与 GIL 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import threadingimport timedef worker (name, delay ): print (f"线程 {name} 开始" ) time.sleep(delay) print (f"线程 {name} 结束" ) threads = [] for i in range (3 ): t = threading.Thread(target=worker, args=(f"T{i} " , 1 )) t.start() threads.append(t) for t in threads: t.join() from threading import Lockcounter = 0 lock = Lock() def increment (): global counter for _ in range (100000 ): with lock: counter += 1 local = threading.local() local.user_id = "123"
GIL(全局解释器锁) :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ┌──────────────────────────────────────────────┐ │ Python 解释器 │ ├──────────────────────────────────────────────┤ │ │ │ ┌──────────────────────────────────────┐ │ │ │ GIL(全局解释器锁) │ │ │ └───────────────┬──────────────────────┘ │ │ │ │ │ ┌───────────────┴───────────────┐ │ │ │ │ │ │ │ Thread 1 │ Thread 2 │ │ │ │ Python代码 │ Python代码 │ │ │ │ │ │ │ │ └─────────────┴─────────────────┘ │ │ │ │ 同一时刻只有一个线程执行 Python 字节码 │ │ I/O 操作时会释放 GIL │ └──────────────────────────────────────────────┘
10.2 concurrent.futures 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor, as_completedimport requestswith ThreadPoolExecutor(max_workers=5 ) as executor: urls = ["http://example.com" ] * 10 results = executor.map (fetch_url, urls) futures = [executor.submit(fetch_url, url) for url in urls] for future in as_completed(futures): try : result = future.result() print (result) except Exception as e: print (f"错误: {e} " ) with ProcessPoolExecutor(max_workers=4 ) as executor: numbers = range (100 ) results = list (executor.map (is_prime, numbers)) def fetch_url (url ): return requests.get(url, timeout=5 ).status_code
10.3 multiprocessing 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from multiprocessing import Pool, Queue, Processdef square (n ): return n * n if __name__ == "__main__" : with Pool(4 ) as p: results = p.map (square, range (10 )) print (results) from multiprocessing import Managerwith Manager() as manager: shared_dict = manager.dict () shared_list = manager.list ()
10.4 asyncio 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import asyncioasync def fetch (url ): await asyncio.sleep(0.1 ) return f"Data from {url} " async def main (): urls = ["url1" , "url2" , "url3" ] results = await asyncio.gather( *(fetch(url) for url in urls) ) tasks = [asyncio.create_task(fetch(url)) for url in urls] for task in asyncio.as_completed(tasks): result = await task print (result)
并发模型选择 :
场景
推荐方案
原因
I/O 密集型(网络请求)
asyncio
单线程高并发,内存开销低
I/O 密集型(简单)
ThreadPoolExecutor
代码简单,可调用同步库
CPU 密集型
ProcessPoolExecutor
绕过 GIL,利用多核
大量 CPU 计算
multiprocessing
更灵活的进程控制
11. 网络与通信 11.1 socket 基础 1 2 3 4 5 6 7 8 9 10 11 12 13 import socketwith socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.connect(("example.com" , 80 )) s.sendall(b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n" ) data = s.recv(4096 ) print (data.decode()) with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s: s.sendto(b"Hello" , ("localhost" , 9999 )) data, addr = s.recvfrom(1024 )
11.2 http.client 与 urllib 1 2 3 4 5 6 7 8 9 10 11 import urllib.requestimport jsonreq = urllib.request.Request( "https://api.example.com/data" , headers={"Accept" : "application/json" } ) with urllib.request.urlopen(req, timeout=10 ) as response: data = json.loads(response.read()) print (data)
11.3 email 模块 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from email.mime.text import MIMETextfrom email.mime.multipart import MIMEMultipartmsg = MIMEMultipart() msg["From" ] = "sender@example.com" msg["To" ] = "recipient@example.com" msg["Subject" ] = "测试邮件" body = MIMEText("这是邮件正文" , "plain" , "utf-8" ) msg.attach(body)
12. 开发支持 12.1 unittest 测试框架 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import unittestfrom unittest.mock import Mock, patch, MagicMockclass TestStringMethods (unittest.TestCase): def setUp (self ): """每个测试方法前执行。""" self .sample = "hello world" def tearDown (self ): """每个测试方法后执行。""" pass def test_upper (self ): self .assertEqual("foo" .upper(), "FOO" ) def test_isupper (self ): self .assertTrue("FOO" .isupper()) self .assertFalse("Foo" .isupper()) def test_split (self ): s = "hello world" self .assertEqual(s.split(), ["hello" , "world" ]) with self .assertRaises(TypeError): s.split(2 ) @patch("module.ClassName.method" ) def test_with_mock (self, mock_method ): mock_method.return_value = "mocked" result = module.ClassName().method() self .assertEqual(result, "mocked" )
12.2 doctest 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 def factorial (n ): """计算阶乘。 >>> factorial(5) 120 >>> factorial(0) 1 >>> factorial(-1) Traceback (most recent call last): ... ValueError: n must be >= 0 """ if n < 0 : raise ValueError("n must be >= 0" ) if n <= 1 : return 1 return n * factorial(n - 1 )
12.3 timeit 与性能分析 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import timeitelapsed = timeit.timeit("sum(range(1000))" , number=10000 ) print (f"耗时: {elapsed:.4 f} s" )import cProfileimport pstatsdef slow_function (): return sum (i ** 2 for i in range (100000 )) profiler = cProfile.Profile() profiler.enable() slow_function() profiler.disable() stats = pstats.Stats(profiler) stats.sort_stats("cumulative" ) stats.print_stats(10 )
12.4 enum 枚举 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 from enum import Enum, auto, IntEnumclass Color (Enum ): RED = 1 GREEN = 2 BLUE = 3 print (Color.RED) print (Color.RED.name) print (Color.RED.value) print (Color(1 )) class Status (Enum ): PENDING = auto() RUNNING = auto() COMPLETED = auto() class Priority (IntEnum ): LOW = 1 MEDIUM = 2 HIGH = 3 print (Priority.HIGH > Priority.LOW) print (Color.RED == Color.GREEN) print (Color.RED != Color.GREEN)
13. 其他实用模块 13.1 base64 编码 1 2 3 4 5 6 7 8 9 10 11 import base64encoded = base64.b64encode(b"hello" ).decode() decoded = base64.b64decode(encoded) url_safe = base64.urlsafe_b64encode(b"hello?world" ).decode() with open ("image.png" , "rb" ) as f: encoded_img = base64.b64encode(f.read()).decode()
13.2 inspect 内省 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import inspectdef example (a: int , b: str = "default" ) -> bool : """示例函数。""" return True sig = inspect.signature(example) for name, param in sig.parameters.items(): print (f"{name} : {param.annotation} , default={param.default} " ) print (inspect.getsource(example))print (inspect.stack())print (inspect.isfunction(example)) print (inspect.ismethod(example)) class MyClass : def method1 (self ): pass def method2 (self ): pass methods = inspect.getmembers(MyClass, predicate=inspect.isfunction) print ([name for name, _ in methods])
13.3 weakref:弱引用 弱引用不增加对象的引用计数,用于解决循环引用问题,以及实现缓存、观察者等场景。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 import weakrefclass Node : def __init__ (self, value ): self .value = value self ._parent = None self ._children = [] @property def parent (self ): """使用弱引用避免循环引用。""" return self ._parent() if self ._parent else None @parent.setter def parent (self, node ): self ._parent = weakref.ref(node) if node else None def add_child (self, child ): self ._children.append(child) child.parent = self def on_delete (obj ): print (f"对象被回收: {obj} " ) data = {"key" : "value" } ref = weakref.ref(data, on_delete) print (ref()) del data print (ref()) cache = weakref.WeakValueDictionary() obj = Node("temp" ) cache["temp" ] = obj print (len (cache)) del objprint (len (cache))
13.4 copy 模块深度解析 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 import copyclass Container : def __init__ (self, items ): self .items = items original = Container([[1 , 2 ], [3 , 4 ]]) shallow = copy.copy(original) print (shallow.items is original.items) shallow.items[0 ].append(99 ) print (original.items) deep = copy.deepcopy(original) print (deep.items is original.items) deep.items[0 ].append(88 ) print (original.items) class Custom : def __init__ (self, value ): self .value = value def __copy__ (self ): """浅拷贝自定义。""" return Custom(self .value) def __deepcopy__ (self, memo ): """深拷贝自定义。注意 memo 参数用于处理循环引用。""" return Custom(copy.deepcopy(self .value, memo))
14. 工作实战场景 14.1 数据处理管道 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 import itertoolsfrom collections import Counterclass DataPipeline : """数据处理管道:流式处理大数据。""" def __init__ (self, data_iter ): self .data_iter = data_iter def filter (self, predicate ): """过滤数据。""" return DataPipeline(filter (predicate, self .data_iter)) def map (self, func ): """转换数据。""" return DataPipeline(map (func, self .data_iter)) def group_by (self, key_func ): """按键分组。""" sorted_data = sorted (self .data_iter, key=key_func) groups = [] for key, group in itertools.groupby(sorted_data, key=key_func): groups.append((key, list (group))) return groups def count_by (self, key_func ): """按键计数。""" counter = Counter(key_func(item) for item in self .data_iter) return counter def collect (self ): """收集结果。""" return list (self .data_iter) data = [ {"name" : "Alice" , "category" : "A" , "value" : 10 }, {"name" : "Bob" , "category" : "B" , "value" : 20 }, {"name" : "Charlie" , "category" : "A" , "value" : 30 }, ] pipeline = DataPipeline(data) result = pipeline \ .filter (lambda x: x["value" ] > 15 ) \ .group_by(lambda x: x["category" ]) print (result)
14.2 高性能缓存系统 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 from functools import lru_cacheimport timefrom threading import Lockclass CacheManager : """缓存管理器:支持多种缓存策略。""" def __init__ (self ): self ._caches = {} self ._lock = Lock() def get_cache (self, name, maxsize=128 ): """获取或创建缓存。""" with self ._lock: if name not in self ._caches: def decorator (func ): @lru_cache(maxsize=maxsize ) def wrapper (*args, **kwargs ): return func(*args, **kwargs) return wrapper self ._caches[name] = decorator return self ._caches[name] def clear_cache (self, name ): """清空指定缓存。""" with self ._lock: if name in self ._caches: pass cache_manager = CacheManager() @cache_manager.get_cache("user_cache" , maxsize=100 ) def get_user (user_id ): """模拟数据库查询。""" time.sleep(0.1 ) return {"id" : user_id, "name" : f"User {user_id} " } start = time.time() user = get_user(1 ) print (f"首次调用耗时: {time.time() - start:.3 f} s" )start = time.time() user = get_user(1 ) print (f"缓存命中耗时: {time.time() - start:.3 f} s" )
14.3 日志分析工具 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 import refrom collections import Counterfrom pathlib import Pathclass LogAnalyzer : """日志分析工具:统计访问频率、错误类型等。""" def __init__ (self, log_path ): self .log_path = Path(log_path) self ._pattern = re.compile ( r'(?P<ip>\d+\.\d+\.\d+\.\d+) - - \[(?P<time>.*?)\] ' r'"(?P<method>\w+) (?P<path>.*?) HTTP/1\.\d" ' r'(?P<status>\d+) (?P<size>\d+)' ) def parse_logs (self ): """解析日志文件,返回生成器。""" with open (self .log_path, "r" , encoding="utf-8" ) as f: for line in f: match = self ._pattern.match (line) if match : yield match .groupdict() def get_ip_stats (self ): """统计 IP 访问次数。""" counter = Counter(entry["ip" ] for entry in self .parse_logs()) return counter.most_common(10 ) def get_status_stats (self ): """统计状态码分布。""" counter = Counter(entry["status" ] for entry in self .parse_logs()) return dict (counter) def get_top_paths (self ): """统计访问最多的路径。""" counter = Counter(entry["path" ] for entry in self .parse_logs()) return counter.most_common(10 ) analyzer = LogAnalyzer("access.log" ) print ("Top IP:" , analyzer.get_ip_stats())print ("Status分布:" , analyzer.get_status_stats())print ("Top Paths:" , analyzer.get_top_paths())
14.4 并发任务调度器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 from concurrent.futures import ThreadPoolExecutor, as_completedimport timeclass TaskScheduler : """任务调度器:并发执行任务并收集结果。""" def __init__ (self, max_workers=5 ): self .executor = ThreadPoolExecutor(max_workers=max_workers) def submit (self, task_func, *args, **kwargs ): """提交单个任务。""" return self .executor.submit(task_func, *args, **kwargs) def run_all (self, tasks ): """并行执行所有任务。""" futures = [self .executor.submit(task) for task in tasks] results = [] errors = [] for future in as_completed(futures): try : results.append(future.result()) except Exception as e: errors.append(e) return results, errors def shutdown (self ): """关闭调度器。""" self .executor.shutdown(wait=True ) def fetch_data (url ): """模拟网络请求。""" time.sleep(0.5 ) return f"Data from {url} " scheduler = TaskScheduler(max_workers=3 ) tasks = [ lambda : fetch_data("http://api1.example.com" ), lambda : fetch_data("http://api2.example.com" ), lambda : fetch_data("http://api3.example.com" ), ] results, errors = scheduler.run_all(tasks) print ("Results:" , results)scheduler.shutdown()
15. 常见面试题汇总 15.1 基础概念题 Q1:collections 模块中 defaultdict 和普通 dict 的区别?
A :defaultdict 在访问不存在的键时会自动创建该键,并使用 default_factory 提供默认值。普通 dict 会抛出 KeyError。
Q2:itertools.chain 和 list.extend 的区别?
A :itertools.chain 返回一个迭代器,不立即合并列表,内存效率更高;list.extend 会立即将所有元素添加到列表中,占用更多内存。
Q3:heapq 实现的是最大堆还是最小堆?如何实现最大堆?
A :heapq 实现的是最小堆。要实现最大堆,可以将元素取负值后存储。
Q4:functools.lru_cache 的工作原理是什么?
A :lru_cache 使用最近最少使用算法缓存函数结果。当缓存满时,会淘汰最近最少使用的条目。它通过装饰器实现,自动将函数参数作为键缓存结果。
Q5:concurrent.futures.ThreadPoolExecutor 和 ProcessPoolExecutor 的区别?
A :
ThreadPoolExecutor:线程池,适用于 I/O 密集型任务,受 GIL 限制,同一时刻只有一个线程执行 Python 字节码
ProcessPoolExecutor:进程池,适用于 CPU 密集型任务,绕过 GIL,利用多核处理器
15.2 实战应用题 Q6:如何选择合适的并发模型?
A :
I/O 密集型(网络请求、文件读写):asyncio(单线程高并发)或 ThreadPoolExecutor(简单)
CPU 密集型(计算任务):ProcessPoolExecutor(绕过 GIL)
需要大量进程控制:multiprocessing
Q7:如何处理浮点数精度问题?
A :
使用 decimal 模块进行精确小数计算
避免直接比较浮点数,使用容差比较(abs(a - b) < 1e-9)
对于金融计算,使用 decimal 或整数(以分为单位)
Q8:如何实现高效的缓存系统?
A :
使用 functools.lru_cache 装饰器(简单场景)
实现自定义缓存类(复杂场景)
使用 weakref.WeakValueDictionary(自动回收缓存)
设置合理的缓存过期策略
Q9:如何进行性能分析?
A :
timeit:简单计时,比较代码片段
cProfile:函数级性能分析,找出耗时函数
line_profiler(第三方):行级性能分析
memory_profiler(第三方):内存使用分析
Q10:re.compile() 的作用是什么?何时使用?
A :re.compile() 用于编译正则表达式,生成正则表达式对象。对于频繁使用的正则表达式,编译后复用可以提升性能(避免重复编译)。
15.3 代码分析题 Q11:分析以下代码的问题:
1 2 3 4 5 6 7 import retext = "email: test@example.com" if re.match (r"\w+@\w+\.\w+" , text): print ("匹配成功" ) else : print ("匹配失败" )
A :re.match() 只从字符串开头匹配,而 email: test@example.com 开头是 “email:”,所以匹配失败。应使用 re.search()。
Q12:分析以下代码的问题:
1 2 3 4 5 6 7 8 from collections import defaultdictdata = [("A" , 1 ), ("B" , 2 ), ("A" , 3 )] groups = defaultdict(list ) for key, value in data: groups[key].append(value) print (dict (groups))
A :这段代码没有问题,是正确的分组方式。输出为 {'A': [1, 3], 'B': [2]}。
Q13:以下代码的输出是什么?
1 2 3 4 5 6 import heapqnums = [3 , 1 , 4 , 1 , 5 , 9 ] heapq.heapify(nums) print (heapq.heappop(nums))print (heapq.heappop(nums))
A :输出为 1 和 1。heapify 将列表转为最小堆,heappop 每次弹出最小元素。
16. 实战项目:日志分析系统 16.1 项目概述 项目目标 :开发一个命令行日志分析工具 loganalyzer,支持分析 Web 服务器日志。
功能需求 :
loganalyzer access <logfile> - 分析访问日志
loganalyzer errors <logfile> - 统计错误类型
loganalyzer top <logfile> - 显示 Top 访问 IP/路径
loganalyzer export <logfile> <output> - 导出分析结果
16.2 项目结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 loganalyzer/ ├── README.md ├── pyproject.toml ├── src/ │ └── loganalyzer/ │ ├── __init__.py │ ├── __main__.py │ ├── cli.py │ ├── analyzer.py │ ├── parsers/ │ │ ├── __init__.py │ │ └── nginx_parser.py │ └── exporters/ │ ├── __init__.py │ └── json_exporter.py └── tests/ ├── __init__.py └── test_analyzer.py
16.3 核心代码 pyproject.toml :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 [build-system] requires = ["setuptools>=61.0" , "wheel" ]build-backend = "setuptools.build_meta" [project] name = "loganalyzer" version = "1.0.0" description = "A log analysis tool" readme = "README.md" license = {text = "MIT" }authors = [ {name = "Your Name" , email = "you@example.com" } ] requires-python = ">=3.9" dependencies = [ "click>=8.0" , ] [project.scripts] loganalyzer = "loganalyzer.cli:main" [tool.setuptools.packages.find] where = ["src" ]
cli.py :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 import clickfrom loganalyzer.analyzer import LogAnalyzer@click.group() def main (): """LogAnalyzer - 日志分析工具""" pass @main.command("access" ) @click.argument("logfile" ) def analyze_access (logfile ): """分析访问日志。""" analyzer = LogAnalyzer(logfile) stats = analyzer.get_access_stats() click.echo(f"总访问次数: {stats['total' ]} " ) click.echo(f"平均响应大小: {stats['avg_size' ]:.2 f} bytes" ) @main.command("errors" ) @click.argument("logfile" ) def analyze_errors (logfile ): """统计错误类型。""" analyzer = LogAnalyzer(logfile) errors = analyzer.get_error_stats() for status, count in errors.items(): click.echo(f"{status} : {count} 次" ) @main.command("top" ) @click.argument("logfile" ) @click.option("-t" , "--type" , default="ip" , type =click.Choice(["ip" , "path" ] ) ) def show_top (logfile, type ): """显示 Top IP 或路径。""" analyzer = LogAnalyzer(logfile) if type == "ip" : top = analyzer.get_top_ips() else : top = analyzer.get_top_paths() click.echo(f"Top 10 {type } :" ) for item, count in top: click.echo(f" {item} : {count} " ) @main.command("export" ) @click.argument("logfile" ) @click.argument("output" ) def export_results (logfile, output ): """导出分析结果。""" analyzer = LogAnalyzer(logfile) analyzer.export(output) click.echo(f"结果已导出到: {output} " )
analyzer.py :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 from collections import Counterfrom loganalyzer.parsers.nginx_parser import NginxParserfrom loganalyzer.exporters.json_exporter import JsonExporterclass LogAnalyzer : """日志分析器。""" def __init__ (self, logfile ): self .logfile = logfile self .parser = NginxParser() self .exporter = JsonExporter() def get_entries (self ): """获取所有日志条目。""" return self .parser.parse(self .logfile) def get_access_stats (self ): """获取访问统计。""" entries = self .get_entries() total = len (entries) total_size = sum (int (e["size" ]) for e in entries) return { "total" : total, "avg_size" : total_size / total if total > 0 else 0 , } def get_error_stats (self ): """获取错误统计。""" entries = self .get_entries() errors = [e["status" ] for e in entries if e["status" ].startswith("4" ) or e["status" ].startswith("5" )] return dict (Counter(errors)) def get_top_ips (self ): """获取 Top IP。""" entries = self .get_entries() counter = Counter(e["ip" ] for e in entries) return counter.most_common(10 ) def get_top_paths (self ): """获取 Top 路径。""" entries = self .get_entries() counter = Counter(e["path" ] for e in entries) return counter.most_common(10 ) def export (self, output ): """导出分析结果。""" data = { "access_stats" : self .get_access_stats(), "error_stats" : self .get_error_stats(), "top_ips" : self .get_top_ips(), "top_paths" : self .get_top_paths(), } self .exporter.export(data, output)
parsers/nginx_parser.py :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import reclass NginxParser : """Nginx 日志解析器。""" def __init__ (self ): self ._pattern = re.compile ( r'(?P<ip>\d+\.\d+\.\d+\.\d+) - - \[(?P<time>.*?)\] ' r'"(?P<method>\w+) (?P<path>.*?) HTTP/1\.\d" ' r'(?P<status>\d+) (?P<size>\d+)' ) def parse (self, logfile ): """解析日志文件。""" entries = [] with open (logfile, "r" , encoding="utf-8" ) as f: for line in f: match = self ._pattern.match (line) if match : entries.append(match .groupdict()) return entries
exporters/json_exporter.py :
1 2 3 4 5 6 7 8 9 import jsonclass JsonExporter : """JSON 导出器。""" def export (self, data, output ): """导出数据到 JSON 文件。""" with open (output, "w" , encoding="utf-8" ) as f: json.dump(data, f, ensure_ascii=False , indent=2 )
16.4 项目运行 1 2 3 4 5 6 7 8 9 10 11 12 pip install -e . loganalyzer access access.log loganalyzer errors access.log loganalyzer top access.log --type ip loganalyzer top access.log --type path loganalyzer export access.log result.json python -m loganalyzer access access.log
16.5 项目亮点
模块化设计 :解析器和导出器独立,易于扩展
正则优化 :预编译正则表达式,提升性能
流式处理 :逐行读取日志,内存效率高
命令行友好 :使用 click 库,提供丰富的命令和选项
附录:第八阶段自检清单 在继续学习第九阶段之前,请确保你能:
工程师寄语 :标准库是 Python 最大的宝藏。在考虑安装第三方包之前,先问一句:”标准库里有吗?” 熟悉标准库不仅能减少依赖,还能提高代码的可维护性和可移植性。collections、itertools、functools 这三个模块特别值得反复研读。
性能提示 :对于频繁调用的正则表达式,务必使用 re.compile() 编译后复用。对于大数据量处理,优先使用 itertools 和生成器表达式,避免一次性加载所有数据到内存。