<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Alexander Lee</title><description>World Peace</description><link>https://lxy-alexander.github.io/</link><language>en</language><item><title>deduplicate</title><link>https://lxy-alexander.github.io/blog/posts/interview/anthropic/deduplicate/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/interview/anthropic/deduplicate/</guid><description>deduplicate</description><pubDate>Fri, 17 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;I. File Deduplication (文件去重)&lt;/h1&gt;
&lt;h2&gt;1. Problem Statement (题目描述)&lt;/h2&gt;
&lt;p&gt;==Given a Directory Tree (目录树), find and group files with identical Byte Content (字节内容) and output duplicate file paths where group size ≥ 2.==&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Task Description (任务说明):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Input: A root directory (根目录) containing files and subdirectories&lt;/li&gt;
&lt;li&gt;Definition: Duplicate Files (重复文件) have exactly the same byte content&lt;/li&gt;
&lt;li&gt;Output: Groups of file paths (文件路径组), each group contains identical files&lt;/li&gt;
&lt;li&gt;Format: One line per group, paths separated by spaces (空格分隔路径)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Example (示例):&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/a/1.txt content &quot;hello&quot;
/b/2.txt content &quot;hello&quot;
/c/3.txt content &quot;world&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/a/1.txt /b/2.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Core Approach (核心思路)&lt;/h2&gt;
&lt;h3&gt;1) Directory Traversal (目录遍历)&lt;/h3&gt;
&lt;p&gt;Use DFS/BFS (深度优先/广度优先搜索) to visit all files in the directory tree.  ==What should we do if the file cannot be opened?==&lt;/p&gt;
&lt;h3&gt;2) Hashing Files (文件哈希)&lt;/h3&gt;
&lt;p&gt;Use a Hash Function (哈希函数) to convert file content into a Hash Value (哈希值) for quick comparison.&lt;/p&gt;
&lt;h3&gt;3) Grouping Duplicates (分组重复文件)&lt;/h3&gt;
&lt;p&gt;Use a Hash Map (哈希表) to map &lt;code&gt;hash -&amp;gt; list of file paths&lt;/code&gt; and collect duplicates.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. Code Implementation (代码实现)&lt;/h2&gt;
&lt;h3&gt;1) Python Example (可独立运行)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import os
import hashlib

def get_file_hash(file_path, chunk_size=4096):
    hasher = hashlib.md5()  # MD5哈希函数
    with open(file_path, &apos;rb&apos;) as f:
        while True:
            chunk = f.read(chunk_size)
            if not chunk:
                break
            hasher.update(chunk)
    return hasher.hexdigest()

def find_duplicates(root_dir):
    hash_map = {}  # 哈希值 -&amp;gt; 文件路径列表

    for dirpath, _, filenames in os.walk(root_dir):
        for filename in filenames:
            file_path = os.path.join(dirpath, filename)
            file_hash = get_file_hash(file_path)

            if file_hash not in hash_map:
                hash_map[file_hash] = []
            hash_map[file_hash].append(file_path)

    for paths in hash_map.values():
        if len(paths) &amp;gt;= 2:
            print(&quot; &quot;.join(paths))

if __name__ == &quot;__main__&quot;:
    # Ensure example_dir exists with test files
    find_duplicates(&quot;./example_dir&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import os
import hashlib
from collections import defaultdict


def get_file_hash(file_path, chunk_size=4096):
    hasher = hashlib.md5()
    with open(file_path, &quot;rb&quot;) as f:
        while True:
            chunk = f.read(chunk_size)
            if not chunk:
                break
            hasher.update(chunk)
    return hasher.hexdigest()


def find_duplicates(root_dir):
    size_map = defaultdict(list)   # 文件大小 -&amp;gt; 文件路径列表
    hash_map = defaultdict(list)   # 文件哈希 -&amp;gt; 文件路径列表

    # 1）先遍历所有文件，按文件大小分组
    for dirpath, _, filenames in os.walk(root_dir):
        for filename in filenames:
            file_path = os.path.join(dirpath, filename)
            try:
                file_size = os.path.getsize(file_path)
                size_map[file_size].append(file_path)
            except (OSError, PermissionError) as e:
                print(f&quot;无法读取文件大小: {file_path}, 错误: {e}&quot;)

    # 2）只对“大小相同”的文件计算哈希
    for file_size, paths in size_map.items():
        if len(paths) &amp;lt; 2:
            continue

        for file_path in paths:
            try:
                file_hash = get_file_hash(file_path)
                hash_map[file_hash].append(file_path)
            except (OSError, PermissionError) as e:
                print(f&quot;无法读取文件内容: {file_path}, 错误: {e}&quot;)

    # 3）输出真正重复的文件
    found = False
    for paths in hash_map.values():
        if len(paths) &amp;gt;= 2:
            found = True
            print(&quot; &quot;.join(paths))

    if not found:
        print(&quot;没有找到重复文件&quot;)


if __name__ == &quot;__main__&quot;:
    find_duplicates(&quot;./example_dir&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import os
import hashlib
from collections import defaultdict
from concurrent.futures import ThreadPoolExecutor, as_completed


def get_file_hash(file_path, chunk_size=4096):
    hasher = hashlib.md5()
    with open(file_path, &quot;rb&quot;) as f:
        while True:
            chunk = f.read(chunk_size)
            if not chunk:
                break
            hasher.update(chunk)
    return hasher.hexdigest()


def hash_file_worker(file_path):
    try:
        file_hash = get_file_hash(file_path)
        return file_path, file_hash, None
    except (OSError, PermissionError) as e:
        return file_path, None, e


def find_duplicates(root_dir, max_workers=8):
    size_map = defaultdict(list)

    # 1）先按文件大小分组
    for dirpath, _, filenames in os.walk(root_dir):
        for filename in filenames:
            file_path = os.path.join(dirpath, filename)
            try:
                file_size = os.path.getsize(file_path)
                size_map[file_size].append(file_path)
            except (OSError, PermissionError) as e:
                print(f&quot;无法读取文件大小: {file_path}, 错误: {e}&quot;)

    duplicate_groups = []

    # 2）只对大小相同的文件组计算哈希
    for file_size, paths in size_map.items():
        if len(paths) &amp;lt; 2:
            continue

        hash_map = defaultdict(list)

        with ThreadPoolExecutor(max_workers=max_workers) as executor:
            futures = [executor.submit(hash_file_worker, file_path) for file_path in paths]

            for future in as_completed(futures):
                file_path, file_hash, error = future.result()
                if error is not None:
                    print(f&quot;无法读取文件内容: {file_path}, 错误: {error}&quot;)
                    continue
                hash_map[file_hash].append(file_path)

        # 3）收集真正重复的文件
        for same_files in hash_map.values():
            if len(same_files) &amp;gt;= 2:
                duplicate_groups.append(same_files)

    # 4）输出结果
    if duplicate_groups:
        print(&quot;找到重复文件：&quot;)
        for i, group in enumerate(duplicate_groups, 1):
            print(f&quot;\n第{i}组重复文件：&quot;)
            for path in group:
                print(path)
    else:
        print(&quot;没有找到重复文件&quot;)


if __name__ == &quot;__main__&quot;:
    find_duplicates(&quot;./example_dir&quot;, max_workers=8)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;4. Complexity Analysis (复杂度分析)&lt;/h2&gt;
&lt;h3&gt;1) Time Complexity (时间复杂度)&lt;/h3&gt;
&lt;p&gt;The Time Complexity (时间复杂度) is $$O(N \cdot S)$$ where N is number of files and S is average file size.&lt;/p&gt;
&lt;h3&gt;2) Space Complexity (空间复杂度)&lt;/h3&gt;
&lt;p&gt;The Space Complexity (空间复杂度) is $$O(N)$$ due to storing hash mappings.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;5. Optimization Strategies (优化策略)&lt;/h2&gt;
&lt;h3&gt;1) I/O Bound Optimization (I/O瓶颈优化)&lt;/h3&gt;
&lt;p&gt;Reduce Disk I/O (磁盘读写) by filtering files using File Size (文件大小) before hashing.&lt;/p&gt;
&lt;h3&gt;2) CPU Bound Optimization (CPU瓶颈优化)&lt;/h3&gt;
&lt;p&gt;Reduce Hash Computation (哈希计算) cost by using faster hash functions or parallel processing.&lt;/p&gt;
&lt;h1&gt;II. Detect Duplicate Files (文件去重-按大小+哈希)&lt;/h1&gt;
&lt;h2&gt;1. Problem Statement (题目描述)&lt;/h2&gt;
&lt;p&gt;==Given File Metadata (文件元数据) and a Content Reading Interface (文件读取接口), detect Duplicate Files (重复文件) where two files are duplicates iff their contents are identical (内容完全相同).==&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Requirements (要求):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Input: A list &lt;code&gt;files&lt;/code&gt;, each contains:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;path&lt;/code&gt; (路径)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;size&lt;/code&gt; (文件大小，字节)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Helper Functions (辅助函数):
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;read_file(path) -&amp;gt; bytes&lt;/code&gt; (读取文件内容)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;hash_bytes(data) -&amp;gt; str&lt;/code&gt; (计算哈希值)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Output: &lt;code&gt;List[List[str]]&lt;/code&gt;, each group contains duplicate file paths (每组至少2个文件)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Constraints (约束):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Number of Files (文件数量) up to $$10^6$$&lt;/li&gt;
&lt;li&gt;Files may be very large (大文件GB级)&lt;/li&gt;
&lt;li&gt;Need Streaming Processing (流式处理) to avoid loading entire file into memory&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Optimization Requirement (优化要求):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Stage 1: Group by File Size (按文件大小分组)&lt;/li&gt;
&lt;li&gt;Stage 2: For same size files, compute Content Hash (内容哈希)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Example (示例):&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Input:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[a.txt size=3 content=abc,
 b.txt size=3 content=abc,
 c.txt size=3 content=abd,
 d.txt size=10 content=0123456789]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Output:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[[a.txt, b.txt]]
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Core Idea (核心思路)&lt;/h2&gt;
&lt;h3&gt;1) Two-Stage Filtering (两阶段过滤)&lt;/h3&gt;
&lt;p&gt;First use File Size (文件大小) to prune candidates, then use Hash Function (哈希函数) to confirm duplicates.&lt;/p&gt;
&lt;h3&gt;2) Performance Insight (性能关键点)&lt;/h3&gt;
&lt;p&gt;This approach reduces expensive I/O (磁盘读取) and Hash Computation (哈希计算).&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. Algorithm Steps (算法步骤)&lt;/h2&gt;
&lt;h3&gt;1) Step Flow (步骤流程)&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Build Size Map (大小映射): &lt;code&gt;size -&amp;gt; list of paths&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Filter groups with size ≥ 2&lt;/li&gt;
&lt;li&gt;For each group, compute Hash (计算哈希)&lt;/li&gt;
&lt;li&gt;Build Hash Map (哈希映射): &lt;code&gt;hash -&amp;gt; list of paths&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Collect groups with size ≥ 2&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;4. Code Implementation (代码实现)&lt;/h2&gt;
&lt;h3&gt;1) Python Example (可独立运行)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import hashlib
from collections import defaultdict

# Mock read_file (模拟读取函数)
def read_file(path):
    data_map = {
        &quot;a.txt&quot;: b&quot;abc&quot;,
        &quot;b.txt&quot;: b&quot;abc&quot;,
        &quot;c.txt&quot;: b&quot;abd&quot;,
        &quot;d.txt&quot;: b&quot;0123456789&quot;
    }
    return data_map[path]

def hash_bytes(data):
    return hashlib.sha256(data).hexdigest()

def find_duplicates(files):
    size_map = defaultdict(list)

    # Stage 1: group by file size
    for f in files:
        size_map[f[&quot;size&quot;]].append(f[&quot;path&quot;])

    result = []

    # Stage 2: group by content hash
    for paths in size_map.values():
        if len(paths) &amp;lt; 2:
            continue

        hash_map = defaultdict(list)
        for path in paths:
            data = read_file(path)
            h = hash_bytes(data)
            hash_map[h].append(path)

        for group in hash_map.values():
            if len(group) &amp;gt;= 2:
                result.append(group)

    return result

if __name__ == &quot;__main__&quot;:
    files = [
        {&quot;path&quot;: &quot;a.txt&quot;, &quot;size&quot;: 3},
        {&quot;path&quot;: &quot;b.txt&quot;, &quot;size&quot;: 3},
        {&quot;path&quot;: &quot;c.txt&quot;, &quot;size&quot;: 3},
        {&quot;path&quot;: &quot;d.txt&quot;, &quot;size&quot;: 10},
    ]

    print(find_duplicates(files))
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;5. Complexity Analysis (复杂度分析)&lt;/h2&gt;
&lt;h3&gt;1) Time Complexity (时间复杂度)&lt;/h3&gt;
&lt;p&gt;The Time Complexity (时间复杂度) is $$O(N + K \cdot S)$$ where K is number of candidate files and S is file size.&lt;/p&gt;
&lt;h3&gt;2) Space Complexity (空间复杂度)&lt;/h3&gt;
&lt;p&gt;The Space Complexity (空间复杂度) is $$O(N)$$ for storing mappings.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;6. System Design Discussion (系统设计讨论)&lt;/h2&gt;
&lt;h3&gt;1) Large File Handling (大文件处理)&lt;/h3&gt;
&lt;p&gt;Use Streaming Hashing (流式哈希) to process files in chunks to avoid Memory Overflow (内存溢出).&lt;/p&gt;
&lt;h3&gt;2) I/O Bound Optimization (I/O瓶颈优化)&lt;/h3&gt;
&lt;p&gt;Use Concurrent I/O (并发I/O) and Batch Processing (批处理) to reduce disk latency.&lt;/p&gt;
&lt;h3&gt;3) CPU Bound Optimization (CPU瓶颈优化)&lt;/h3&gt;
&lt;p&gt;Use Parallel Hashing (并行哈希) with Multi-processing (多进程) to speed up computation.&lt;/p&gt;
&lt;h3&gt;4) Real-time Detection (实时检测)&lt;/h3&gt;
&lt;p&gt;Use File System Watcher (文件系统监听器) and Incremental Indexing (增量索引) to detect duplicates dynamically.&lt;/p&gt;
&lt;h1&gt;III. Find Duplicate Files by Content (按内容查找重复文件)&lt;/h1&gt;
&lt;h2&gt;1. Problem Statement (题目描述)&lt;/h2&gt;
&lt;p&gt;Given a Directory Structure (目录结构), find all files with duplicate content where file content can be compared by a Hash String (哈希字符串).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Requirements (要求):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Input: A list of strings &lt;code&gt;paths&lt;/code&gt;, each string contains:
&lt;ul&gt;
&lt;li&gt;Directory Path (目录路径)&lt;/li&gt;
&lt;li&gt;File Name (文件名)&lt;/li&gt;
&lt;li&gt;File Content (文件内容)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Output: &lt;code&gt;List[List[str]]&lt;/code&gt;, each group contains file paths with identical content (内容相同的文件路径分组)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Example (示例):&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Input:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[
    &quot;root/a 1.txt(abcd) 2.txt(efgh)&quot;,
    &quot;root/c 3.txt(abcd)&quot;,
    &quot;root/c/d 4.txt(efgh)&quot;,
    &quot;root 4.txt(1234)&quot;
]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Output:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[
    [&quot;root/a/1.txt&quot;, &quot;root/c/3.txt&quot;],
    [&quot;root/a/2.txt&quot;, &quot;root/c/d/4.txt&quot;]
]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Constraints (约束):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Each input string length (每个输入字符串长度) is less than $$300$$&lt;/li&gt;
&lt;li&gt;Number of files (文件数量) is less than $$10^4$$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Extra Example (额外示例):&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Input:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[&quot;root/a 1.txt(abcd) 2.txt(efgh)&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Output:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[]
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Core Idea (核心思路)&lt;/h2&gt;
&lt;h3&gt;1) Hash Map Grouping (哈希表分组)&lt;/h3&gt;
&lt;p&gt;Use a Hash Map (哈希表) to map Content (内容) to Full Paths (完整路径), because the same content should belong to the same group.&lt;/p&gt;
&lt;h3&gt;2) String Parsing (字符串解析)&lt;/h3&gt;
&lt;p&gt;Split each record into Directory (目录) and File Info (文件信息), then extract File Name (文件名) and Content (内容) from each file token.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. Algorithm Steps (算法步骤)&lt;/h2&gt;
&lt;h3&gt;1) Step Flow (步骤流程)&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Traverse each path string&lt;/li&gt;
&lt;li&gt;Split it by spaces into Directory (目录) and File Entries (文件项)&lt;/li&gt;
&lt;li&gt;For each file entry, parse File Name (文件名) and Content (内容)&lt;/li&gt;
&lt;li&gt;Build Full Path (完整路径)&lt;/li&gt;
&lt;li&gt;Store it in Hash Map (哈希表): &lt;code&gt;content -&amp;gt; list of full paths&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Return groups whose size is at least 2&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;4. Code Implementation (代码实现)&lt;/h2&gt;
&lt;h3&gt;1) Python Example (可独立运行)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import defaultdict


def find_duplicate(paths):
    content_map = defaultdict(list)

    for record in paths:
        parts = record.split(&quot; &quot;)
        directory = parts[0]

        for file_info in parts[1:]:
            left = file_info.find(&quot;(&quot;)
            right = file_info.rfind(&quot;)&quot;)

            file_name = file_info[:left]
            content = file_info[left + 1:right]
            full_path = directory + &quot;/&quot; + file_name

            content_map[content].append(full_path)

    return [group for group in content_map.values() if len(group) &amp;gt;= 2]


if __name__ == &quot;__main__&quot;:
    paths = [
        &quot;root/a 1.txt(abcd) 2.txt(efgh)&quot;,
        &quot;root/c 3.txt(abcd)&quot;,
        &quot;root/c/d 4.txt(efgh)&quot;,
        &quot;root 4.txt(1234)&quot;
    ]

    result = find_duplicate(paths)
    print(result)

    extra_input = [&quot;root/a 1.txt(abcd) 2.txt(efgh)&quot;]
    print(find_duplicate(extra_input))
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;from collections import defaultdict
from concurrent.futures import ThreadPoolExecutor


def parse_record(record):
    parts = record.split(&quot; &quot;)
    directory = parts[0]

    local_map = defaultdict(list)

    for file_info in parts[1:]:
        left = file_info.find(&quot;(&quot;)
        right = file_info.rfind(&quot;)&quot;)

        file_name = file_info[:left]
        content = file_info[left + 1:right]
        full_path = directory + &quot;/&quot; + file_name

        local_map[content].append(full_path)

    return local_map


def find_duplicate(paths, max_workers=4):
    content_map = defaultdict(list)

    # 多线程解析
    with ThreadPoolExecutor(max_workers=max_workers) as executor:
        results = executor.map(parse_record, paths)

    # 合并结果
    for local_map in results:
        for content, file_list in local_map.items():
            content_map[content].extend(file_list)

    return [group for group in content_map.values() if len(group) &amp;gt;= 2]


if __name__ == &quot;__main__&quot;:
    paths = [
        &quot;root/a 1.txt(abcd) 2.txt(efgh)&quot;,
        &quot;root/c 3.txt(abcd)&quot;,
        &quot;root/c/d 4.txt(efgh)&quot;,
        &quot;root 4.txt(1234)&quot;
    ]

    result = find_duplicate(paths, max_workers=4)
    print(result)

    extra_input = [&quot;root/a 1.txt(abcd) 2.txt(efgh)&quot;]
    print(find_duplicate(extra_input))
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;5. Complexity Analysis (复杂度分析)&lt;/h2&gt;
&lt;h3&gt;1) Time Complexity (时间复杂度)&lt;/h3&gt;
&lt;p&gt;The Time Complexity (时间复杂度) is $$O(N \cdot K)$$, where $N$ is the number of files and $K$ is the average parsing cost.&lt;/p&gt;
&lt;h3&gt;2) Space Complexity (空间复杂度)&lt;/h3&gt;
&lt;p&gt;The Space Complexity (空间复杂度) is $$O(N \cdot K)$$, because we store Content (内容) and File Paths (文件路径) in a Hash Map (哈希表).&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;6. Interview Notes (面试要点)&lt;/h2&gt;
&lt;h3&gt;1) Why Hash Map (为什么用哈希表)&lt;/h3&gt;
&lt;p&gt;A Hash Map (哈希表) is the most direct way to group files by the same Content (内容).&lt;/p&gt;
&lt;h3&gt;2) Why Not Compare Every Pair (为什么不两两比较)&lt;/h3&gt;
&lt;p&gt;Pairwise Comparison (两两比较) is too slow at $$O(N^2)$$, so grouping by key is the standard optimization.&lt;/p&gt;
&lt;h3&gt;3) Edge Case (边界情况)&lt;/h3&gt;
&lt;p&gt;If every file has unique content, the answer is an empty list because no group has at least two files.&lt;/p&gt;
&lt;hr /&gt;
</content:encoded></item><item><title>Prefill-Decode Disaggregation</title><link>https://lxy-alexander.github.io/blog/posts/llm/prefill-decode-disaggregation/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/llm/prefill-decode-disaggregation/</guid><description>Prefill-Decode Disaggregation</description><pubDate>Wed, 15 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;hr /&gt;
&lt;h1&gt;I. Prefill-Decode Disaggregation (PD 分离)&lt;/h1&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-c69d652d2a0747fab9aad1fab48ff742.r2.dev/images/image-20260415032239293&quot; alt=&quot;image-20260415032239293&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;1. Motivation (动机)&lt;/h2&gt;
&lt;p&gt;In standard LLM serving, prefill and decode run on the &lt;strong&gt;same GPU&lt;/strong&gt; and interfere with each other:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Prefill&lt;/strong&gt; is compute-bound (计算密集型): processes hundreds of tokens in parallel, saturates CUDA cores, one long iteration.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Decode&lt;/strong&gt; is memory-bandwidth-bound (内存带宽密集型): reads the full KV cache per step for just 1 new token, starved of compute.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Running them together causes &lt;strong&gt;prefill-decode interference (干扰)&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A large prefill blocks decode iterations → spikes in Inter-Token Latency (ITL, 令牌间延迟).&lt;/li&gt;
&lt;li&gt;Decode&apos;s need for low batch size conflicts with prefill&apos;s need for large batches.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;PD disaggregation&lt;/strong&gt; puts them on &lt;strong&gt;separate GPU pools&lt;/strong&gt; so each can be tuned independently.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Architecture (架构)&lt;/h2&gt;
&lt;h3&gt;1) Two Pools (两个资源池)&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pool&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Bottleneck&lt;/th&gt;
&lt;th&gt;Optimal hardware&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Prefill pool (预填充池)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Process prompt tokens, build KV cache&lt;/td&gt;
&lt;td&gt;Compute (FLOPs)&lt;/td&gt;
&lt;td&gt;High-FLOP GPUs (e.g. H100 SXM)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Decode pool (解码池)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Autoregressive token generation&lt;/td&gt;
&lt;td&gt;Memory bandwidth&lt;/td&gt;
&lt;td&gt;High-bandwidth GPUs or more GPUs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;2) KV Cache Transfer (KV缓存传输)&lt;/h3&gt;
&lt;p&gt;After prefill completes, the KV cache must be &lt;strong&gt;migrated&lt;/strong&gt; from the prefill GPU to the decode GPU. This is the central engineering challenge of PD disaggregation.&lt;/p&gt;
&lt;p&gt;$$ \text{Transfer cost} = \frac{2 \times n_{\text{layers}} \times d_{\text{model}} \times L_{\text{prompt}}}{\text{NVLink / RDMA bandwidth}} $$&lt;/p&gt;
&lt;p&gt;Where $L_{\text{prompt}}$ is the prompt length (提示长度). A 4096-token prompt on a 70B model generates ~8 GB of KV cache — transfer latency directly adds to TTFT (首个令牌时间).&lt;/p&gt;
&lt;p&gt;Transfer methods (传输方式):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;NVLink&lt;/strong&gt; — within a node, ~600 GB/s, negligible latency.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RDMA over InfiniBand&lt;/strong&gt; — across nodes, ~200–400 GB/s.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TCP/IP&lt;/strong&gt; — fallback, much slower, not recommended.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;3. Runnable Example (可运行示例)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;# pd_disaggregation_sim.py
# Simulates PD-disaggregated scheduling with KV transfer cost.
# No external dependencies required.

import time
import threading
from queue import Queue

PREFILL_TIME_PER_TOKEN = 0.0005   # seconds per token (compute-bound)
DECODE_TIME_PER_TOKEN  = 0.020    # seconds per token (memory-bound)
KV_TRANSFER_GBPS       = 200      # simulated NVLink bandwidth (GB/s)
BYTES_PER_KV_TOKEN     = 2 * 80 * 8192  # 2 (K+V) × 80 layers × 8192 bytes

class Request:
    def __init__(self, req_id: str, prompt_len: int, max_new_tokens: int):
        self.req_id = req_id
        self.prompt_len = prompt_len
        self.max_new_tokens = max_new_tokens

def prefill_worker(req: Request, kv_queue: Queue):
    &quot;&quot;&quot;Prefill pool: process prompt, produce KV cache.&quot;&quot;&quot;
    t0 = time.time()
    time.sleep(req.prompt_len * PREFILL_TIME_PER_TOKEN)   # simulate compute
    prefill_ms = (time.time() - t0) * 1000

    # Simulate KV cache transfer
    kv_bytes = BYTES_PER_KV_TOKEN * req.prompt_len
    transfer_s = kv_bytes / (KV_TRANSFER_GBPS * 1e9)
    time.sleep(transfer_s)
    transfer_ms = transfer_s * 1000

    print(f&quot;[Prefill→Transfer] {req.req_id}: &quot;
          f&quot;prefill={prefill_ms:.1f}ms  transfer={transfer_ms:.1f}ms  &quot;
          f&quot;KV={kv_bytes/1e6:.1f}MB&quot;)
    kv_queue.put(req)    # hand off to decode pool

def decode_worker(kv_queue: Queue):
    &quot;&quot;&quot;Decode pool: consume KV cache, generate tokens.&quot;&quot;&quot;
    while True:
        req = kv_queue.get()
        if req is None:
            break
        t0 = time.time()
        time.sleep(req.max_new_tokens * DECODE_TIME_PER_TOKEN)
        decode_ms = (time.time() - t0) * 1000
        ttft = (req.prompt_len * PREFILL_TIME_PER_TOKEN
                + BYTES_PER_KV_TOKEN * req.prompt_len / (KV_TRANSFER_GBPS * 1e9)
                + DECODE_TIME_PER_TOKEN) * 1000
        print(f&quot;[Decode Done]     {req.req_id}: &quot;
              f&quot;decode={decode_ms:.1f}ms  est.TTFT={ttft:.1f}ms&quot;)
        kv_queue.task_done()

if __name__ == &quot;__main__&quot;:
    requests = [
        Request(&quot;R1&quot;, prompt_len=512,  max_new_tokens=50),
        Request(&quot;R2&quot;, prompt_len=2048, max_new_tokens=20),
        Request(&quot;R3&quot;, prompt_len=256,  max_new_tokens=100),
    ]

    kv_queue: Queue = Queue()

    # Start decode pool (always listening)
    decoder = threading.Thread(target=decode_worker, args=(kv_queue,), daemon=True)
    decoder.start()

    # Prefill pool: process all requests (could be parallel in real systems)
    threads = [threading.Thread(target=prefill_worker, args=(r, kv_queue))
               for r in requests]
    for t in threads:
        t.start()
    for t in threads:
        t.join()

    kv_queue.join()
    kv_queue.put(None)   # signal decoder to exit
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;4. Benefits and Trade-offs (优缺点)&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Coupled (耦合)&lt;/th&gt;
&lt;th&gt;Disaggregated (分离)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;TTFT&lt;/td&gt;
&lt;td&gt;Higher (prefill blocks decode)&lt;/td&gt;
&lt;td&gt;Lower (dedicated prefill GPUs)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ITL&lt;/td&gt;
&lt;td&gt;Spikey under long prompts&lt;/td&gt;
&lt;td&gt;Stable (no prefill interference)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Independent scaling (独立扩缩容)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes — scale each pool by workload&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;KV transfer overhead&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Adds latency on long prompts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hardware cost&lt;/td&gt;
&lt;td&gt;Lower&lt;/td&gt;
&lt;td&gt;Higher (more GPUs)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;5. Key Formula — Transfer Latency (传输延迟)&lt;/h2&gt;
&lt;p&gt;For a &lt;strong&gt;LLaMA-3 70B&lt;/strong&gt; with a 4096-token prompt over 200 GB/s RDMA:&lt;/p&gt;
&lt;p&gt;$$ T_{\text{transfer}} \approx \frac{8,\text{GB}}{200,\text{GB/s}} = 40,\text{ms} $$&lt;/p&gt;
&lt;p&gt;This 40 ms is added directly to TTFT — the central cost of PD disaggregation.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;6. Related Concepts (相关概念)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Chunked Prefill (分块预填充)&lt;/strong&gt; — an alternative to PD disaggregation that interleaves prefill and decode on the same GPU; lower cost but less isolation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Continuous Batching (连续批处理)&lt;/strong&gt; — iteration-level scheduling; used within each pool in PD disaggregation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;KV Cache Migration (KV缓存迁移)&lt;/strong&gt; — the core engineering problem: moving large tensors across GPUs with minimal TTFT penalty.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mooncake / Splitwise / DistServe&lt;/strong&gt; — research systems that implement PD disaggregation at production scale.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>nn.Module</title><link>https://lxy-alexander.github.io/blog/posts/pytorch/nnmodule/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/pytorch/nnmodule/</guid><description>nn.Module</description><pubDate>Wed, 15 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;I. &lt;code&gt;nn.Module&lt;/code&gt; (神经网络模块基类)&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;nn.Module&lt;/code&gt; is ==the base class (基类) for all PyTorch models== — it provides ==parameter tracking== (参数追踪), ==device management== (设备管理), and ==serialization== (序列化), so subclasses only need to ==define &lt;code&gt;__init__&lt;/code&gt; and &lt;code&gt;forward&lt;/code&gt;.==&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;1. Lifecycle (生命周期)&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;nn.Module&lt;/code&gt; enforces a two-method contract (两方法约定) that separates structure from computation.&lt;/p&gt;
&lt;h3&gt;1) &lt;code&gt;__init__&lt;/code&gt; — Structure (结构定义)&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;__init__&lt;/code&gt; registers submodules (子模块) and parameters (参数) into ==PyTorch&apos;s internal registry== (内部注册表) via &lt;code&gt;super().__init__()&lt;/code&gt;. Skipping &lt;code&gt;super().__init__()&lt;/code&gt; leaves the registry uninitialized — every subsequent attribute assignment silently fails to register.&lt;/p&gt;
&lt;h3&gt;2) &lt;code&gt;forward&lt;/code&gt; — Computation (计算定义)&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;forward&lt;/code&gt; defines ==the computation graph (计算图) traced by autograd (自动微分)== on each call. Call the module as &lt;code&gt;model(x)&lt;/code&gt; rather than &lt;code&gt;model.forward(x)&lt;/code&gt; — the &lt;code&gt;__call__&lt;/code&gt; wrapper fires registered hooks (钩子) before and after &lt;code&gt;forward&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import torch
import torch.nn as nn

class MLP(nn.Module):
    def __init__(self, in_dim: int, out_dim: int):
        super().__init__()
        self.fc = nn.Linear(in_dim, out_dim)  # auto-registered (自动注册)

    def forward(self, x: torch.Tensor) -&amp;gt; torch.Tensor:
        return torch.relu(self.fc(x))

model = MLP(4, 2)
print(model(torch.randn(3, 4)).shape)  # torch.Size([3, 2])
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Parameter Management (参数管理)&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;nn.Module&lt;/code&gt; distinguishes three kinds of named tensors stored inside a module.&lt;/p&gt;
&lt;h3&gt;1) &lt;code&gt;nn.Parameter&lt;/code&gt; — Learnable (可学习参数)&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;nn.Parameter&lt;/code&gt; wraps a tensor so that &lt;code&gt;requires_grad=True&lt;/code&gt; by default and it appears in &lt;code&gt;model.parameters()&lt;/code&gt;. Use it for weights that the optimizer (优化器) must update — plain tensors assigned as attributes are invisible to the optimizer.&lt;/p&gt;
&lt;h3&gt;2) &lt;code&gt;register_buffer&lt;/code&gt; — Non-learnable State (非可学习状态)&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;register_buffer&lt;/code&gt; attaches a tensor to the module that moves with &lt;code&gt;.to(device)&lt;/code&gt; but is excluded from &lt;code&gt;parameters()&lt;/code&gt;. Prefer it over plain attributes for fixed tensors like running statistics (运行统计量) in BatchNorm.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import torch
import torch.nn as nn

class NormLayer(nn.Module):
    def __init__(self, dim: int):
        super().__init__()
        self.weight = nn.Parameter(torch.ones(dim))        # learnable (可学习)
        self.register_buffer(&quot;running_mean&quot;, torch.zeros(dim))  # non-learnable (非可学习)

    def forward(self, x: torch.Tensor) -&amp;gt; torch.Tensor:
        return (x - self.running_mean) * self.weight

model = NormLayer(4)
print(dict(model.named_parameters()).keys())  # weight only
print(dict(model.named_buffers()).keys())     # running_mean only
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) &lt;code&gt;named_parameters&lt;/code&gt; vs &lt;code&gt;parameters&lt;/code&gt; (命名参数 vs 参数迭代器)&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;parameters()&lt;/code&gt; yields tensors for the optimizer; &lt;code&gt;named_parameters()&lt;/code&gt; yields &lt;code&gt;(name, tensor)&lt;/code&gt; pairs for debugging or selective freezing (选择性冻结). Freeze a layer by setting &lt;code&gt;param.requires_grad = False&lt;/code&gt; — the optimizer skips tensors where &lt;code&gt;requires_grad&lt;/code&gt; is false.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. Hooks (钩子)&lt;/h2&gt;
&lt;p&gt;Hooks intercept (拦截) the forward and backward passes without modifying &lt;code&gt;forward&lt;/code&gt; itself.&lt;/p&gt;
&lt;h3&gt;1) Forward Hook (前向钩子)&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;register_forward_hook(fn)&lt;/code&gt; fires after &lt;code&gt;forward&lt;/code&gt; completes, receiving &lt;code&gt;(module, input, output)&lt;/code&gt;. Use it for activation logging (激活值记录) or feature extraction (特征提取) without altering model code.&lt;/p&gt;
&lt;h3&gt;2) Backward Hook (反向钩子)&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;register_full_backward_hook(fn)&lt;/code&gt; fires during the backward pass (反向传播), receiving &lt;code&gt;(module, grad_input, grad_output)&lt;/code&gt;. Use it to inspect or clip gradients (梯度裁剪) at the module level; the trade-off is a slight overhead on every backward call.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import torch
import torch.nn as nn

model = nn.Linear(4, 2)
activations = {}

def fwd_hook(module, inp, out):
    activations[&quot;linear&quot;] = out.detach()

handle = model.register_forward_hook(fwd_hook)
model(torch.randn(3, 4))
print(activations[&quot;linear&quot;].shape)  # torch.Size([3, 2])
handle.remove()  # always remove hooks when done (用完及时移除)
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Always call &lt;code&gt;handle.remove()&lt;/code&gt; after use — unreleased hooks accumulate (积累) and slow down every forward pass.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;4. &lt;code&gt;state_dict&lt;/code&gt; and Serialization (&lt;code&gt;state_dict&lt;/code&gt; 与序列化)&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;state_dict&lt;/code&gt; is the canonical (规范) way to save and restore model weights in PyTorch.&lt;/p&gt;
&lt;h3&gt;1) Save and Load (保存与加载)&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;state_dict()&lt;/code&gt; returns an &lt;code&gt;OrderedDict&lt;/code&gt; of all parameters and buffers keyed by their registered names. Prefer &lt;code&gt;torch.save(model.state_dict(), path)&lt;/code&gt; over pickling the entire model — it decouples weights from the class definition (解耦权重与类定义), making loading robust across code refactors.&lt;/p&gt;
&lt;h3&gt;2) &lt;code&gt;load_state_dict&lt;/code&gt; — &lt;code&gt;strict&lt;/code&gt; Flag (&lt;code&gt;strict&lt;/code&gt; 标志)&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;load_state_dict(sd, strict=True)&lt;/code&gt; raises an error on any key mismatch (键不匹配); set &lt;code&gt;strict=False&lt;/code&gt; when loading a pretrained backbone (预训练骨干网络) into a model with extra heads — missing or unexpected keys are silently ignored.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import torch
import torch.nn as nn

model = nn.Linear(4, 2)
torch.save(model.state_dict(), &quot;/tmp/weights.pt&quot;)

# Restore on any device (在任意设备恢复)
new_model = nn.Linear(4, 2)
new_model.load_state_dict(torch.load(&quot;/tmp/weights.pt&quot;, map_location=&quot;cpu&quot;))
print(new_model(torch.randn(3, 4)).shape)  # torch.Size([3, 2])
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;5. Training vs Eval Mode (训练模式 vs 推理模式)&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;.train()&lt;/code&gt; and &lt;code&gt;.eval()&lt;/code&gt; toggle (切换) the behavior of stateful layers like Dropout (随机失活) and BatchNorm (批归一化).&lt;/p&gt;
&lt;h3&gt;1) &lt;code&gt;.train()&lt;/code&gt; / &lt;code&gt;.eval()&lt;/code&gt; — Mode Switch (模式切换)&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;.train()&lt;/code&gt; enables Dropout and uses per-batch statistics in BatchNorm; ==&lt;code&gt;.eval()&lt;/code&gt; disables Dropout and switches BatchNorm to its running statistics (运行统计量).==&lt;/p&gt;
&lt;p&gt;==Forgetting &lt;code&gt;.eval()&lt;/code&gt; at inference (推理) is one of the most common bugs in PyTorch== — Dropout randomly zeroes activations and BatchNorm uses noisy batch stats instead of the learned ones.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import torch
import torch.nn as nn

model = nn.Sequential(nn.Linear(4, 8), nn.Dropout(0.5), nn.Linear(8, 2))

model.train()
out_train = model(torch.randn(3, 4))  # dropout active (激活)

model.eval()
with torch.no_grad():
    out_eval = model(torch.randn(3, 4))  # dropout disabled (禁用)

print(out_train.shape, out_eval.shape)  # both torch.Size([3, 2])
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; ==&lt;code&gt;torch.no_grad()&lt;/code&gt; disables gradient tracking (梯度追踪)== for memory efficiency but does not switch layer behavior — always pair it with &lt;code&gt;.eval()&lt;/code&gt; at inference.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt; &lt;code&gt;nn.Module&lt;/code&gt; tracks parameters via &lt;code&gt;nn.Parameter&lt;/code&gt; and &lt;code&gt;register_buffer&lt;/code&gt;, intercepts passes via hooks, serializes via &lt;code&gt;state_dict&lt;/code&gt;, and gates layer behavior via &lt;code&gt;.train()&lt;/code&gt; / &lt;code&gt;.eval()&lt;/code&gt; — mastering these five mechanisms covers the vast majority of real-world PyTorch interview questions.&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item><item><title>NumPy Overview</title><link>https://lxy-alexander.github.io/blog/posts/numpy/numpy-overview/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/numpy/numpy-overview/</guid><description>NumPy Overview</description><pubDate>Wed, 15 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;I. NumPy Overview(数值计算库)&lt;/h1&gt;
&lt;p&gt;==NumPy is a fundamental library for numerical computing (数值计算) in Python==. It provides efficient data structures and operations for handling large-scale numerical data.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1. What is NumPy (是什么)&lt;/h2&gt;
&lt;p&gt;NumPy is a library that provides the &lt;code&gt;ndarray (多维数组)&lt;/code&gt; object for storing and manipulating numerical data efficiently.&lt;/p&gt;
&lt;h3&gt;1) Core Data Structure&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ndarray (多维数组)&lt;/code&gt;
A homogeneous (同质的) multi-dimensional array.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;import numpy as np

x = np.array([[1, 2], [3, 4]])
print(x)
print(type(x))
print(x.shape)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Why Learn NumPy (为什么需要学习)&lt;/h2&gt;
&lt;h3&gt;1) High Performance (高性能)&lt;/h3&gt;
&lt;p&gt;NumPy operations are implemented in C (底层C实现), making them much faster than Python loops.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import numpy as np

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

print(a + b)  # element-wise addition (逐元素加法)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;2) Vectorization (向量化)&lt;/h3&gt;
&lt;p&gt;Vectorization avoids explicit loops (避免显式循环), improving speed and readability.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import numpy as np

a = np.array([1, 2, 3])
print(a * 2)  # vectorized operation (向量化操作)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;3) Foundation of AI Libraries (AI基础)&lt;/h3&gt;
&lt;p&gt;Many libraries depend on NumPy:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PyTorch (深度学习框架)&lt;/li&gt;
&lt;li&gt;TensorFlow (深度学习框架)&lt;/li&gt;
&lt;li&gt;SciPy (科学计算库)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;3. Comparison&lt;/h2&gt;
&lt;h3&gt;1) NumPy (数值计算库)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Focus: numerical computing (数值计算)&lt;/li&gt;
&lt;li&gt;Core object: &lt;code&gt;ndarray (多维数组)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Mainly used for CPU computation (CPU计算)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;import numpy as np

x = np.array([1, 2, 3])
print(x * 2)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) PyTorch (深度学习框架)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Focus: deep learning (深度学习)&lt;/li&gt;
&lt;li&gt;Core object: &lt;code&gt;Tensor (张量)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Supports GPU acceleration (GPU加速)&lt;/li&gt;
&lt;li&gt;Supports automatic differentiation (自动求导)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;import torch

x = torch.tensor([1, 2, 3], dtype=torch.float32)
print(x * 2)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;4. Basic Operations (基础操作)&lt;/h2&gt;
&lt;h3&gt;1) Create Arrays (创建数组)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import numpy as np

a = np.zeros((2, 3))   # all zeros (全0)
b = np.ones((2, 3))    # all ones (全1)
c = np.arange(0, 10)   # range (范围)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;2) Shape and Reshape (形状操作)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import numpy as np

x = np.array([1, 2, 3, 4])
y = x.reshape(2, 2)

print(y)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;3) Indexing (索引)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import numpy as np

x = np.array([[1, 2], [3, 4]])

print(x[0, 1])  # access element (访问元素)
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;II. NumPy Interview Questions&lt;/h1&gt;
&lt;hr /&gt;
&lt;h2&gt;1. What is NumPy (是什么)&lt;/h2&gt;
&lt;p&gt;NumPy is a numerical computing library (数值计算库) in Python that provides the &lt;code&gt;ndarray (多维数组)&lt;/code&gt; for efficient computation.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. What is ndarray (多维数组)&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;ndarray&lt;/code&gt; is a homogeneous array (同质数组) that stores elements of the same data type in contiguous memory (连续内存).&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. Difference between list and ndarray (区别)&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;list&lt;/code&gt; allows mixed types (可混合类型), while &lt;code&gt;ndarray&lt;/code&gt; requires a single type (统一类型) and supports vectorized operations (向量化运算).&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4. What is vectorization (向量化)&lt;/h2&gt;
&lt;p&gt;Vectorization is performing operations on entire arrays without explicit loops (无需显式循环).&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;5. What is broadcasting (广播机制)&lt;/h2&gt;
&lt;p&gt;Broadcasting allows arrays of different shapes (不同形状) to be operated together automatically.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;6. Why is NumPy faster than Python (为什么更快)&lt;/h2&gt;
&lt;p&gt;Because it uses C implementation (底层C实现), contiguous memory (连续内存), and vectorization (向量化).&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;7. How to create an array (创建数组)&lt;/h2&gt;
&lt;p&gt;Use functions like &lt;code&gt;np.array()&lt;/code&gt;, &lt;code&gt;np.zeros()&lt;/code&gt;, &lt;code&gt;np.ones()&lt;/code&gt;, &lt;code&gt;np.arange()&lt;/code&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;8. What is shape (形状)&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;shape&lt;/code&gt; describes the dimensions (维度) of an array.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;9. What is reshape (重塑)&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;reshape&lt;/code&gt; changes the shape (改变形状) of an array without changing its data.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;10. Difference between copy and view (拷贝 vs 视图)&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;copy&lt;/code&gt; creates new memory (新内存), while &lt;code&gt;view&lt;/code&gt; shares memory (共享内存).&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;11. What is slicing (切片)&lt;/h2&gt;
&lt;p&gt;Slicing extracts a subset (子数组) of an array.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;12. What is dtype (数据类型)&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;dtype&lt;/code&gt; defines the type (数据类型) of elements in an array.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;13. What is axis (轴)&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;axis&lt;/code&gt; specifies the direction (方向) along which operations are performed.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;14. What is flatten (展平)&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;flatten&lt;/code&gt; converts a multi-dimensional array (多维数组) into one dimension.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;15. Difference between arange and linspace (区别)&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;arange&lt;/code&gt; uses step size (步长), while &lt;code&gt;linspace&lt;/code&gt; uses number of points (点的数量).&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;16. What is indexing (索引)&lt;/h2&gt;
&lt;p&gt;Indexing accesses specific elements (访问元素) using positions.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;17. What is boolean indexing (布尔索引)&lt;/h2&gt;
&lt;p&gt;Boolean indexing selects elements based on conditions (条件筛选).&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;18. What is aggregation (聚合)&lt;/h2&gt;
&lt;p&gt;Aggregation performs operations like &lt;code&gt;sum&lt;/code&gt;, &lt;code&gt;mean&lt;/code&gt; on arrays.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;19. What is matrix multiplication (矩阵乘法)&lt;/h2&gt;
&lt;p&gt;Matrix multiplication follows:&lt;/p&gt;
&lt;p&gt;$$
C = A \cdot B
$$&lt;/p&gt;
&lt;p&gt;using &lt;code&gt;np.dot()&lt;/code&gt; or &lt;code&gt;@&lt;/code&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;20. What is the main purpose of NumPy (核心作用)&lt;/h2&gt;
&lt;p&gt;NumPy is used for efficient numerical computation (高效数值计算) and is the foundation of scientific computing (科学计算基础).&lt;/p&gt;
</content:encoded></item><item><title>Dynamic Graph vs Static Graph</title><link>https://lxy-alexander.github.io/blog/posts/pytorch/dynamic-graph-vs-static-graph/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/pytorch/dynamic-graph-vs-static-graph/</guid><description>Dynamic Graph vs Static Graph</description><pubDate>Wed, 15 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;I. Dynamic Graph vs Static Graph (动态图 vs 静态图)&lt;/h1&gt;
&lt;h2&gt;1. What is Dynamic Graph? (动态图是什么)&lt;/h2&gt;
&lt;h3&gt;1) Definition (定义)&lt;/h3&gt;
&lt;p&gt;A &lt;strong&gt;Dynamic Computation Graph (动态图计算图)&lt;/strong&gt; is built during runtime (运行时构建).&lt;/p&gt;
&lt;p&gt;👉 The graph changes as the program executes (执行时动态变化).&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;2) Characteristics (特点)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Defined on-the-fly (即时定义)&lt;/li&gt;
&lt;li&gt;Flexible control flow (灵活控制流)&lt;/li&gt;
&lt;li&gt;Easy debugging (易调试)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;3) Example (示例)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import torch

x = torch.tensor(2.0, requires_grad=True)
y = x * x + 3

y.backward()
print(x.grad)  # dy/dx = 2x = 4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;👉 The graph is created dynamically when &lt;code&gt;y&lt;/code&gt; is computed.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. What is Static Graph? (静态图是什么)&lt;/h2&gt;
&lt;h3&gt;1) Definition (定义)&lt;/h3&gt;
&lt;p&gt;A &lt;strong&gt;Static Computation Graph (静态计算图)&lt;/strong&gt; is defined before execution (运行前定义).&lt;/p&gt;
&lt;p&gt;👉 The graph structure does not change during runtime (运行时不变).&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;2) Characteristics (特点)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Predefined graph (预先定义)&lt;/li&gt;
&lt;li&gt;Optimized before execution (执行前优化)&lt;/li&gt;
&lt;li&gt;Better performance (更高性能)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;3) Conceptual Example (概念示例)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# Pseudo-code (伪代码)
x = placeholder()
y = x * x + 3

# Build graph first (先构建图)
graph = build_graph(x, y)

# Execute later (再执行)
run(graph, feed_dict={x: 2})
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;3. Key Differences (核心区别)&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature (特性)&lt;/th&gt;
&lt;th&gt;Dynamic Graph&lt;/th&gt;
&lt;th&gt;Static Graph&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Build Time (构建时间)&lt;/td&gt;
&lt;td&gt;Runtime (运行时)&lt;/td&gt;
&lt;td&gt;Before execution (运行前)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flexibility (灵活性)&lt;/td&gt;
&lt;td&gt;High (高)&lt;/td&gt;
&lt;td&gt;Low (低)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Debugging (调试)&lt;/td&gt;
&lt;td&gt;Easy (容易)&lt;/td&gt;
&lt;td&gt;Hard (困难)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Performance (性能)&lt;/td&gt;
&lt;td&gt;Medium (中等)&lt;/td&gt;
&lt;td&gt;High (高)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;4. Mathematical View (数学视角)&lt;/h2&gt;
&lt;p&gt;A computation graph (计算图) represents operations:&lt;/p&gt;
&lt;p&gt;$$
y = f(x)
$$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Dynamic graph: build $f(x)$ during execution (运行时构建函数)&lt;/li&gt;
&lt;li&gt;Static graph: define $f(x)$ before execution (运行前定义函数)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;5. When to Use (何时使用)&lt;/h2&gt;
&lt;h3&gt;1) Dynamic Graph&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Research (科研)&lt;/li&gt;
&lt;li&gt;Prototyping (快速实验)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;2) Static Graph&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Production systems (生产环境)&lt;/li&gt;
&lt;li&gt;Performance-critical tasks (高性能场景)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;6. One-Line Summary (一句话总结)&lt;/h2&gt;
&lt;p&gt;👉 Dynamic graph (动态图) = flexible and easy (灵活易用)
👉 Static graph (静态图) = efficient and optimized (高效优化)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>PyTorch Overview</title><link>https://lxy-alexander.github.io/blog/posts/pytorch/pytorch-overview/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/pytorch/pytorch-overview/</guid><description>PyTorch Overview</description><pubDate>Wed, 15 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;I. PyTorch Overview (概述)&lt;/h1&gt;
&lt;h2&gt;1. What is PyTorch? (它是做什么的)&lt;/h2&gt;
&lt;h3&gt;1) Definition (定义)&lt;/h3&gt;
&lt;p&gt;==&lt;strong&gt;PyTorch&lt;/strong&gt; is a deep learning framework (深度学习框架) used for building and training neural networks (神经网络).==&lt;/p&gt;
&lt;p&gt;👉 It provides:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Tensor computation (张量计算)&lt;/li&gt;
&lt;li&gt;Automatic differentiation (自动求导)&lt;/li&gt;
&lt;li&gt;GPU acceleration (GPU加速)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;2) Core Idea (核心思想)&lt;/h3&gt;
&lt;p&gt;PyTorch uses a &lt;strong&gt;==dynamic computation graph== (动态图计算图)&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;👉 This means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The graph is built during execution (运行时构建)&lt;/li&gt;
&lt;li&gt;Easier debugging (更易调试)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Why Learn PyTorch? (为什么学习)&lt;/h2&gt;
&lt;h3&gt;1) Widely Used in Industry (工业应用广泛)&lt;/h3&gt;
&lt;p&gt;PyTorch is used in:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Computer Vision (计算机视觉)&lt;/li&gt;
&lt;li&gt;Natural Language Processing (自然语言处理)&lt;/li&gt;
&lt;li&gt;Large Language Models (大模型)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;2) Easy to Use (易用性强)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Pythonic syntax (Python风格语法)&lt;/li&gt;
&lt;li&gt;Flexible design (灵活设计)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;3) Strong Ecosystem (生态系统强大)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Integrated with libraries (库集成)&lt;/li&gt;
&lt;li&gt;Active community (活跃社区)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;3. Comparison (对比)&lt;/h2&gt;
&lt;h3&gt;1) PyTorch vs TensorFlow&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;PyTorch:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Dynamic graph (动态图)&lt;/li&gt;
&lt;li&gt;Easier debugging (易调试)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;TensorFlow:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Static graph (静态图)&lt;/li&gt;
&lt;li&gt;More production tools (生产工具多)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;2) PyTorch vs NumPy&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;PyTorch:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Supports GPU (支持GPU)&lt;/li&gt;
&lt;li&gt;Automatic differentiation (自动求导)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;NumPy:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CPU only (仅CPU)&lt;/li&gt;
&lt;li&gt;No gradients (无梯度)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;4. Runnable Example (可运行示例)&lt;/h2&gt;
&lt;h3&gt;1) Simple Linear Model (线性模型)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import torch
import torch.nn as nn
import torch.optim as optim

# Define model (定义模型)
model = nn.Linear(1, 1)

# Loss function (损失函数)
criterion = nn.MSELoss()

# Optimizer (优化器)
optimizer = optim.SGD(model.parameters(), lr=0.01)

# Training data (训练数据)
x = torch.tensor([[1.0], [2.0], [3.0]])
y = torch.tensor([[2.0], [4.0], [6.0]])

# Training loop (训练循环)
for epoch in range(100):
    y_pred = model(x)
    loss = criterion(y_pred, y)

    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

# Output result (输出结果)
print(&quot;Weight:&quot;, model.weight.item())
print(&quot;Bias:&quot;, model.bias.item())&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Triton Overview</title><link>https://lxy-alexander.github.io/blog/posts/triton/triton-overview/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/triton/triton-overview/</guid><description>Triton Overview</description><pubDate>Wed, 15 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;I. Triton Overview (概述)&lt;/h1&gt;
&lt;h2&gt;1. What is Triton? (它是做什么的)&lt;/h2&gt;
&lt;h3&gt;1) Definition (定义)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Triton&lt;/strong&gt; is a GPU programming language (GPU编程语言) and compiler (编译器).&lt;/p&gt;
&lt;p&gt;👉 It is used to write custom GPU kernels (自定义GPU算子) using Python.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;2) Core Function (核心功能)&lt;/h3&gt;
&lt;p&gt;Triton allows you to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Control GPU computation (控制GPU计算)&lt;/li&gt;
&lt;li&gt;Optimize memory access (优化内存访问)&lt;/li&gt;
&lt;li&gt;Improve performance (提升性能)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Why Learn Triton? (为什么学习)&lt;/h2&gt;
&lt;h3&gt;1) Performance Optimization (性能优化)&lt;/h3&gt;
&lt;p&gt;Deep learning systems (深度学习系统) rely heavily on GPU computation (GPU计算).&lt;/p&gt;
&lt;p&gt;👉 Triton helps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Reduce runtime (减少运行时间)&lt;/li&gt;
&lt;li&gt;Improve efficiency (提升效率)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;2) Simpler than CUDA (比CUDA简单)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;CUDA: low-level programming (底层编程)&lt;/li&gt;
&lt;li&gt;Triton: high-level abstraction (高层抽象)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;👉 Easier to write and maintain (更易编写和维护)&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. Comparison (对比)&lt;/h2&gt;
&lt;h3&gt;1) Triton vs CUDA&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Triton:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Python-based (基于Python)&lt;/li&gt;
&lt;li&gt;Automatic optimization (自动优化)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;CUDA:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;C++-based (基于C++)&lt;/li&gt;
&lt;li&gt;Manual optimization (手动优化)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;2) Triton vs PyTorch&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Triton:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Kernel-level programming (算子级编程)&lt;/li&gt;
&lt;li&gt;Used for optimization (用于优化)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;PyTorch:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Model-level framework (模型级框架)&lt;/li&gt;
&lt;li&gt;Used for training (用于训练)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;4. Runnable Example (可运行示例)&lt;/h2&gt;
&lt;h3&gt;1) Vector Addition (向量加法)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import torch
import triton
import triton.language as tl

@triton.jit
def add_kernel(x_ptr, y_ptr, output_ptr, n, BLOCK_SIZE: tl.constexpr):
    pid = tl.program_id(0)
    offsets = pid * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE)
    mask = offsets &amp;lt; n

    x = tl.load(x_ptr + offsets, mask=mask)
    y = tl.load(y_ptr + offsets, mask=mask)
    tl.store(output_ptr + offsets, x + y, mask=mask)

def add(x, y):
    n = x.numel()
    output = torch.empty_like(x)

    grid = lambda meta: (triton.cdiv(n, meta[&apos;BLOCK_SIZE&apos;]),)

    add_kernel[grid](x, y, output, n, BLOCK_SIZE=1024)
    return output

# Test
x = torch.randn(1024, device=&apos;cuda&apos;)
y = torch.randn(1024, device=&apos;cuda&apos;)

z = add(x, y)
print(z[:5])&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>nano-vllm</title><link>https://lxy-alexander.github.io/blog/posts/vllm/nano-vllm/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/vllm/nano-vllm/</guid><description>nano-vllm</description><pubDate>Fri, 20 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;1. 初始化阶段（LLMEngine.&lt;strong&gt;init&lt;/strong&gt;）&lt;/h1&gt;
&lt;pre&gt;&lt;code&gt;用户：LLM(path, ...)
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;步骤&lt;/th&gt;
&lt;th&gt;内容&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;解析 Config&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;启动 Tensor Parallel 子进程（tp&amp;gt;1 时）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;创建 ModelRunner（主进程 rank 0）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;加载 Tokenizer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;创建 Scheduler&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h1&gt;2. 模型构建阶段（ModelRunner.&lt;strong&gt;init&lt;/strong&gt;）&lt;/h1&gt;
&lt;pre&gt;&lt;code&gt;ModelRunner(config, 0, events)
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;步骤&lt;/th&gt;
&lt;th&gt;内容&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;dist.init_process_group&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Qwen3ForCausalLM(hf_config)&lt;/code&gt;：construct model structure + allocate space for parameters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;code&gt;load_model(model, path)&lt;/code&gt;：从 safetensors 加载权重&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;code&gt;warmup_model()&lt;/code&gt;：跑一次 prefill，触发 JIT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;code&gt;allocate_kv_cache()&lt;/code&gt;：分配 KV cache blocks，挂到 Attention&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;code&gt;capture_cudagraph()&lt;/code&gt;：为 decode 捕获 CUDAGraph（可选）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;3. 请求入队阶段&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;用户：add_request(prompt) 或 generate(prompts)
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;步骤&lt;/th&gt;
&lt;th&gt;内容&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Tokenizer 编码 prompt → token_ids&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;构建 Sequence（含 sampling_params）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Scheduler.add(seq) → 进入 waiting 队列&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;4. 推理循环阶段（step()）&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;while not is_finished():
    step()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;每个 step 内部：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;子阶段&lt;/th&gt;
&lt;th&gt;职责&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;调度&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;scheduler.schedule()&lt;/code&gt; → 选出 seqs，决定 prefill 或 decode&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;数据准备&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;prepare_prefill&lt;/code&gt; / &lt;code&gt;prepare_decode&lt;/code&gt; → input_ids, positions, slot_mapping 等&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;模型前向&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;run_model&lt;/code&gt; → embed → layers → lm_head → logits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;采样&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sampler(logits)&lt;/code&gt; → token_ids&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;后处理&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;scheduler.postprocess&lt;/code&gt; → append_token，更新状态，回收 blocks&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;p&gt;==Prefill 存在的意义就是：利用 prompt 已知的特点，用 GPU 并行一次性处理完，避免逐 token 串行的巨大开销。==&lt;/p&gt;
&lt;h2&gt;5. 输出阶段&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;generate() 返回后
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;步骤&lt;/th&gt;
&lt;th&gt;内容&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;按 seq_id 收集 outputs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Tokenizer.decode(token_ids) → 文本&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;返回 &lt;code&gt;[{&quot;text&quot;: ..., &quot;token_ids&quot;: ...}, ...]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;阶段关系示意&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────────────────┐
│  1. 初始化    Config + ModelRunner + Scheduler + Tokenizer          │
└─────────────────────────────────────────────────────────────────────┘
                                    │
┌─────────────────────────────────────────────────────────────────────┐
│  2. 模型构建  建图 → 加载权重 → warmup → 分配 KV cache → CUDAGraph   │
└─────────────────────────────────────────────────────────────────────┘
                                    │
┌─────────────────────────────────────────────────────────────────────┐
│  3. 请求入队  prompt → tokenize → Sequence → Scheduler.add()        │
└─────────────────────────────────────────────────────────────────────┘
                                    │
┌─────────────────────────────────────────────────────────────────────┐
│  4. 推理循环  schedule → prepare → run_model → sampler → postprocess │
│             （prefill 和 decode 交替）                                │
└─────────────────────────────────────────────────────────────────────┘
                                    │
┌─────────────────────────────────────────────────────────────────────┐
│  5. 输出     收集完成 seq → decode → 返回 texts                      │
└─────────────────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;简要对照表&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;阶段&lt;/th&gt;
&lt;th&gt;入口&lt;/th&gt;
&lt;th&gt;主要动作&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1 初始化&lt;/td&gt;
&lt;td&gt;&lt;code&gt;LLM(...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Config、进程、Tokenizer、Scheduler&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2 模型构建&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ModelRunner.__init__&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;模型结构、加载权重、KV cache、CUDAGraph&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3 请求入队&lt;/td&gt;
&lt;td&gt;&lt;code&gt;add_request&lt;/code&gt; / &lt;code&gt;generate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tokenize → Sequence → waiting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4 推理循环&lt;/td&gt;
&lt;td&gt;&lt;code&gt;step()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;schedule → prepare → run → sample → postprocess&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5 输出&lt;/td&gt;
&lt;td&gt;&lt;code&gt;generate&lt;/code&gt; 返回&lt;/td&gt;
&lt;td&gt;收集、decode、返回文本&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
</content:encoded></item><item><title>itertools.count()</title><link>https://lxy-alexander.github.io/blog/posts/python/data_structures/itertoolscount/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/python/data_structures/itertoolscount/</guid><description>itertools.count()</description><pubDate>Fri, 20 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;I. &lt;code&gt;itertools.count()&lt;/code&gt;&lt;/h1&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;&lt;code&gt;itertools.count()&lt;/code&gt; (计数迭代器)&amp;lt;/span&amp;gt; creates an &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;infinite iterator (无限迭代器)&amp;lt;/span&amp;gt; that generates evenly spaced numbers（等间距数字） starting from a specified value. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;1. Basic Usage&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;line-height:1.85&quot;&amp;gt; &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;&lt;code&gt;itertools.count()&lt;/code&gt; (计数迭代器)&amp;lt;/span&amp;gt; generates an infinite arithmetic progression. &amp;lt;span style=&quot;color:#2980B9;font-weight:700&quot;&amp;gt;Use it when you need an endless sequence of numbers&amp;lt;/span&amp;gt; for generating IDs, indices, or combining with other iterators. &amp;lt;span style=&quot;color:#C0392B;font-weight:700&quot;&amp;gt;Warning: Always provide a termination condition when iterating over count()&amp;lt;/span&amp;gt; to avoid infinite loops.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;1) Function Parameters&lt;/h3&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;line-height:1.85&quot;&amp;gt; &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;&lt;code&gt;itertools.count(start=0, step=1)&lt;/code&gt; (起始值, 步长)&amp;lt;/span&amp;gt; accepts two numeric parameters. &amp;lt;span style=&quot;color:#2980B9;font-weight:700&quot;&amp;gt;&lt;code&gt;start&lt;/code&gt; (起始值)&amp;lt;/span&amp;gt; defines the first value in the sequence, while &amp;lt;span style=&quot;color:#2980B9;font-weight:700&quot;&amp;gt;&lt;code&gt;step&lt;/code&gt; (步长)&amp;lt;/span&amp;gt; determines the increment between consecutive values. &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Both parameters can be integers, floats, or any numeric type&amp;lt;/span&amp;gt; that supports addition.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import itertools

# Default: start=0, step=1
counter = itertools.count()
print(next(counter))  # 0
print(next(counter))  # 1

# Custom start and step
counter = itertools.count(start=5, step=3)
print(next(counter))  # 5
print(next(counter))  # 8
print(next(counter))  # 11

# Using float step
counter = itertools.count(start=1.0, step=0.5)
print([next(counter) for _ in range(3)])  # [1.0, 1.5, 2.0]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #C0392B;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;line-height:1.85;font-size:0.80em&quot;&amp;gt;&amp;lt;span style=&quot;color:#C0392B;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Using floating-point steps may lead to &amp;lt;span style=&quot;color:#C0392B;font-weight:700&quot;&amp;gt;precision accumulation errors&amp;lt;/span&amp;gt; over many iterations. Consider using integers and dividing when precise decimal values are needed.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;2. Practical Applications&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;line-height:1.85&quot;&amp;gt; &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;&lt;code&gt;itertools.count()&lt;/code&gt; (计数迭代器)&amp;lt;/span&amp;gt; shines in scenarios requiring automatic indexing or sequence generation. &amp;lt;span style=&quot;color:#2980B9;font-weight:700&quot;&amp;gt;Common use cases include adding line numbers to data, generating unique IDs, and creating paginated sequences.&amp;lt;/span&amp;gt; Its infinite nature makes it particularly useful when the iteration length is determined by another iterable.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;1) Adding Indices to Data&lt;/h3&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;line-height:1.85&quot;&amp;gt; Combine &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;&lt;code&gt;count()&lt;/code&gt;&amp;lt;/span&amp;gt; with &amp;lt;span style=&quot;color:#2980B9;font-weight:700&quot;&amp;gt;&lt;code&gt;zip()&lt;/code&gt;&amp;lt;/span&amp;gt; to &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;automatically number items (自动编号项目)&amp;lt;/span&amp;gt; in any iterable. &amp;lt;span style=&quot;color:#2980B9;font-weight:700&quot;&amp;gt;This pattern is memory-efficient because it generates indices on-the-fly&amp;lt;/span&amp;gt; rather than storing them in a list.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import itertools

# Adding line numbers to text lines
lines = [&quot;First line&quot;, &quot;Second line&quot;, &quot;Third line&quot;]
numbered_lines = zip(itertools.count(1), lines)

for num, line in numbered_lines:
    print(f&quot;{num}: {line}&quot;)
# Output:
# 1: First line
# 2: Second line
# 3: Third line

# Creating dictionary with auto-generated keys
names = [&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;]
user_dict = dict(zip(itertools.count(100), names))
print(user_dict)  # {100: &apos;Alice&apos;, 101: &apos;Bob&apos;, 102: &apos;Charlie&apos;}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) Generating Infinite Sequences&lt;/h3&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;line-height:1.85&quot;&amp;gt; Use &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;&lt;code&gt;count()&lt;/code&gt;&amp;lt;/span&amp;gt; with &amp;lt;span style=&quot;color:#2980B9;font-weight:700&quot;&amp;gt;&lt;code&gt;itertools.islice()&lt;/code&gt;&amp;lt;/span&amp;gt; to &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;generate finite slices of arithmetic sequences (生成有限段的算术序列)&amp;lt;/span&amp;gt;. &amp;lt;span style=&quot;color:#2980B9;font-weight:700&quot;&amp;gt;This approach is ideal for generating test data, mathematical sequences, or pagination&amp;lt;/span&amp;gt; where you need predictable, spaced values.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import itertools

# First 5 multiples of 10
multiples_of_10 = itertools.islice(itertools.count(10, 10), 5)
print(list(multiples_of_10))  # [10, 20, 30, 40, 50]

# Skip first 3, then take 4 numbers
skip_take = itertools.islice(itertools.count(100, -1), 3, 7)
print(list(skip_take))  # [97, 96, 95, 94]

# Generating powers of 2 using indices
powers_of_2 = (2 ** i for i in itertools.islice(itertools.count(), 6))
print(list(powers_of_2))  # [1, 2, 4, 8, 16, 32]
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&amp;lt;span style=&quot;color:#2980B9;font-weight:700&quot;&amp;gt;Pattern (模式)&amp;lt;/span&amp;gt;&lt;/th&gt;
&lt;th&gt;&amp;lt;span style=&quot;color:#2980B9;font-weight:700&quot;&amp;gt;Code Example (代码示例)&amp;lt;/span&amp;gt;&lt;/th&gt;
&lt;th&gt;&amp;lt;span style=&quot;color:#2980B9;font-weight:700&quot;&amp;gt;Use Case (使用场景)&amp;lt;/span&amp;gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;span style=&quot;color:#2980B9;font-weight:700&quot;&amp;gt;1-based indexing&amp;lt;/span&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;zip(itertools.count(1), data)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Displaying numbered lists, generating SQL IDs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Staggered steps&amp;lt;/span&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;itertools.islice(itertools.count(0, 5), 10)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Creating evenly spaced time intervals, pagination offsets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Descending sequences&amp;lt;/span&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;itertools.islice(itertools.count(100, -1), 5)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Generating countdowns, reverse numbering&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;3. Performance Comparison&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;line-height:1.85&quot;&amp;gt; &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;&lt;code&gt;itertools.count()&lt;/code&gt; (计数迭代器)&amp;lt;/span&amp;gt; is implemented in C, making it &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;significantly faster than manual Python counter loops (比手动Python计数循环快得多)&amp;lt;/span&amp;gt;. &amp;lt;span style=&quot;color:#2980B9;font-weight:700&quot;&amp;gt;Choose count() when you need infinite sequences or functional composition&amp;lt;/span&amp;gt;, and use &lt;code&gt;range()&lt;/code&gt; for simple finite sequences.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import itertools
import time

# Manual counter (Python loop)
def manual_counter(n):
    result = []
    i = 0
    while i &amp;lt; n:
        result.append(i)
        i += 1
    return result

# Count with islice (C implementation)
def count_islice(n):
    return list(itertools.islice(itertools.count(), n))

# Range (most optimized for finite sequences)
def range_approach(n):
    return list(range(n))

# n = 10,000,000
# manual_counter: ~0.85s
# count_islice: ~0.42s
# range_approach: ~0.28s
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&amp;lt;span style=&quot;color:#2980B9;font-weight:700&quot;&amp;gt;Method (方法)&amp;lt;/span&amp;gt;&lt;/th&gt;
&lt;th&gt;&amp;lt;span style=&quot;color:#2980B9;font-weight:700&quot;&amp;gt;Implementation (实现)&amp;lt;/span&amp;gt;&lt;/th&gt;
&lt;th&gt;&amp;lt;span style=&quot;color:#2980B9;font-weight:700&quot;&amp;gt;Best For (最佳场景)&amp;lt;/span&amp;gt;&lt;/th&gt;
&lt;th&gt;&amp;lt;span style=&quot;color:#C0392B;font-weight:700&quot;&amp;gt;Limitation (限制)&amp;lt;/span&amp;gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;span style=&quot;color:#2980B9;font-weight:700&quot;&amp;gt;Manual counter&amp;lt;/span&amp;gt;&lt;/td&gt;
&lt;td&gt;Python loop&lt;/td&gt;
&lt;td&gt;Simple educational examples&lt;/td&gt;
&lt;td&gt;Slow for large iterations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;&lt;code&gt;itertools.count()&lt;/code&gt;&amp;lt;/span&amp;gt;&lt;/td&gt;
&lt;td&gt;C implementation&lt;/td&gt;
&lt;td&gt;Infinite sequences, functional pipelines&lt;/td&gt;
&lt;td&gt;Requires &lt;code&gt;islice&lt;/code&gt; for finite use&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;&lt;code&gt;enumerate()&lt;/code&gt;&amp;lt;/span&amp;gt;&lt;/td&gt;
&lt;td&gt;Built-in function&lt;/td&gt;
&lt;td&gt;Indexing existing iterables&lt;/td&gt;
&lt;td&gt;Fixed start=0, no custom step&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;&lt;code&gt;range()&lt;/code&gt;&amp;lt;/span&amp;gt;&lt;/td&gt;
&lt;td&gt;C implementation&lt;/td&gt;
&lt;td&gt;Simple finite sequences&lt;/td&gt;
&lt;td&gt;Cannot be infinite&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt; &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;&lt;code&gt;itertools.count()&lt;/code&gt;&amp;lt;/span&amp;gt; is your &amp;lt;span style=&quot;color:#2980B9;font-weight:700&quot;&amp;gt;go-to C-level tool for infinite arithmetic sequences&amp;lt;/span&amp;gt; — pair it with &amp;lt;span style=&quot;color:#2980B9;font-weight:700&quot;&amp;gt;&lt;code&gt;zip()&lt;/code&gt; for auto-indexing&amp;lt;/span&amp;gt; or &amp;lt;span style=&quot;color:#2980B9;font-weight:700&quot;&amp;gt;&lt;code&gt;islice()&lt;/code&gt; for finite slices&amp;lt;/span&amp;gt;, &amp;lt;span style=&quot;color:#C0392B;font-weight:700&quot;&amp;gt;but never iterate directly without a termination condition&amp;lt;/span&amp;gt;.&amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>dataclass</title><link>https://lxy-alexander.github.io/blog/posts/python/oop/dataclass/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/python/oop/dataclass/</guid><description>dataclass</description><pubDate>Thu, 19 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;I. Dataclass&lt;/h1&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt;
Python dataclass is a &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;decorator (装饰器)&amp;lt;/span&amp;gt; that automatically generates special methods like &amp;lt;code&amp;gt;&lt;strong&gt;init&lt;/strong&gt;&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;&lt;strong&gt;repr&lt;/strong&gt;&amp;lt;/code&amp;gt; for classes primarily used to &amp;lt;span style=&quot;color:#2980B9;font-weight:700&quot;&amp;gt;store data&amp;lt;/span&amp;gt;. It reduces boilerplate code by letting you &amp;lt;span style=&quot;color:#2980B9;font-weight:700&quot;&amp;gt;declare fields as class variables&amp;lt;/span&amp;gt; with type annotations. The dataclass makes your code more &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;readable and maintainable (可读性和可维护性)&amp;lt;/span&amp;gt; by eliminating repetitive method definitions.
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;1. Basic Dataclass Definition&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;line-height:1.85&quot;&amp;gt;
The &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;@dataclass decorator (装饰器)&amp;lt;/span&amp;gt; automatically adds &amp;lt;span style=&quot;color:#2980B9;font-weight:700&quot;&amp;gt;&lt;strong&gt;init&lt;/strong&gt;&amp;lt;/span&amp;gt;, &amp;lt;span style=&quot;color:#2980B9;font-weight:700&quot;&amp;gt;&lt;strong&gt;repr&lt;/strong&gt;&amp;lt;/span&amp;gt;, and &amp;lt;span style=&quot;color:#2980B9;font-weight:700&quot;&amp;gt;&lt;strong&gt;eq&lt;/strong&gt;&amp;lt;/span&amp;gt; methods based on the class variables you define with &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;type hints (类型提示)&amp;lt;/span&amp;gt;. Use this when you need a simple container for data without writing repetitive constructor code.
&amp;lt;/div&amp;gt;
The &lt;code&gt;@dataclass&lt;/code&gt; decorator auto-generates:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;&lt;strong&gt;init&lt;/strong&gt;(self, x, y)&amp;lt;/code&amp;gt; — constructor&lt;/li&gt;
&lt;li&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;&lt;strong&gt;repr&lt;/strong&gt;&amp;lt;/code&amp;gt; — pretty string representation&lt;/li&gt;
&lt;li&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;&lt;strong&gt;eq&lt;/strong&gt;&amp;lt;/code&amp;gt; — equality comparison&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;1) Basic Implementation&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from dataclasses import dataclass

@dataclass
class Person:
    name: str
    age: int
    email: str = &quot;unknown@email.com&quot;  # Default value

# Usage example
person1 = Person(&quot;Alice&quot;, 25, &quot;alice@email.com&quot;)
person2 = Person(&quot;Bob&quot;, 30)  # Uses default email

print(person1)  # Automatically generated __repr__
print(person1 == person2)  # Automatically generated __eq__
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #C0392B;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;line-height:1.85;font-size:0.80em&quot;&amp;gt;&amp;lt;span style=&quot;color:#C0392B;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Fields without default values &amp;lt;span style=&quot;color:#C0392B;font-weight:700&quot;&amp;gt;must come before&amp;lt;/span&amp;gt; fields with default values, otherwise Python raises a &amp;lt;span style=&quot;color:#C0392B;font-weight:700&quot;&amp;gt;SyntaxError (语法错误)&amp;lt;/span&amp;gt;.
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;2. Field Customization&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;line-height:1.85&quot;&amp;gt;
The &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;field() function (字段函数)&amp;lt;/span&amp;gt; provides &amp;lt;span style=&quot;color:#2980B9;font-weight:700&quot;&amp;gt;fine-grained control&amp;lt;/span&amp;gt; over individual dataclass fields, allowing you to set &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;default factories (默认工厂)&amp;lt;/span&amp;gt;, exclude fields from comparisons, or mark fields as &amp;lt;span style=&quot;color:#2980B9;font-weight:700&quot;&amp;gt;private (私有)&amp;lt;/span&amp;gt;.
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;1) Using field() with Parameters&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from dataclasses import dataclass, field
import random
from typing import List

@dataclass
class Student:
    name: str
    student_id: int = field(init=False)  # Not in __init__
    grades: List[int] = field(default_factory=list)  # Mutable default
    _internal_id: int = field(default=0, repr=False)  # Hidden in __repr__
    
    def __post_init__(self):
        # Initialize after dataclass generation
        self.student_id = random.randint(1000, 9999)
        self._internal_id = hash(self.name)

# Usage example
student = Student(&quot;Alice&quot;)
student.grades.append(95)  # Works with mutable default
print(student)  # Shows name and grades, but not _internal_id
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #C0392B;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;line-height:1.85;font-size:0.80em&quot;&amp;gt;&amp;lt;span style=&quot;color:#C0392B;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Always use &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;default_factory (默认工厂)&amp;lt;/span&amp;gt; for mutable types like lists or dictionaries. Using &amp;lt;code&amp;gt;grades: List[int] = []&amp;lt;/code&amp;gt; would cause all instances to &amp;lt;span style=&quot;color:#C0392B;font-weight:700&quot;&amp;gt;share the same list&amp;lt;/span&amp;gt;.
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;3. Dataclass Parameters&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;line-height:1.85&quot;&amp;gt;
The &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;@dataclass decorator&amp;lt;/span&amp;gt; accepts parameters that &amp;lt;span style=&quot;color:#2980B9;font-weight:700&quot;&amp;gt;control which methods are generated&amp;lt;/span&amp;gt;. Use &amp;lt;span style=&quot;color:#2980B9;font-weight:700&quot;&amp;gt;frozen=True&amp;lt;/span&amp;gt; for immutable objects, &amp;lt;span style=&quot;color:#2980B9;font-weight:700&quot;&amp;gt;order=True&amp;lt;/span&amp;gt; for sorting capabilities, and &amp;lt;span style=&quot;color:#2980B9;font-weight:700&quot;&amp;gt;kw_only=True&amp;lt;/span&amp;gt; to enforce keyword arguments.
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;1) Configuration Options&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from dataclasses import dataclass

@dataclass(frozen=True, order=True)
class Point:
    x: int
    y: int

@dataclass(kw_only=True)  # Python 3.10+
class Configuration:
    host: str
    port: int = 8080

# Usage examples
p1 = Point(1, 2)
p2 = Point(1, 3)
# p1.x = 5  # This would raise FrozenInstanceError
print(p1 &amp;lt; p2)  # Works because order=True

# Must use keyword arguments
config = Configuration(host=&quot;localhost&quot;, port=3000)
# config = Configuration(&quot;localhost&quot;, 3000)  # This would fail
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #C0392B;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;line-height:1.85;font-size:0.80em&quot;&amp;gt;&amp;lt;span style=&quot;color:#C0392B;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; When using &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;frozen=True&amp;lt;/span&amp;gt;, the dataclass becomes &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;immutable (不可变的)&amp;lt;/span&amp;gt; — you cannot modify attributes after creation. This is ideal for &amp;lt;span style=&quot;color:#2980B9;font-weight:700&quot;&amp;gt;configuration objects&amp;lt;/span&amp;gt; or &amp;lt;span style=&quot;color:#2980B9;font-weight:700&quot;&amp;gt;value objects&amp;lt;/span&amp;gt;.
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;4. Inheritance with Dataclasses&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;line-height:1.85&quot;&amp;gt;
Dataclasses &amp;lt;span style=&quot;color:#2980B9;font-weight:700&quot;&amp;gt;support inheritance (继承)&amp;lt;/span&amp;gt;, with fields from parent classes being combined with child class fields. Use this when you need to &amp;lt;span style=&quot;color:#2980B9;font-weight:700&quot;&amp;gt;extend data containers&amp;lt;/span&amp;gt; while maintaining the automatic method generation.
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;1) Extending Dataclasses&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from dataclasses import dataclass

@dataclass
class Vehicle:
    brand: str
    model: str
    year: int

@dataclass
class Car(Vehicle):
    doors: int
    electric: bool = False

# Usage example
my_car = Car(&quot;Tesla&quot;, &quot;Model 3&quot;, 2023, doors=4, electric=True)
print(my_car)  # Includes all fields from both classes
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #C0392B;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;line-height:1.85;font-size:0.80em&quot;&amp;gt;&amp;lt;span style=&quot;color:#C0392B;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; When inheriting, the &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;field order matters&amp;lt;/span&amp;gt; — child class fields are appended after parent fields. All fields without defaults in the parent must come before child fields with defaults.
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;5. Comparison Table: Regular Class vs Dataclass&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;line-height:1.85&quot;&amp;gt;
This table compares the &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;boilerplate code (样板代码)&amp;lt;/span&amp;gt; required for a simple data container using a regular class versus a dataclass.
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Regular Class&lt;/th&gt;
&lt;th&gt;Dataclass&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;span style=&quot;color:#2980B9;font-weight:700&quot;&amp;gt;Lines of Code&amp;lt;/span&amp;gt;&lt;/td&gt;
&lt;td&gt;~10-15 lines&lt;/td&gt;
&lt;td&gt;~3-5 lines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;&lt;strong&gt;init&lt;/strong&gt; method&amp;lt;/span&amp;gt;&lt;/td&gt;
&lt;td&gt;Manual implementation&lt;/td&gt;
&lt;td&gt;Auto-generated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;&lt;strong&gt;repr&lt;/strong&gt; method&amp;lt;/span&amp;gt;&lt;/td&gt;
&lt;td&gt;Manual implementation&lt;/td&gt;
&lt;td&gt;Auto-generated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;&lt;strong&gt;eq&lt;/strong&gt; method&amp;lt;/span&amp;gt;&lt;/td&gt;
&lt;td&gt;Manual implementation&lt;/td&gt;
&lt;td&gt;Auto-generated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;span style=&quot;color:#2980B9;font-weight:700&quot;&amp;gt;Type hints&amp;lt;/span&amp;gt;&lt;/td&gt;
&lt;td&gt;Optional in body&lt;/td&gt;
&lt;td&gt;Required for fields&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;span style=&quot;color:#2980B9;font-weight:700&quot;&amp;gt;Default values&amp;lt;/span&amp;gt;&lt;/td&gt;
&lt;td&gt;In &lt;strong&gt;init&lt;/strong&gt; method&lt;/td&gt;
&lt;td&gt;Direct field assignment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;span style=&quot;color:#C0392B;font-weight:700&quot;&amp;gt;Mutable defaults&amp;lt;/span&amp;gt;&lt;/td&gt;
&lt;td&gt;Safe with proper code&lt;/td&gt;
&lt;td&gt;Must use default_factory&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;1) Code Comparison Example&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# Regular class - 15 lines
class RegularPerson:
    def __init__(self, name: str, age: int, email: str = &quot;unknown&quot;):
        self.name = name
        self.age = age
        self.email = email
    
    def __repr__(self):
        return f&quot;RegularPerson(name=&apos;{self.name}&apos;, age={self.age}, email=&apos;{self.email}&apos;)&quot;
    
    def __eq__(self, other):
        if not isinstance(other, RegularPerson):
            return False
        return (self.name, self.age, self.email) == (other.name, other.age, other.email)

# Dataclass - 4 lines
@dataclass
class DataclassPerson:
    name: str
    age: int
    email: str = &quot;unknown&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Python dataclasses automatically generate &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;&lt;strong&gt;init&lt;/strong&gt;, &lt;strong&gt;repr&lt;/strong&gt;, and &lt;strong&gt;eq&lt;/strong&gt;&amp;lt;/span&amp;gt; from type-annotated fields, eliminating boilerplate code for simple data containers.
&amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Git LFS (Large File Storage)</title><link>https://lxy-alexander.github.io/blog/posts/tools/git-lfs-large-file-storage/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/tools/git-lfs-large-file-storage/</guid><description>Git LFS (Large File Storage)</description><pubDate>Sat, 14 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;&lt;strong&gt;I. &lt;code&gt;git lfs install&lt;/code&gt; (Git LFS 初始化命令)&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Git LFS (Large File Storage，大文件存储)&amp;lt;/span&amp;gt; is an extension of &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Git (版本控制系统)&amp;lt;/span&amp;gt; used to manage large files such as datasets, models, or binaries.   The command &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;git lfs install&amp;lt;/code&amp;gt; initializes &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Git LFS&amp;lt;/span&amp;gt; on your machine by configuring Git hooks and global settings.   &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;In simple words&amp;lt;/span&amp;gt;, it prepares Git so that large files will automatically be handled by the LFS system instead of normal Git storage. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;&amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;1.&amp;lt;/span&amp;gt; What &lt;code&gt;git lfs install&lt;/code&gt; Does (命令作用)&lt;/h2&gt;
&lt;p&gt;Running&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git lfs install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;performs several setup steps:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Explanation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Install hooks&lt;/td&gt;
&lt;td&gt;Adds &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Git Hooks (Git钩子)&amp;lt;/span&amp;gt; such as &lt;code&gt;pre-push&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Configure Git&lt;/td&gt;
&lt;td&gt;Enables &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;LFS Filters (LFS过滤器)&amp;lt;/span&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Activate LFS&lt;/td&gt;
&lt;td&gt;Allows Git to replace large files with &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;pointer files (指针文件)&amp;lt;/span&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;After installation, Git will automatically:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;detect large files&lt;/li&gt;
&lt;li&gt;store them in &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;LFS storage (LFS存储)&amp;lt;/span&amp;gt;&lt;/li&gt;
&lt;li&gt;keep only small pointer references in the repository&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;&amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;2.&amp;lt;/span&amp;gt; How Git LFS Works (工作原理)&lt;/h2&gt;
&lt;p&gt;Normal Git workflow:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;file → git repository
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Git LFS workflow:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;large file → LFS server
pointer file → git repository
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example pointer file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;version https://git-lfs.github.com/spec/v1
oid sha256:xxxx
size 104857600
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt; &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; A &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;pointer file (指针文件)&amp;lt;/span&amp;gt; is a small text file that references the real large file stored in the LFS server. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;&amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;3.&amp;lt;/span&amp;gt; Typical Usage Workflow (常见使用流程)&lt;/h2&gt;
&lt;h3&gt;1) Install Git LFS&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;git lfs install
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) Track large files&lt;/h3&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git lfs track &quot;*.pt&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This tells Git to manage &lt;code&gt;.pt&lt;/code&gt; files using LFS.&lt;/p&gt;
&lt;h3&gt;3) Commit tracking rules&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;git add .gitattributes
git commit -m &quot;track model files with LFS&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4) Add large file&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;git add model.pt
git commit -m &quot;add model&quot;
git push
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The file will be stored in &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;LFS storage (LFS服务器)&amp;lt;/span&amp;gt;.&lt;/p&gt;
&lt;h2&gt;&amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;4.&amp;lt;/span&amp;gt; When You Need &lt;code&gt;git lfs&lt;/code&gt; (什么时候需要)&lt;/h2&gt;
&lt;p&gt;You should use Git LFS when managing:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File Type&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Machine learning models&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.pt&lt;/code&gt;, &lt;code&gt;.pth&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Datasets&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.csv&lt;/code&gt;, &lt;code&gt;.parquet&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Game assets&lt;/td&gt;
&lt;td&gt;textures, audio&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Large binaries&lt;/td&gt;
&lt;td&gt;compiled files&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Warning:&amp;lt;/span&amp;gt;
Normal Git performs poorly with very large files because the entire file history is stored inside the repository.&lt;/p&gt;
&lt;h2&gt;&amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;5.&amp;lt;/span&amp;gt; Verify Installation (验证安装)&lt;/h2&gt;
&lt;p&gt;Check whether Git LFS is installed:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git lfs version
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example output:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git-lfs/3.4.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check tracked files:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git lfs ls-files
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;git lfs install&amp;lt;/code&amp;gt; initializes &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Git LFS (Git大文件存储)&amp;lt;/span&amp;gt; by configuring Git hooks and filters so that large files are stored in &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;LFS storage&amp;lt;/span&amp;gt; instead of the Git repository. &amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>NumPy Array Creation</title><link>https://lxy-alexander.github.io/blog/posts/numpy/api/01numpy-array-creation/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/numpy/api/01numpy-array-creation/</guid><description>NumPy Array Creation</description><pubDate>Thu, 12 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;I. NumPy Array Creation (数组创建)&lt;/h1&gt;
&lt;h2&gt;1. Basic Array Creation (基本数组创建)&lt;/h2&gt;
&lt;h3&gt;1) &lt;code&gt;np.array()&lt;/code&gt; — Create from List (从列表创建)&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;np.array()&lt;/code&gt; function converts a Python List (列表) or Tuple (元组) into an ndarray (多维数组).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import numpy as np

# 1D Array (一维数组)
a = np.array([1, 2, 3, 4, 5])
print(a)          # [1 2 3 4 5]
print(a.dtype)    # int64

# 2D Array (二维数组)
b = np.array([[1, 2, 3], [4, 5, 6]])
print(b)
# [[1 2 3]
#  [4 5 6]]
print(b.shape)    # (2, 3)

# Specify dtype (指定数据类型)
c = np.array([1, 2, 3], dtype=np.float32)
print(c)          # [1. 2. 3.]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) &lt;code&gt;np.zeros()&lt;/code&gt; / &lt;code&gt;np.ones()&lt;/code&gt; / &lt;code&gt;np.full()&lt;/code&gt; — Constant Arrays (常量数组)&lt;/h3&gt;
&lt;p&gt;These functions create arrays filled with a Constant Value (常量值).&lt;/p&gt;
&lt;p&gt;python&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import numpy as np

# All zeros (全零数组)
a = np.zeros((2, 3))
print(a)
# [[0. 0. 0.]
#  [0. 0. 0.]]

# All ones (全一数组)
b = np.ones((3, 2), dtype=int)
print(b)
# [[1 1]
#  [1 1]
#  [1 1]]

# Fill with a specific value (填充指定值)
c = np.full((2, 2), 7)
print(c)
# [[7 7]
#  [7 7]]

# Like versions: same shape as another array (与另一个数组形状相同)
d = np.zeros_like(b)
print(d)
# [[0 0]
#  [0 0]]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) &lt;code&gt;np.arange()&lt;/code&gt; / &lt;code&gt;np.linspace()&lt;/code&gt; — Sequence Arrays (序列数组)&lt;/h3&gt;
&lt;p&gt;Use these to generate Evenly Spaced (等间隔) values.&lt;/p&gt;
&lt;p&gt;python&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import numpy as np

# arange: similar to range() but returns ndarray
# arange(start, stop, step)
a = np.arange(0, 10, 2)
print(a)    # [0 2 4 6 8]

# Float step (浮点步长)
b = np.arange(0, 1, 0.3)
print(b)    # [0.  0.3 0.6 0.9]

# linspace: evenly spaced over an interval (等分区间)
# linspace(start, stop, num)
c = np.linspace(0, 1, 5)
print(c)    # [0.   0.25 0.5  0.75 1.  ]

# logspace: logarithmically spaced (对数等间隔)
d = np.logspace(0, 3, 4)   # 10^0 to 10^3
print(d)    # [   1.   10.  100. 1000.]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4) &lt;code&gt;np.eye()&lt;/code&gt; / &lt;code&gt;np.identity()&lt;/code&gt; — Identity Matrix (单位矩阵)&lt;/h3&gt;
&lt;p&gt;python&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import numpy as np

# Identity Matrix (单位矩阵)
a = np.eye(3)
print(a)
# [[1. 0. 0.]
#  [0. 1. 0.]
#  [0. 0. 1.]]

# Offset diagonal (偏移对角线)
b = np.eye(3, k=1)
print(b)
# [[0. 1. 0.]
#  [0. 0. 1.]
#  [0. 0. 0.]]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5) &lt;code&gt;np.empty()&lt;/code&gt; / &lt;code&gt;np.diag()&lt;/code&gt; — Other Creation Methods (其他创建方式)&lt;/h3&gt;
&lt;p&gt;python&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import numpy as np

# empty: uninitialized (未初始化, values are random)
a = np.empty((2, 2))
print(a)    # random values, fast allocation

# diag: create diagonal matrix or extract diagonal (对角矩阵)
b = np.diag([1, 2, 3])
print(b)
# [[1 0 0]
#  [0 2 0]
#  [0 0 3]]

# Extract diagonal from a matrix (提取对角线)
c = np.array([[1, 2], [3, 4]])
print(np.diag(c))    # [1 4]
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>NumPy Array Operations</title><link>https://lxy-alexander.github.io/blog/posts/numpy/api/02numpy-array-operations/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/numpy/api/02numpy-array-operations/</guid><description>II. NumPy Array Operations</description><pubDate>Thu, 12 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;&lt;strong&gt;II. NumPy Array Operations (数组操作)&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Array operations (数组操作) let you change an array&apos;s &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;shape (形状)&amp;lt;/span&amp;gt;, &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;dimensions (维度)&amp;lt;/span&amp;gt;, and structure — without touching the underlying data values. These are the core tools for preparing data before computation. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;1. &lt;code&gt;reshape()&lt;/code&gt; — Change Shape Without Copying (改变形状)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Returns a &lt;strong&gt;new view&lt;/strong&gt; of the same data with a different shape. Total elements must stay the same.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import numpy as np

a = np.arange(12)          # [0, 1, 2, ..., 11]
b = a.reshape(3, 4)        # 3 rows × 4 cols
c = a.reshape(2, 3, 2)     # 3-D: 2×3×2

# Use -1 to let NumPy infer one dimension
d = a.reshape(4, -1)       # → shape (4, 3)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;reshape&amp;lt;/code&amp;gt; returns a &amp;lt;strong&amp;gt;view&amp;lt;/strong&amp;gt; — modifying &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;b&amp;lt;/code&amp;gt; will also change &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;a&amp;lt;/code&amp;gt;. Use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;.copy()&amp;lt;/code&amp;gt; if you need independence.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. &lt;code&gt;resize()&lt;/code&gt; — Reshape In-Place (原地改变形状)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Like &lt;code&gt;reshape&lt;/code&gt;, but modifies the array &amp;lt;strong&amp;gt;in-place&amp;lt;/strong&amp;gt; and can change total element count by repeating or truncating data.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a = np.array([1, 2, 3, 4])
a.resize(2, 3)   # repeats values to fill: [[1,2,3],[4,1,2]]
print(a)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;resize() modifies the original array permanently — use with caution.&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. &lt;code&gt;flatten()&lt;/code&gt; — Collapse to 1-D (展平为一维)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Always returns a &lt;strong&gt;copy&lt;/strong&gt; as a flat 1-D array.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;b = np.array([[1, 2], [3, 4]])
print(b.flatten())    # [1 2 3 4]
print(b.ravel())      # [1 2 3 4] — same result, but returns a VIEW
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Returns&lt;/th&gt;
&lt;th&gt;Modifies original?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;flatten()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Copy&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ravel()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;View (usually)&lt;/td&gt;
&lt;td&gt;Yes (if view)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;4. &lt;code&gt;transpose()&lt;/code&gt; — Swap Axes (转置)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Swap rows and columns (or any axes in higher dimensions). Shortcut: &lt;code&gt;.T&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a = np.array([[1, 2, 3],
              [4, 5, 6]])   # shape (2, 3)

print(a.T)                  # shape (3, 2)
print(a.transpose())        # same as a.T

# For 3-D: specify axis order
c = np.ones((2, 3, 4))
c.transpose(2, 0, 1)        # new shape: (4, 2, 3)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;5. &lt;code&gt;concatenate()&lt;/code&gt; — Join Arrays (拼接数组)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Join a sequence of arrays along an existing axis (轴).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6]])

# axis=0: stack rows (垂直拼接)
np.concatenate([a, b], axis=0)   # shape (3, 2)

# axis=1: stack columns (水平拼接)
c = np.array([[7], [8]])
np.concatenate([a, c], axis=1)   # shape (2, 3)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Convenience wrappers: &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;np.vstack()&amp;lt;/code&amp;gt; (vertical / axis=0) and &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;np.hstack()&amp;lt;/code&amp;gt; (horizontal / axis=1).&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;6. &lt;code&gt;split()&lt;/code&gt; — Divide an Array (分割数组)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Split an array into multiple sub-arrays along an axis.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a = np.arange(12).reshape(4, 3)

# Split into 2 equal halves along rows (axis=0)
parts = np.split(a, 2, axis=0)   # [shape(2,3), shape(2,3)]

# Split at specific indices
np.split(a, [1, 3], axis=0)      # rows 0, rows 1–2, rows 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;np.vsplit(a, n)&amp;lt;/code&amp;gt; and &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;np.hsplit(a, n)&amp;lt;/code&amp;gt; are shorthand for axis=0 and axis=1 splits.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;7. Quick Comparison Table&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function (函数)&lt;/th&gt;
&lt;th&gt;In-place?&lt;/th&gt;
&lt;th&gt;Returns&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;reshape(shape)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;View (same data)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;resize(shape)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Yes&amp;lt;/span&amp;gt;&lt;/td&gt;
&lt;td&gt;None (modifies array)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;flatten()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Copy, 1-D&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ravel()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;View, 1-D&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;transpose()&lt;/code&gt; / &lt;code&gt;.T&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;View&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;concatenate()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;New array&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;split()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;List of views&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt; Most shape-change operations return &amp;lt;strong&amp;gt;views&amp;lt;/strong&amp;gt; (not copies) — changes propagate back to the original array, so use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;.copy()&amp;lt;/code&amp;gt; when you need an independent result.&amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>NumPy Math Operations</title><link>https://lxy-alexander.github.io/blog/posts/numpy/api/03numpy-math-operations/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/numpy/api/03numpy-math-operations/</guid><description>NumPy Math Operations</description><pubDate>Thu, 12 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;&lt;strong&gt;III. NumPy Math Operations (数学运算)&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; NumPy math functions are &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;element-wise (逐元素)&amp;lt;/span&amp;gt; — they operate on each element independently and return a new array of the same shape. They are implemented in C, making them far faster than Python loops. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;1. Basic Arithmetic (四则运算)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Operator symbols (&lt;code&gt;+&lt;/code&gt;, &lt;code&gt;-&lt;/code&gt;, &lt;code&gt;*&lt;/code&gt;, &lt;code&gt;/&lt;/code&gt;) and their functional equivalents work element-by-element.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import numpy as np

a = np.array([10, 20, 30])
b = np.array([1,  2,  3])

np.add(a, b)        # [11 22 33]  → same as a + b
np.subtract(a, b)   # [ 9 18 27]  → same as a - b
np.multiply(a, b)   # [10 40 90]  → same as a * b
np.divide(a, b)     # [10. 10. 10.] → same as a / b
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Broadcasting (广播机制) allows operations between arrays of different shapes. E.g., &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;a + 5&amp;lt;/code&amp;gt; adds 5 to every element.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. &lt;code&gt;power()&lt;/code&gt; — Exponentiation (幂运算)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Raise each element to a given power.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a = np.array([2, 3, 4])

np.power(a, 3)    # [ 8 27 64]  → a³
a ** 2            # [ 4  9 16]  → shorthand

np.sqrt(a)        # [1.41 1.73 2.0] → square root (平方根)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;3. &lt;code&gt;exp()&lt;/code&gt; / &lt;code&gt;log()&lt;/code&gt; — Exponential &amp;amp; Logarithm (指数与对数)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Apply the natural exponential $e^x$ or logarithm $\ln(x)$ element-wise.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a = np.array([0, 1, 2])

np.exp(a)         # [1.    2.718 7.389]  → e^x
np.log(a + 1)     # [0.    0.693 1.099]  → ln(x)
np.log2(np.array([1, 2, 8]))   # [0. 1. 3.]
np.log10(np.array([1, 10, 100]))  # [0. 1. 2.]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;log(0) returns -inf and raises a warning — always check for zero values before applying log.&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4. &lt;code&gt;sin()&lt;/code&gt; / &lt;code&gt;cos()&lt;/code&gt; / &lt;code&gt;tan()&lt;/code&gt; — Trigonometry (三角函数)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Input angles must be in &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;radians (弧度)&amp;lt;/span&amp;gt;, not degrees.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;angles = np.array([0, np.pi/6, np.pi/4, np.pi/2])

np.sin(angles)   # [0.    0.5   0.707 1.   ]
np.cos(angles)   # [1.    0.866 0.707 0.   ]
np.tan(angles)   # [0.    0.577 1.    inf  ]

# Convert degrees to radians (角度转弧度)
deg = np.array([0, 30, 45, 90])
np.sin(np.deg2rad(deg))  # same result
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;5. Rounding Functions (取整函数)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Control how floating-point values are rounded.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a = np.array([1.4, 1.5, 2.6, -1.7])

np.round(a)    # [ 1.  2.  3. -2.]  → nearest even
np.floor(a)    # [ 1.  1.  2. -2.]  → round down (向下取整)
np.ceil(a)     # [ 2.  2.  3. -1.]  → round up (向上取整)
np.trunc(a)    # [ 1.  1.  2. -1.]  → truncate toward zero (截断)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;6. Absolute Value &amp;amp; Sign (绝对值与符号)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;a = np.array([-3, -1, 0, 2, 5])

np.abs(a)     # [3 1 0 2 5]
np.sign(a)    # [-1 -1  0  1  1]
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;7. Quick Comparison Table&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function (函数)&lt;/th&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Example Input → Output&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;add / subtract&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;±&lt;/code&gt; element-wise&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[1,2] + [3,4]&lt;/code&gt; → &lt;code&gt;[4,6]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;multiply / divide&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;×÷&lt;/code&gt; element-wise&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[2,4] * [3,2]&lt;/code&gt; → &lt;code&gt;[6,8]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;power(a, n)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;$a^n$&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[2,3]^2&lt;/code&gt; → &lt;code&gt;[4,9]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sqrt(a)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;$\sqrt{a}$&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[4,9]&lt;/code&gt; → &lt;code&gt;[2,3]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;exp(a)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;$e^a$&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[0,1]&lt;/code&gt; → &lt;code&gt;[1, 2.718]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;log(a)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;$\ln(a)$&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[1, e]&lt;/code&gt; → &lt;code&gt;[0, 1]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sin / cos / tan&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Trig (radians)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[0, π/2]&lt;/code&gt; → &lt;code&gt;[0, 1]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt; All NumPy math functions are element-wise and support broadcasting — they are always faster than Python loops; just watch out for &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;log(0)&amp;lt;/code&amp;gt; and &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;tan(π/2)&amp;lt;/code&amp;gt; edge cases.&amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>NumPy Linear Algebra</title><link>https://lxy-alexander.github.io/blog/posts/numpy/api/05numpy-linear-algebra/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/numpy/api/05numpy-linear-algebra/</guid><description>NumPy Linear Algebra</description><pubDate>Thu, 12 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;&lt;strong&gt;V. NumPy Linear Algebra (线性代数)&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; The &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;numpy.linalg&amp;lt;/code&amp;gt; module provides standard linear algebra (线性代数) operations on 2-D arrays treated as matrices (矩阵). These are essential for machine learning, physics simulations, and engineering calculations. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;1. Matrix Multiplication (矩阵乘法)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Use &lt;code&gt;@&lt;/code&gt; or &lt;code&gt;np.dot()&lt;/code&gt; for matrix multiplication — NOT &lt;code&gt;*&lt;/code&gt; (which is element-wise).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import numpy as np

A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])

A @ B          # Matrix multiply (矩阵乘法)
np.dot(A, B)   # Same result: [[19 22] [43 50]]

A * B          # Element-wise multiply (逐元素乘法) — different!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Always use @ or np.dot() for matrix multiplication. Using * gives element-wise results, which is a common mistake.&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. &lt;code&gt;linalg.inv()&lt;/code&gt; — Inverse Matrix (逆矩阵)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Find matrix $A^{-1}$ such that $A \cdot A^{-1} = I$ (identity matrix).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;A = np.array([[1, 2], [3, 4]])

A_inv = np.linalg.inv(A)
# [[-2.   1. ]
#  [ 1.5 -0.5]]

# Verify: A @ A_inv ≈ Identity matrix (单位矩阵)
np.round(A @ A_inv)   # [[1. 0.] [0. 1.]]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Only square, non-singular matrices (非奇异矩阵) are invertible. A singular matrix (奇异矩阵) will raise LinAlgError.&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. &lt;code&gt;linalg.det()&lt;/code&gt; — Determinant (行列式)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; A scalar value describing matrix properties. If &lt;code&gt;det = 0&lt;/code&gt;, the matrix is singular (not invertible).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;A = np.array([[1, 2], [3, 4]])
np.linalg.det(A)   # -2.0

B = np.array([[1, 2], [2, 4]])  # rows are proportional
np.linalg.det(B)   # 0.0  → singular matrix!
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;4. &lt;code&gt;linalg.eig()&lt;/code&gt; — Eigenvalues &amp;amp; Eigenvectors (特征值与特征向量)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Find $\lambda$ and $v$ such that $A \cdot v = \lambda \cdot v$. Core of PCA (主成分分析) and many ML algorithms.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;A = np.array([[4, 1], [2, 3]])

eigenvalues, eigenvectors = np.linalg.eig(A)
# eigenvalues:  [5. 2.]
# eigenvectors: columns are the eigenvectors

print(eigenvalues)    # [5. 2.]
print(eigenvectors)   # [[0.707 -0.447]
                      #  [0.707  0.894]]
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;5. &lt;code&gt;linalg.svd()&lt;/code&gt; — Singular Value Decomposition (奇异值分解)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Decompose any matrix $M = U \Sigma V^T$. Used in data compression, image processing, and recommendation systems.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;A = np.array([[1, 2], [3, 4], [5, 6]])

U, S, Vt = np.linalg.svd(A)
# U: left singular vectors (shape: 3×3)
# S: singular values (shape: 2,) — diagonal of Σ
# Vt: right singular vectors transposed (shape: 2×2)

print(S)   # [9.525 0.514]
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;6. &lt;code&gt;linalg.solve()&lt;/code&gt; — Solve Linear Equations (线性方程组)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Solve $Ax = b$ for $x$. More numerically stable than computing $A^{-1} \cdot b$.&lt;/p&gt;
&lt;p&gt;$$Ax = b \quad \Rightarrow \quad x = \text{linalg.solve}(A, b)$$&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Solve: 2x + y = 5
#        x + 3y = 10
A = np.array([[2, 1], [1, 3]])
b = np.array([5, 10])

x = np.linalg.solve(A, b)
# [1. 3.] → x=1, y=3

# Verify
np.allclose(A @ x, b)   # True
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Always prefer linalg.solve() over inv(A) @ b for numerical stability and performance.&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;7. Quick Comparison Table&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function (函数)&lt;/th&gt;
&lt;th&gt;Mathematical Operation&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@&lt;/code&gt; / &lt;code&gt;dot(A,B)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;$AB$&lt;/td&gt;
&lt;td&gt;Matrix multiply&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;linalg.inv(A)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;$A^{-1}$&lt;/td&gt;
&lt;td&gt;Invert matrix&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;linalg.det(A)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;$\det(A)$&lt;/td&gt;
&lt;td&gt;Check singularity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;linalg.eig(A)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;$Av = \lambda v$&lt;/td&gt;
&lt;td&gt;PCA, stability&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;linalg.svd(A)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;$U\Sigma V^T$&lt;/td&gt;
&lt;td&gt;Compression, rank&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;linalg.solve(A,b)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;$Ax = b$&lt;/td&gt;
&lt;td&gt;Linear systems&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt; Use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;@&amp;lt;/code&amp;gt; for matrix multiplication, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;linalg.solve()&amp;lt;/code&amp;gt; instead of &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;inv()&amp;lt;/code&amp;gt; for equation solving, and check &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;det()&amp;lt;/code&amp;gt; before inverting.&amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>NumPy Random Sampling</title><link>https://lxy-alexander.github.io/blog/posts/numpy/api/06numpy-random-sampling/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/numpy/api/06numpy-random-sampling/</guid><description>NumPy Random Sampling</description><pubDate>Thu, 12 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;&lt;strong&gt;VI. NumPy Random Sampling (随机抽样)&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;numpy.random&amp;lt;/code&amp;gt; generates pseudo-random numbers (伪随机数) following various distributions (分布). Always set a &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;seed (随机种子)&amp;lt;/span&amp;gt; for reproducible experiments. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;1. Setting the Seed (设置随机种子)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import numpy as np

np.random.seed(42)   # All subsequent random calls are reproducible
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; The modern recommended way is &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;rng = np.random.default_rng(42)&amp;lt;/code&amp;gt;, then use &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;rng.random()&amp;lt;/code&amp;gt; etc. (NumPy 1.17+).&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. &lt;code&gt;random.rand()&lt;/code&gt; — Uniform Distribution (均匀分布)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Floats uniformly distributed in &lt;strong&gt;[0, 1)&lt;/strong&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;np.random.rand(3)        # 1-D: 3 random floats
np.random.rand(2, 4)     # 2-D: shape (2, 4)

# Scale to [a, b]: a + (b - a) * rand()
np.random.rand(5) * 10   # uniform in [0, 10)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;3. &lt;code&gt;random.randn()&lt;/code&gt; — Standard Normal Distribution (标准正态分布)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Floats from a distribution with &lt;strong&gt;mean=0, std=1&lt;/strong&gt; (Bell curve / 钟形曲线).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;np.random.randn(4)         # 1-D: 4 values near 0
np.random.randn(3, 3)      # 2-D: 3×3 matrix

# Scale to mean=μ, std=σ
mu, sigma = 5, 2
mu + sigma * np.random.randn(100)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;4. &lt;code&gt;random.randint()&lt;/code&gt; — Random Integers (随机整数)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Random integers in &lt;strong&gt;[low, high)&lt;/strong&gt; — high is excluded.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;np.random.randint(0, 10)           # single integer, 0–9
np.random.randint(1, 7, size=5)    # five dice rolls
np.random.randint(0, 100, size=(3, 4))  # 3×4 matrix
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;5. &lt;code&gt;random.choice()&lt;/code&gt; — Sample from Array (从数组中抽取)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Randomly pick elements from a 1-D array — with or without replacement (有/无放回).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;arr = np.array([10, 20, 30, 40, 50])

np.random.choice(arr, 3)                # 3 samples WITH replacement
np.random.choice(arr, 3, replace=False) # 3 samples WITHOUT replacement

# Weighted sampling (带权重抽取)
np.random.choice(arr, 3, p=[0.1, 0.2, 0.4, 0.2, 0.1])
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;6. &lt;code&gt;random.normal()&lt;/code&gt; — Custom Normal Distribution (自定义正态分布)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Generate samples from a normal distribution with any mean (均值) and standard deviation (标准差).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;np.random.normal(loc=0, scale=1, size=5)   # = randn(5)
np.random.normal(loc=170, scale=10, size=1000)  # heights in cm
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;7. Other Useful Distributions (其他常用分布)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;np.random.uniform(low=1, high=6, size=10)   # Continuous uniform (连续均匀)
np.random.binomial(n=10, p=0.5, size=5)     # Binomial (二项分布)
np.random.poisson(lam=3, size=10)           # Poisson (泊松分布)
np.random.shuffle(arr)                       # Shuffle in-place (原地打乱)
np.random.permutation(arr)                  # Shuffled copy (打乱副本)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;8. Quick Comparison Table&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function (函数)&lt;/th&gt;
&lt;th&gt;Distribution&lt;/th&gt;
&lt;th&gt;Output Range&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rand(*shape)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Uniform&lt;/td&gt;
&lt;td&gt;[0, 1) floats&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;randn(*shape)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Standard Normal&lt;/td&gt;
&lt;td&gt;≈ [-3, 3] floats&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;randint(lo, hi, size)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Discrete Uniform&lt;/td&gt;
&lt;td&gt;[lo, hi) integers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;choice(a, n)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Custom array&lt;/td&gt;
&lt;td&gt;Elements of &lt;code&gt;a&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;normal(μ, σ, size)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Normal&lt;/td&gt;
&lt;td&gt;Floats near μ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;uniform(lo, hi, size)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Continuous Uniform&lt;/td&gt;
&lt;td&gt;[lo, hi) floats&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt; Always call &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;np.random.seed(n)&amp;lt;/code&amp;gt; at the start of experiments for reproducibility, and use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;replace=False&amp;lt;/code&amp;gt; in &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;choice()&amp;lt;/code&amp;gt; when sampling without repetition.&amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>VII. NumPy Input&amp;Output</title><link>https://lxy-alexander.github.io/blog/posts/numpy/api/07numpy-inputoutput/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/numpy/api/07numpy-inputoutput/</guid><description>VII. NumPy Input&amp;Output</description><pubDate>Thu, 12 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;&lt;strong&gt;VII. NumPy Input / Output (输入输出)&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; NumPy supports two file formats: &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;text files (文本文件)&amp;lt;/span&amp;gt; like CSV for human-readable data, and &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;binary files (二进制文件)&amp;lt;/span&amp;gt; like &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;.npy&amp;lt;/code&amp;gt; / &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;.npz&amp;lt;/code&amp;gt; for fast, compact storage. Use text for sharing; use binary for speed. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;1. &lt;code&gt;savetxt()&lt;/code&gt; / &lt;code&gt;loadtxt()&lt;/code&gt; — Text Files (文本文件)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Save/load arrays as human-readable text (CSV, TSV, etc.).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import numpy as np

a = np.array([[1.0, 2.0, 3.0],
              [4.0, 5.0, 6.0]])

# Save to CSV (保存为CSV)
np.savetxt(&apos;data.csv&apos;, a, delimiter=&apos;,&apos;, fmt=&apos;%.2f&apos;,
           header=&apos;col1,col2,col3&apos;, comments=&apos;&apos;)

# Load from CSV (从CSV加载)
b = np.loadtxt(&apos;data.csv&apos;, delimiter=&apos;,&apos;, skiprows=1)
print(b)
# [[1. 2. 3.]
#  [4. 5. 6.]]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;loadtxt&amp;lt;/code&amp;gt; is for &amp;lt;strong&amp;gt;numeric data only&amp;lt;/strong&amp;gt;. For mixed types (strings + numbers), use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;np.genfromtxt()&amp;lt;/code&amp;gt; or &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;pandas.read_csv()&amp;lt;/code&amp;gt;.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. &lt;code&gt;save()&lt;/code&gt; / &lt;code&gt;load()&lt;/code&gt; — Binary &lt;code&gt;.npy&lt;/code&gt; Format (二进制格式)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Save a single array to a &lt;code&gt;.npy&lt;/code&gt; binary file — preserves dtype, shape, and is much faster than text.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a = np.array([1, 2, 3, 4, 5])

np.save(&apos;my_array.npy&apos;, a)       # auto-adds .npy extension

b = np.load(&apos;my_array.npy&apos;)
print(b)   # [1 2 3 4 5]
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;th&gt;Extension&lt;/th&gt;
&lt;th&gt;Speed&lt;/th&gt;
&lt;th&gt;Human-readable?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Text&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.csv&lt;/code&gt;, &lt;code&gt;.txt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Slow&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Binary&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.npy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Fast&amp;lt;/span&amp;gt;&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;3. &lt;code&gt;savez()&lt;/code&gt; / &lt;code&gt;load()&lt;/code&gt; — Multiple Arrays (多数组存储)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Save multiple arrays into a single &lt;code&gt;.npz&lt;/code&gt; file (a zip of &lt;code&gt;.npy&lt;/code&gt; files).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a = np.array([1, 2, 3])
b = np.array([[4, 5], [6, 7]])

np.savez(&apos;multi.npz&apos;, x=a, y=b)            # uncompressed
np.savez_compressed(&apos;multi.npz&apos;, x=a, y=b) # compressed (smaller)

data = np.load(&apos;multi.npz&apos;)
print(data[&apos;x&apos;])   # [1 2 3]
print(data[&apos;y&apos;])   # [[4 5] [6 7]]
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;4. &lt;code&gt;tofile()&lt;/code&gt; / &lt;code&gt;fromfile()&lt;/code&gt; — Raw Binary (原始二进制)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Write raw bytes to disk — no metadata (shape/dtype) is saved. You must know the dtype and shape to reload correctly.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a = np.array([1, 2, 3, 4], dtype=np.int32)
a.tofile(&apos;raw.bin&apos;)

b = np.fromfile(&apos;raw.bin&apos;, dtype=np.int32)
print(b)   # [1 2 3 4]  — shape info is LOST, always 1-D
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;tofile/fromfile do NOT save dtype or shape. Always use save/load (.npy) unless you need raw binary for interoperability with C/Fortran code.&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;5. &lt;code&gt;genfromtxt()&lt;/code&gt; — Robust Text Loading (鲁棒文本加载)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Like &lt;code&gt;loadtxt&lt;/code&gt; but handles missing values (缺失值) and mixed data types.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# CSV with missing values
# 1,2,NaN
# 4,,6
data = np.genfromtxt(&apos;messy.csv&apos;, delimiter=&apos;,&apos;,
                     filling_values=0)   # replace NaN with 0
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;6. Quick Comparison Table&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;th&gt;Preserves dtype/shape?&lt;/th&gt;
&lt;th&gt;Use case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;savetxt / loadtxt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Text (CSV)&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;Share data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;save / load&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.npy&lt;/code&gt; binary&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Fast single array&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;savez / load&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.npz&lt;/code&gt; binary&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Multiple arrays&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tofile / fromfile&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Raw binary&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;C interop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;genfromtxt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Text&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;Missing values&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt; Use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;.npy&amp;lt;/code&amp;gt; / &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;.npz&amp;lt;/code&amp;gt; for fast internal storage, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;savetxt&amp;lt;/code&amp;gt; for sharing CSVs, and &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;genfromtxt&amp;lt;/code&amp;gt; when data has missing values.&amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>NumPy Statistical Analysis</title><link>https://lxy-alexander.github.io/blog/posts/numpy/api/04numpy-statistical-analysis/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/numpy/api/04numpy-statistical-analysis/</guid><description>NumPy Statistical Analysis</description><pubDate>Thu, 12 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;&lt;strong&gt;IV. NumPy Statistical Analysis (统计分析)&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; NumPy&apos;s statistical functions let you summarize arrays with one line of code. Most functions accept an &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;axis (轴)&amp;lt;/span&amp;gt; argument — without it, they operate on &amp;lt;strong&amp;gt;all elements&amp;lt;/strong&amp;gt;; with it, they reduce along the specified dimension. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;1. &lt;code&gt;sum()&lt;/code&gt; / &lt;code&gt;mean()&lt;/code&gt; — Total &amp;amp; Average (总和与均值)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import numpy as np

a = np.array([[1, 2, 3],
              [4, 5, 6]])

np.sum(a)           # 21   — sum of ALL elements
np.sum(a, axis=0)   # [5 7 9] — column sums (按列求和)
np.sum(a, axis=1)   # [6 15]  — row sums (按行求和)

np.mean(a)          # 3.5
np.mean(a, axis=0)  # [2.5 3.5 4.5]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;axis=0&amp;lt;/code&amp;gt; collapses &amp;lt;strong&amp;gt;rows&amp;lt;/strong&amp;gt; (operates down each column); &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;axis=1&amp;lt;/code&amp;gt; collapses &amp;lt;strong&amp;gt;columns&amp;lt;/strong&amp;gt; (operates across each row).&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. &lt;code&gt;min()&lt;/code&gt; / &lt;code&gt;max()&lt;/code&gt; — Extreme Values (极值)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;np.min(a)           # 1
np.max(a)           # 6
np.min(a, axis=1)   # [1 4] — min of each row
np.max(a, axis=0)   # [4 5 6] — max of each column

np.ptp(a)           # 5 — peak-to-peak = max - min (极差)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;3. &lt;code&gt;argmin()&lt;/code&gt; / &lt;code&gt;argmax()&lt;/code&gt; — Index of Extreme Values (极值索引)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Returns the &amp;lt;strong&amp;gt;index (索引)&amp;lt;/strong&amp;gt; of the minimum or maximum element, not the value itself.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;b = np.array([3, 1, 4, 1, 5, 9, 2])

np.argmin(b)   # 1  (index of first minimum value 1)
np.argmax(b)   # 5  (index of maximum value 9)

# Along an axis
np.argmax(a, axis=0)  # [1 1 1] → row index of max in each column
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;4. &lt;code&gt;std()&lt;/code&gt; / &lt;code&gt;var()&lt;/code&gt; — Spread Measures (离散程度)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Measure how spread out the data is. Standard deviation (标准差) = $\sqrt{\text{variance (方差)}}$&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a = np.array([2, 4, 4, 4, 5, 5, 7, 9])

np.std(a)    # 2.0  — population std (总体标准差)
np.var(a)    # 4.0  — population variance (总体方差)

# Sample std/var (样本标准差/方差): use ddof=1
np.std(a, ddof=1)   # 2.138...
np.var(a, ddof=1)   # 4.571...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Default &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;ddof=0&amp;lt;/code&amp;gt; gives &amp;lt;strong&amp;gt;population&amp;lt;/strong&amp;gt; statistics. Use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;ddof=1&amp;lt;/code&amp;gt; for &amp;lt;strong&amp;gt;sample&amp;lt;/strong&amp;gt; statistics (common in data analysis).&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;5. &lt;code&gt;cumsum()&lt;/code&gt; / &lt;code&gt;cumprod()&lt;/code&gt; — Cumulative Functions (累积函数)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Returns running totals — each output element is the sum (or product) of all elements up to that position.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a = np.array([1, 2, 3, 4])

np.cumsum(a)    # [1  3  6 10]  — running sum (累积和)
np.cumprod(a)   # [1  2  6 24]  — running product (累积积)

# 2-D with axis
m = np.array([[1,2],[3,4]])
np.cumsum(m, axis=0)  # [[1,2],[4,6]] — cumulative down columns
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;6. &lt;code&gt;median()&lt;/code&gt; / &lt;code&gt;percentile()&lt;/code&gt; — Percentile Stats (百分位数)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;a = np.array([1, 2, 3, 4, 5])

np.median(a)                    # 3.0 — middle value (中位数)
np.percentile(a, 25)            # 2.0 — 25th percentile (四分位数)
np.percentile(a, [25, 50, 75])  # [2. 3. 4.]
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;7. Quick Comparison Table&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function (函数)&lt;/th&gt;
&lt;th&gt;Returns&lt;/th&gt;
&lt;th&gt;axis support?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sum()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Total of elements&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mean()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Average value&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;min()&lt;/code&gt; / &lt;code&gt;max()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Smallest / largest value&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;argmin()&lt;/code&gt; / &lt;code&gt;argmax()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Index of min / max&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;std()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Standard deviation&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;var()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Variance&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cumsum()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Running sum array&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;median()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Middle value&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;percentile(a, q)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;q-th percentile&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt; Always specify &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;axis&amp;lt;/code&amp;gt; for multi-dimensional arrays, and use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;ddof=1&amp;lt;/code&amp;gt; when computing sample (not population) statistics.&amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>NumPy Set Operations</title><link>https://lxy-alexander.github.io/blog/posts/numpy/api/08numpy-set-operations/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/numpy/api/08numpy-set-operations/</guid><description>NumPy Set Operations</description><pubDate>Thu, 12 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;&lt;strong&gt;VIII. NumPy Set Operations (集合运算)&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; NumPy provides set-like operations (集合运算) on &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;1-D arrays&amp;lt;/span&amp;gt;. These treat the array as a set of values and support finding unique elements, intersections (交集), unions (并集), and differences (差集) — all returned as &amp;lt;strong&amp;gt;sorted&amp;lt;/strong&amp;gt; arrays. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;1. &lt;code&gt;unique()&lt;/code&gt; — Find Unique Values (求唯一值)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Return sorted unique elements. Optionally return counts or indices.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import numpy as np

a = np.array([3, 1, 2, 1, 3, 3, 2])

np.unique(a)                        # [1 2 3]

# Also return counts (每个值出现的次数)
vals, counts = np.unique(a, return_counts=True)
# vals:   [1 2 3]
# counts: [2 2 3]

# Also return first-occurrence indices (首次出现的索引)
vals, idx = np.unique(a, return_index=True)
# idx: [1 2 0]  (positions in original array)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;2. &lt;code&gt;intersect1d()&lt;/code&gt; — Intersection (交集)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Elements that appear in &lt;strong&gt;both&lt;/strong&gt; arrays.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a = np.array([1, 2, 3, 4, 5])
b = np.array([3, 4, 5, 6, 7])

np.intersect1d(a, b)   # [3 4 5]

# Also return indices in each array
common, ia, ib = np.intersect1d(a, b, return_indices=True)
# ia: [2 3 4] (positions in a)
# ib: [0 1 2] (positions in b)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;3. &lt;code&gt;union1d()&lt;/code&gt; — Union (并集)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; All elements from &lt;strong&gt;either&lt;/strong&gt; array, deduplicated and sorted.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a = np.array([1, 2, 3])
b = np.array([2, 3, 4, 5])

np.union1d(a, b)   # [1 2 3 4 5]
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;4. &lt;code&gt;setdiff1d()&lt;/code&gt; — Difference (差集)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Elements in &lt;code&gt;a&lt;/code&gt; that are &lt;strong&gt;not in&lt;/strong&gt; &lt;code&gt;b&lt;/code&gt; (order matters: a − b).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a = np.array([1, 2, 3, 4, 5])
b = np.array([3, 4])

np.setdiff1d(a, b)   # [1 2 5]  — in a but not in b
np.setdiff1d(b, a)   # []       — b minus a (empty here)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;5. &lt;code&gt;in1d()&lt;/code&gt; — Membership Test (成员检测)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Returns a boolean array — &lt;code&gt;True&lt;/code&gt; where elements of &lt;code&gt;a&lt;/code&gt; appear in &lt;code&gt;b&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a = np.array([1, 2, 3, 4, 5])
b = np.array([2, 4])

mask = np.in1d(a, b)        # [False  True False  True False]
a[mask]                     # [2 4]  — filter using the mask

# Modern equivalent (NumPy 1.24+)
np.isin(a, b)               # same result, more readable
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;np.isin()&amp;lt;/code&amp;gt; is preferred over &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;in1d()&amp;lt;/code&amp;gt; in modern NumPy — it supports multi-dimensional arrays and has clearer naming.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;6. &lt;code&gt;setxor1d()&lt;/code&gt; — Symmetric Difference (对称差集)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Elements in &lt;strong&gt;either&lt;/strong&gt; array but &lt;strong&gt;not in both&lt;/strong&gt; (XOR logic).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a = np.array([1, 2, 3, 4])
b = np.array([3, 4, 5, 6])

np.setxor1d(a, b)   # [1 2 5 6]  — not in common
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;7. Visual Summary&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;a = [1, 2, 3, 4, 5]
b =       [3, 4, 5, 6, 7]

intersect1d: [3, 4, 5]         ← overlap
union1d:     [1, 2, 3, 4, 5, 6, 7]  ← all
setdiff1d(a,b): [1, 2]         ← only in a
setxor1d:    [1, 2, 6, 7]      ← not shared
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;8. Quick Comparison Table&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function (函数)&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;th&gt;Analogy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;unique(a)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Deduplicated &lt;code&gt;a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove duplicates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;intersect1d(a, b)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;a ∩ b&lt;/td&gt;
&lt;td&gt;Both have it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;union1d(a, b)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;a ∪ b&lt;/td&gt;
&lt;td&gt;Either has it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;setdiff1d(a, b)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;a − b&lt;/td&gt;
&lt;td&gt;Only &lt;code&gt;a&lt;/code&gt; has it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;setxor1d(a, b)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;a △ b&lt;/td&gt;
&lt;td&gt;Only one has it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;in1d(a, b)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Boolean mask&lt;/td&gt;
&lt;td&gt;Is &lt;code&gt;a[i]&lt;/code&gt; in &lt;code&gt;b&lt;/code&gt;?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt; All set functions operate on &amp;lt;strong&amp;gt;1-D sorted arrays&amp;lt;/strong&amp;gt; — use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;unique()&amp;lt;/code&amp;gt; first to deduplicate, then apply &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;intersect1d / union1d / setdiff1d&amp;lt;/code&amp;gt; for standard set logic.&amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>NumPy Logical Operations</title><link>https://lxy-alexander.github.io/blog/posts/numpy/api/09numpy-logical-operations/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/numpy/api/09numpy-logical-operations/</guid><description>NumPy Logical Operations</description><pubDate>Thu, 12 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;&lt;strong&gt;IX. NumPy Logical Operations (逻辑运算)&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Logical operations (逻辑运算) in NumPy work &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;element-wise&amp;lt;/span&amp;gt; on arrays and always return a &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;boolean array (布尔数组)&amp;lt;/span&amp;gt;. These are the foundation of &amp;lt;strong&amp;gt;masking (掩码)&amp;lt;/strong&amp;gt; and &amp;lt;strong&amp;gt;conditional filtering (条件筛选)&amp;lt;/strong&amp;gt;. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;1. Comparison Operators (比较运算符)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Compare each element to a value or to another array. Returns &lt;code&gt;True&lt;/code&gt;/&lt;code&gt;False&lt;/code&gt; for each position.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import numpy as np

a = np.array([1, 2, 3, 4, 5])

a &amp;gt; 3          # [False False False  True  True]
a == 3         # [False False  True False False]
a != 3         # [ True  True False  True  True]
a &amp;gt;= 3         # [False False  True  True  True]

# Functional equivalents (函数式写法)
np.greater(a, 3)       # same as a &amp;gt; 3
np.less(a, 3)          # same as a &amp;lt; 3
np.equal(a, 3)         # same as a == 3
np.not_equal(a, 3)     # same as a != 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Never use Python&apos;s &lt;code&gt;and&lt;/code&gt; / &lt;code&gt;or&lt;/code&gt; / &lt;code&gt;not&lt;/code&gt; on arrays — they raise errors. Always use np.logical_and / logical_or / logical_not instead.&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Logical AND / OR / NOT (逻辑与/或/非)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Combine boolean arrays element-wise. Use &lt;code&gt;&amp;amp;&lt;/code&gt;, &lt;code&gt;|&lt;/code&gt;, &lt;code&gt;~&lt;/code&gt; as shorthand.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a = np.array([1, 2, 3, 4, 5])

# AND (与): both conditions must be True
mask = (a &amp;gt; 2) &amp;amp; (a &amp;lt; 5)             # [F F T T F]
np.logical_and(a &amp;gt; 2, a &amp;lt; 5)         # same

# OR (或): at least one condition is True
mask = (a &amp;lt; 2) | (a &amp;gt; 4)             # [T F F F T]
np.logical_or(a &amp;lt; 2, a &amp;gt; 4)          # same

# NOT (非): invert boolean
mask = ~(a &amp;gt; 3)                       # [T T T F F]
np.logical_not(a &amp;gt; 3)                 # same
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Always wrap individual conditions in parentheses when using &amp;amp; and |, because &amp;amp; has higher precedence than &amp;gt; in Python.&amp;lt;/span&amp;gt; E.g., write &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;(a &amp;gt; 2) &amp;amp; (a &amp;lt; 5)&amp;lt;/code&amp;gt;, not &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;a &amp;gt; 2 &amp;amp; a &amp;lt; 5&amp;lt;/code&amp;gt;.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. Boolean Masking — Filter Arrays (布尔掩码筛选)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Use a boolean array as an index to select matching elements.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a = np.array([10, 25, 3, 47, 8, 60])

mask = a &amp;gt; 20
print(mask)    # [False  True False  True False  True]
print(a[mask]) # [25 47 60]  — only values where mask is True

# One-liner
a[a &amp;gt; 20]      # [25 47 60]
a[(a &amp;gt; 10) &amp;amp; (a &amp;lt; 50)]  # [25 47]
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;4. &lt;code&gt;np.where()&lt;/code&gt; — Conditional Selection (条件选择)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Like a vectorized ternary: &lt;code&gt;where(condition, value_if_true, value_if_false)&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a = np.array([1, -2, 3, -4, 5])

np.where(a &amp;gt; 0, a, 0)       # [1  0  3  0  5]  — replace negatives with 0
np.where(a &amp;gt; 0, &apos;pos&apos;, &apos;neg&apos;)  # [&apos;pos&apos; &apos;neg&apos; &apos;pos&apos; &apos;neg&apos; &apos;pos&apos;]

# Without x, y: returns indices where condition is True (返回满足条件的索引)
np.where(a &amp;gt; 0)    # (array([0, 2, 4]),)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;5. &lt;code&gt;any()&lt;/code&gt; / &lt;code&gt;all()&lt;/code&gt; — Global Boolean Tests (全局布尔检验)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Core idea:&amp;lt;/span&amp;gt; Test whether &lt;strong&gt;any&lt;/strong&gt; or &lt;strong&gt;all&lt;/strong&gt; elements satisfy a condition.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a = np.array([1, 2, 3, 4, 5])

np.any(a &amp;gt; 4)          # True  — at least one element &amp;gt; 4
np.all(a &amp;gt; 0)          # True  — all elements &amp;gt; 0
np.all(a &amp;gt; 3)          # False — not all &amp;gt; 3

# With axis
m = np.array([[1, 2], [0, 4]])
np.any(m == 0, axis=1)  # [False  True]
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;6. &lt;code&gt;isnan()&lt;/code&gt; / &lt;code&gt;isinf()&lt;/code&gt; — Special Value Checks (特殊值检验)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;a = np.array([1.0, np.nan, np.inf, -np.inf, 2.0])

np.isnan(a)    # [F  T  F  F  F]
np.isinf(a)    # [F  F  T  T  F]
np.isfinite(a) # [T  F  F  F  T]

# Clean NaN values (清除NaN值)
a[~np.isnan(a)]   # [1.  inf -inf  2.]
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;7. Quick Comparison Table&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation (操作)&lt;/th&gt;
&lt;th&gt;Shorthand&lt;/th&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Greater than (大于)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;a &amp;gt; b&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;np.greater(a, b)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Less than (小于)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;a &amp;lt; b&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;np.less(a, b)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Equal (等于)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;a == b&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;np.equal(a, b)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AND (与)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mask1 &amp;amp; mask2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;np.logical_and(m1, m2)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OR (或)&lt;/td&gt;
&lt;td&gt;`mask1&lt;/td&gt;
&lt;td&gt;mask2`&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NOT (非)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;~mask&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;np.logical_not(mask)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conditional replace&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;&lt;code&gt;np.where(cond, x, y)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Any true?&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;&lt;code&gt;np.any(cond)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;All true?&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;&lt;code&gt;np.all(cond)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt; Build boolean masks with &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;&amp;amp;&amp;lt;/code&amp;gt; / &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;|&amp;lt;/code&amp;gt; / &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;~&amp;lt;/code&amp;gt; (always use parentheses!), apply them for filtering, and use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;np.where()&amp;lt;/code&amp;gt; for conditional replacement.&amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Tensor Shape &amp; Dimension Transforms</title><link>https://lxy-alexander.github.io/blog/posts/pytorch/api/02tensor-shape--dimension-transforms/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/pytorch/api/02tensor-shape--dimension-transforms/</guid><description>Tensor Shape &amp; Dimension Transforms</description><pubDate>Thu, 12 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;II. Tensor Shape &amp;amp; Dimension Transforms (张量形状与维度变换)&lt;/h1&gt;
&lt;h2&gt;1. &lt;code&gt;Tensor.view()&lt;/code&gt; / &lt;code&gt;Tensor.reshape()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Reshapes a Tensor without changing its data. &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;view&amp;lt;/code&amp;gt; requires Contiguous Memory (连续内存); &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;reshape&amp;lt;/code&amp;gt; handles non-contiguous cases automatically. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;x = torch.arange(12)   # shape [12]
y = x.view(3, 4)       # shape [3, 4]
z = x.reshape(2, 6)    # shape [2, 6]
w = x.reshape(-1, 3)   # -1 auto-infers → shape [4, 3]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Prefer &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;reshape&amp;lt;/code&amp;gt; by default; switch to &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;view&amp;lt;/code&amp;gt; only when you need to guarantee zero-copy memory sharing.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. &lt;code&gt;torch.squeeze()&lt;/code&gt; / &lt;code&gt;torch.unsqueeze()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;squeeze&amp;lt;/code&amp;gt;: Removes dimensions of size 1. &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;unsqueeze&amp;lt;/code&amp;gt;: Inserts a size-1 dimension at a specified position. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;x = torch.zeros(1, 3, 1, 5)
y = x.squeeze()       # [3, 5]

z = torch.zeros(3, 5)
w = z.unsqueeze(0)    # [1, 3, 5]
v = z.unsqueeze(-1)   # [3, 5, 1]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;unsqueeze&amp;lt;/code&amp;gt; is frequently used for Broadcasting (广播): add a batch dimension (批次维度) or channel dimension (通道维度) to a vector.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. &lt;code&gt;torch.cat()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Concatenates multiple Tensors along an &amp;lt;strong&amp;gt;existing dimension (已有维度)&amp;lt;/strong&amp;gt;. Does &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;not&amp;lt;/span&amp;gt; create a new axis. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a = torch.zeros(2, 3)
b = torch.ones(4, 3)
c = torch.cat([a, b], dim=0)  # shape [6, 3]

d = torch.cat([torch.zeros(2, 2), torch.ones(2, 4)], dim=1)  # shape [2, 6]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; All Tensors must have the same shape on every dimension except the concatenation axis (拼接轴).&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4. &lt;code&gt;torch.stack()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Stacks Tensors along a &amp;lt;strong&amp;gt;new dimension (新维度)&amp;lt;/strong&amp;gt;. All input Tensors must be exactly the same shape. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a = torch.tensor([1, 2, 3])
b = torch.tensor([4, 5, 6])
c = torch.stack([a, b])        # [2, 3]  — new dim=0
d = torch.stack([a, b], dim=1) # [3, 2]  — new dim=1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Key difference from &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;cat&amp;lt;/code&amp;gt;: &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;stack&amp;lt;/code&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;requires identical shapes and always creates a new axis&amp;lt;/span&amp;gt;.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;5. &lt;code&gt;Tensor.permute()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Reorders dimensions according to a specified axis order. Equivalent to NumPy&apos;s &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;transpose(axes)&amp;lt;/code&amp;gt;. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Convert NCHW → NHWC
x = torch.zeros(8, 3, 224, 224)
y = x.permute(0, 2, 3, 1)  # shape [8, 224, 224, 3]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; After &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;permute&amp;lt;/code&amp;gt;, the Tensor becomes Non-contiguous (非连续). Call &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;.contiguous()&amp;lt;/code&amp;gt; if a subsequent op requires contiguous memory.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;6. &lt;code&gt;Tensor.transpose()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Swaps exactly two specified dimensions. A simplified version of &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;permute&amp;lt;/code&amp;gt; for axis swapping. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;x = torch.zeros(4, 5, 6)
y = x.transpose(1, 2)   # shape [4, 6, 5]

m = torch.rand(3, 4)
mt = m.t()              # 2D matrix transpose → shape [4, 3]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;.transpose(-1, -2)&amp;lt;/code&amp;gt; for Batched Matrix Transpose (批量矩阵转置), valid over any batch dimension.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;7. &lt;code&gt;torch.split()&lt;/code&gt; / &lt;code&gt;torch.chunk()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;split&amp;lt;/code&amp;gt;: Splits by specified sizes. &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;chunk&amp;lt;/code&amp;gt;: Splits into equal pieces; the last chunk may be smaller. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;x = torch.arange(10)
parts = torch.split(x, 3)   # (tensor([0,1,2]), tensor([3,4,5]), tensor([6,7,8]), tensor([9]))
chunks = torch.chunk(x, 3)  # 3 chunks: [0–3], [4–6], [7–9]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Multi-GPU Sharding (多GPU分片) and DataLoader batch splitting internally rely on &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;chunk&amp;lt;/code&amp;gt; / &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;split&amp;lt;/code&amp;gt; logic.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;8. &lt;code&gt;Tensor.flatten()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Flattens a Tensor to 1D, or flattens a specific range of dimensions. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;x = torch.zeros(2, 3, 4)
y = x.flatten()       # shape [24]
z = x.flatten(1, 2)   # shape [2, 12]  — only flatten dims 1–2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Most commonly used at the CNN → Fully Connected (全连接) transition. Equivalent to &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;x.view(x.size(0), -1)&amp;lt;/code&amp;gt;.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;9. &lt;code&gt;torch.broadcast_to()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Broadcasts (广播) a Tensor to a target shape as a read-only view. No data is copied. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;x = torch.tensor([1, 2, 3])      # shape [3]
y = torch.broadcast_to(x, (4, 3)) # shape [4, 3]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Broadcasting is the underlying mechanism of most PyTorch arithmetic operations. Understanding it helps avoid Shape Errors (形状错误).&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;10. &lt;code&gt;Tensor.expand()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Expands size-1 dimensions to a specified size. Shares storage (no memory copy), unlike &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;repeat&amp;lt;/code&amp;gt;. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;x = torch.zeros(3, 1)
y = x.expand(3, 4)   # shape [3, 4] — zero memory copy
z = x.repeat(1, 4)   # shape [3, 4] — actual data copy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;expand&amp;lt;/code&amp;gt; results in a Non-contiguous Tensor; call &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;.contiguous()&amp;lt;/code&amp;gt; or &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;.clone()&amp;lt;/code&amp;gt; before writing.&amp;lt;/div&amp;gt; &amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Master &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;reshape&amp;lt;/code&amp;gt;, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;squeeze/unsqueeze&amp;lt;/code&amp;gt;, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;cat/stack&amp;lt;/code&amp;gt;, and &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;permute&amp;lt;/code&amp;gt; — together they cover 90% of all shape manipulation needs.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
</content:encoded></item><item><title>Tensor Creation &amp; Basic Operations</title><link>https://lxy-alexander.github.io/blog/posts/pytorch/api/01tensor-creation--basic-operations/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/pytorch/api/01tensor-creation--basic-operations/</guid><description>Tensor Creation &amp; Basic Operations</description><pubDate>Thu, 12 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;I. Tensor Creation &amp;amp; Basic Operations (张量创建与基础操作)&lt;/h1&gt;
&lt;h2&gt;1. &lt;code&gt;torch.tensor()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Creates a Tensor (张量) directly from a Python list or NumPy array. You can specify the Data Type (数据类型) and Device (设备) at creation time. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import torch
x = torch.tensor(
    [[1.0, 2.0], [3.0, 4.0]],
    dtype=torch.float32
)
print(x.shape)  # torch.Size([2, 2])


import numpy as np

arr = np.array([1, 2, 3], dtype=np.float32)
x = torch.as_tensor(arr)

arr[0] = 100 # share the memory with array
print(x)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Every call &amp;lt;strong&amp;gt;copies&amp;lt;/strong&amp;gt; the data. To share memory with the source array, use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;torch.as_tensor()&amp;lt;/code&amp;gt; instead.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. &lt;code&gt;torch.zeros()&lt;/code&gt; / &lt;code&gt;torch.ones()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Creates all-zero or all-one Tensors (全零/全一张量). Commonly used for bias initialization (偏置初始化) and mask generation (掩码生成). &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;z = torch.zeros(3, 4)                    # 3×4 all zeros
o = torch.ones(2, 3, dtype=torch.int32) # 2×3 all ones, int type


out = torch.empty(2, 3,  dtype=torch.float16)
print(out)
out = torch.zeros(2, 3, dtype=torch.float16, out=out)
print(out)

# tensor([[ 5.5680e+03, -9.3126e-04,         nan],
#         [ 0.0000e+00,  7.8906e-01,  1.1133e-01]], dtype=torch.float16)
# tensor([[0., 0., 0.],
#         [0., 0., 0.]], dtype=torch.float16)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; The &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;out=&amp;lt;/code&amp;gt; parameter writes results into an existing Tensor, avoiding extra memory allocation (内存分配).&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3.&lt;code&gt;torch.arange()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Generates an Arithmetic Sequence Tensor (等差数列张量), analogous to Python&apos;s &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;range()&amp;lt;/code&amp;gt;. Supports float step sizes. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;t = torch.arange(0, 10, 2)       # tensor([0, 2, 4, 6, 8])
f = torch.arange(0.0, 1.0, step = 0.25) # tensor([0.00, 0.25, 0.50, 0.75])

print(t)
print(f)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Floating-point steps may cause Boundary Precision Issues (边界精度问题). For exact equal-interval sampling, use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;torch.linspace()&amp;lt;/code&amp;gt;.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4. &lt;code&gt;torch.linspace()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Uniformly generates &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;steps&amp;lt;/code&amp;gt; points in the interval [start, end]. More precise than &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;arange&amp;lt;/code&amp;gt; for float ranges. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;t = torch.linspace(0, 1, steps=5)
# tensor([0.00, 0.25, 0.50, 0.75, 1.00])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Commonly used for plotting function curves (函数曲线) and generating uniformly sampled frequency axes (均匀采样频率轴).&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;5. &lt;code&gt;torch.rand()&lt;/code&gt; / &lt;code&gt;torch.randn()&lt;/code&gt;/ &lt;code&gt;torch.randint()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;rand&amp;lt;/code&amp;gt;: Uniform Distribution (均匀分布) U[0,1). &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;randn&amp;lt;/code&amp;gt;: Standard Normal Distribution (标准正态分布) N(-∞, +∞). &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;u = torch.rand(2, 3)   # Uniform distribution
n = torch.randn(2, 3)  # Normal distribution
i = torch.randint(low=0, high=10, size=(2, 3))

# Fix seed for reproducibility (可复现性)
torch.manual_seed(42)
x = torch.rand(3)
print(x)

print(u)
print(n)
print(i)

# tensor([0.8823, 0.9150, 0.3829])
# tensor([[0.9593, 0.3904, 0.6009],
#         [0.2566, 0.7936, 0.9408]])
# tensor([[ 1.5231,  0.6647, -1.0324],
#         [-0.2770, -0.1671, -0.1079]])
# tensor([[6, 3, 1],
#         [9, 3, 1]])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Neural network Weight Initialization (权重初始化) typically uses &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;randn&amp;lt;/code&amp;gt;; Dropout Mask (Dropout掩码) generation uses &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;rand&amp;lt;/code&amp;gt;.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;6. &lt;code&gt;torch.eye()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Creates an Identity Matrix (单位矩阵) with ones on the diagonal and zeros elsewhere. Commonly used in Linear Algebra (线性代数) and regularization (正则化). &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;I = torch.eye(3)
# tensor([[1., 0., 0.],
#         [0., 1., 0.],
#         [0., 0., 1.]])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Pass &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;n, m&amp;lt;/code&amp;gt; to create a Non-square Identity Matrix (非方阵单位矩阵), e.g. &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;torch.eye(3, 4)&amp;lt;/code&amp;gt;.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;7. &lt;code&gt;torch.full()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Creates a Tensor of specified shape where all elements equal a given Fill Value (填充值). &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;t = torch.full((2, 3), fill_value=7.0)
# tensor([[7., 7., 7.],
#         [7., 7., 7.]])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; More efficient than &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;torch.zeros() * 7&amp;lt;/code&amp;gt;. Ideal for creating padding masks (填充掩码).&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;8. &lt;code&gt;torch.from_numpy()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Converts a NumPy ndarray to a Tensor. The two &amp;lt;strong&amp;gt;share memory (共享内存)&amp;lt;/strong&amp;gt; — modifying one affects the other. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import numpy as np
arr = np.array([1.0, 2.0, 3.0])
t = torch.from_numpy(arr) # as_tensor(arr)
arr[0] = 99
print(t)  # tensor([99., 2., 3.])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Shared memory is a &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;double-edged sword&amp;lt;/span&amp;gt;: it saves memory but can cause unintended modifications to the source array.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;9. &lt;code&gt;Tensor.numpy()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Converts a CPU Tensor back to a NumPy ndarray. Also shares the underlying memory (底层内存). &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;t = torch.tensor([1.0, 2.0, 3.0])
arr = t.numpy()
t[0] = 100
print(arr)  # [100.  2.  3.]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;GPU Tensors must call &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;.cpu()&amp;lt;/code&amp;gt; first&amp;lt;/span&amp;gt;, and Tensors with &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;requires_grad=True&amp;lt;/code&amp;gt; must call &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;.detach()&amp;lt;/code&amp;gt; first.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;10.&lt;code&gt;torch.empty()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Allocates Uninitialized Memory (未初始化内存) for a Tensor — the fastest allocation method. Values are whatever remains in memory. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;t = torch.empty(3, 3)  # Values are undefined
t.fill_(0.5)           # Must fill before reading
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Never read values before filling&amp;lt;/span&amp;gt;. Best for performance-sensitive scenarios where you immediately overwrite the buffer.&amp;lt;/div&amp;gt; &amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;torch.tensor()&amp;lt;/code&amp;gt; for known data, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;torch.zeros/ones/rand/randn()&amp;lt;/code&amp;gt; for initialized buffers, and &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;torch.empty()&amp;lt;/code&amp;gt; only when you&apos;ll immediately overwrite every element.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
</content:encoded></item><item><title>Math &amp; Statistical Operations</title><link>https://lxy-alexander.github.io/blog/posts/pytorch/api/03math--statistical-operations/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/pytorch/api/03math--statistical-operations/</guid><description>Math &amp; Statistical Operations</description><pubDate>Thu, 12 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;III. Math &amp;amp; Statistical Operations (数学与统计运算)&lt;/h1&gt;
&lt;h2&gt;1. &lt;code&gt;torch.matmul()&lt;/code&gt; / &lt;code&gt;@&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; General Matrix Multiplication (通用矩阵乘法). Supports 2D matrices, batched matrix multiplication (批量矩阵乘), and mixed broadcasting. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a = torch.rand(3, 4)
b = torch.rand(4, 5)
c = torch.matmul(a, b)  # [3, 5]
d = a @ b               # equivalent

# Batched matmul
x = torch.rand(8, 3, 4)
y = torch.rand(8, 4, 5)
z = x @ y               # [8, 3, 5]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; The core of Transformer Attention Computation (注意力计算). &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;mm&amp;lt;/code&amp;gt; is 2D-only; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;matmul&amp;lt;/code&amp;gt; is more general.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. &lt;code&gt;torch.sum()&lt;/code&gt; / &lt;code&gt;torch.mean()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Computes the sum or mean over all elements or a specified axis. &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;keepdim=True&amp;lt;/code&amp;gt; preserves the reduced dimension. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;x = torch.tensor([[1., 2., 3.], [4., 5., 6.]])
print(x.sum())                        # 21.0
print(x.sum(dim=0))                   # [5, 7, 9]
print(x.mean(dim=1, keepdim=True))    # [[2.], [5.]]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;keepdim=True&amp;lt;/code&amp;gt; avoids Dimension Alignment Issues (维度对齐问题) during broadcasting.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;3) &lt;code&gt;torch.max()&lt;/code&gt; / &lt;code&gt;torch.min()&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Returns the maximum/minimum value. When a &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;dim&amp;lt;/code&amp;gt; is specified, returns both values and indices (argmax/argmin). &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;x = torch.tensor([3., 1., 4., 1., 5.])
print(x.max())                 # tensor(5.)
vals, idx = x.max(dim=0)       # vals=5.0, idx=4
idx2 = x.argmax()              # tensor(4)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Classification Prediction Labels (分类网络预测标签): &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;preds = logits.argmax(dim=1)&amp;lt;/code&amp;gt;.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;4) &lt;code&gt;torch.abs()&lt;/code&gt; / &lt;code&gt;torch.sqrt()&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Element-wise absolute value or square root. Used in loss computation (损失计算) and feature normalization (特征归一化). &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;x = torch.tensor([-1., 4., -9.])
print(torch.abs(x))   # [1., 4., 9.]
y = torch.tensor([1., 4., 9.])
print(torch.sqrt(y))  # [1., 2., 3.]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;torch.sqrt&amp;lt;/code&amp;gt; returns NaN for negative inputs&amp;lt;/span&amp;gt;. Use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;torch.clamp(x, min=0)&amp;lt;/code&amp;gt; first.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;5) &lt;code&gt;torch.clamp()&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Clips values to the range [min, max]. Values outside the range are truncated to the boundary. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;x = torch.tensor([-2., 0., 3., 8.])
y = torch.clamp(x, min=0., max=5.)  # tensor([0., 0., 3., 5.])
z = torch.clamp(x, min=0.)          # equivalent to ReLU
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; The go-to tool for Gradient Clipping (梯度裁剪), normalization, and avoiding &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;log(0)&amp;lt;/code&amp;gt;.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;6) &lt;code&gt;torch.pow()&lt;/code&gt; / &lt;code&gt;torch.exp()&lt;/code&gt; / &lt;code&gt;torch.log()&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Element-wise power, natural exponent, and natural logarithm. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;x = torch.tensor([1., 2., 3.])
print(torch.pow(x, 2))    # [1., 4., 9.]
print(torch.exp(x))       # [e^1, e^2, e^3]
print(torch.log(x))       # [0., 0.693, 1.099]
print(torch.log1p(x))     # Numerically stable log(1+x)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Cross-entropy already uses &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;log_softmax&amp;lt;/code&amp;gt; internally. When computing manually, use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;log_softmax&amp;lt;/code&amp;gt; for Numerical Stability (数值稳定性).&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;7) &lt;code&gt;torch.dot()&lt;/code&gt; / &lt;code&gt;torch.cross()&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;dot&amp;lt;/code&amp;gt;: Inner product of 1D vectors. &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;cross&amp;lt;/code&amp;gt;: Cross product (叉积) of 3D vectors (physics / 3D graphics). &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a = torch.tensor([1., 2., 3.])
b = torch.tensor([4., 5., 6.])
print(torch.dot(a, b))  # 32.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; For batched inner products, use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;(a * b).sum(-1)&amp;lt;/code&amp;gt; — more efficient than looping &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;dot&amp;lt;/code&amp;gt;.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;8) &lt;code&gt;torch.norm()&lt;/code&gt; / &lt;code&gt;torch.linalg.norm()&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Computes vector/matrix norms: L1, L2, Frobenius Norm (Frobenius范数), etc. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;x = torch.tensor([3., 4.])
print(torch.linalg.norm(x))          # L2: 5.0
print(torch.linalg.norm(x, ord=1))   # L1: 7.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;torch.norm&amp;lt;/code&amp;gt; is deprecated&amp;lt;/span&amp;gt;. New code should use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;torch.linalg.norm&amp;lt;/code&amp;gt;.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;9) &lt;code&gt;torch.topk()&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Returns the top-k largest (or smallest) values and their indices from a Tensor. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;x = torch.tensor([3., 1., 4., 1., 5., 9.])
vals, idx = torch.topk(x, k=3)
# vals: tensor([9., 5., 4.])
# idx:  tensor([5, 4, 2])

_, top5 = logits.topk(5, dim=1)  # Top-5 accuracy evaluation
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Standard approach for Top-5 Accuracy (Top-5准确率) evaluation. Use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;largest=False&amp;lt;/code&amp;gt; to get the smallest k values.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;10) &lt;code&gt;torch.unique()&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Returns unique elements from a Tensor, with optional sorting, counting, and inverse mapping (逆映射). &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;x = torch.tensor([1, 2, 2, 3, 1, 4])
u, cnt = torch.unique(x, return_counts=True)
# u:   tensor([1, 2, 3, 4])
# cnt: tensor([2, 2, 1, 1])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Commonly used for processing Category Labels (类别标签) and deduplicating tokens.&amp;lt;/div&amp;gt; &amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;matmul/@&amp;lt;/code&amp;gt; powers Transformers, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;clamp&amp;lt;/code&amp;gt; guards numerical safety, and &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;topk&amp;lt;/code&amp;gt; drives classification evaluation.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
</content:encoded></item><item><title>Automatic Differentiation — Autograd</title><link>https://lxy-alexander.github.io/blog/posts/pytorch/api/04automatic-differentiation--autograd/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/pytorch/api/04automatic-differentiation--autograd/</guid><description>Automatic Differentiation — Autograd</description><pubDate>Thu, 12 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;IV. Automatic Differentiation — Autograd (自动微分)&lt;/h1&gt;
&lt;h2&gt;1. &lt;code&gt;Tensor.requires_grad&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Marks whether gradient computation (梯度计算) is needed for this Tensor. It is the entry switch of the Autograd System (自动微分系统). &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;x = torch.tensor([2.0], requires_grad=True)
y = x ** 2 + 3 * x
y.backward()
print(x.grad)  # dy/dx = 2x+3 = 7
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Only Leaf Nodes (叶子节点) created by the user can directly set &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;requires_grad&amp;lt;/code&amp;gt;. Intermediate nodes propagate it automatically.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. &lt;code&gt;Tensor.backward()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Triggers Backpropagation (反向传播) from a scalar (or with a gradient tensor argument), computing gradients for all leaf nodes. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;x = torch.tensor([1., 2., 3.], requires_grad=True)
y = (x * 2).sum()
y.backward()
print(x.grad)  # tensor([2., 2., 2.])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Gradients &amp;lt;strong&amp;gt;accumulate&amp;lt;/strong&amp;gt; by default. &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Call &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;optimizer.zero_grad()&amp;lt;/code&amp;gt; before each iteration&amp;lt;/span&amp;gt;.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. &lt;code&gt;torch.no_grad()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Context manager that disables gradient computation — saves memory and speeds up inference (推理) / evaluation (评估). &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;model.eval()
with torch.no_grad():
    output = model(x)
    loss = criterion(output, labels)

# Also usable as a decorator
@torch.no_grad()
def predict(x):
    return model(x)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Always enable this during inference&amp;lt;/span&amp;gt;, otherwise inference is slow and VRAM usage is high.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4. &lt;code&gt;Tensor.detach()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Returns a new Tensor disconnected from the Computation Graph (计算图), sharing data but not propagating gradients. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;x = torch.tensor([1., 2.], requires_grad=True)
y = x * 3
z = y.detach()       # no gradient tracking
arr = y.detach().numpy()  # must detach before .numpy()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; In GAN training, freeze the Generator by calling &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;fake_img.detach()&amp;lt;/code&amp;gt; before passing it to the Discriminator (判别器).&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;5. &lt;code&gt;torch.autograd.grad()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Explicitly computes gradients of outputs w.r.t. inputs. Supports Higher-order Gradients (高阶梯度) like Hessians. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;x = torch.tensor(2.0, requires_grad=True)
y = x ** 3
dy_dx, = torch.autograd.grad(y, x, create_graph=True)  # 1st order
d2y,   = torch.autograd.grad(dy_dx, x)                 # 2nd order
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Core API for MAML (Model-Agnostic Meta-Learning, 模型无关元学习) and Physics-Informed Neural Networks (物理信息神经网络, PINN).&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;6. &lt;code&gt;Tensor.grad&lt;/code&gt; / &lt;code&gt;Tensor.grad_fn&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;grad&amp;lt;/code&amp;gt;: stores the accumulated gradient. &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;grad_fn&amp;lt;/code&amp;gt;: points to the Backward Function (反向传播函数) that created this Tensor. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;x = torch.tensor([1., 2.], requires_grad=True)
y = x * x
print(y.grad_fn)   # &amp;lt;MulBackward0 ...&amp;gt;
y.sum().backward()
print(x.grad)      # tensor([2., 4.])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;grad_fn=None&amp;lt;/code&amp;gt; indicates a leaf node. Check with &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;.is_leaf&amp;lt;/code&amp;gt;.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;7. &lt;code&gt;torch.enable_grad()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Re-enables gradient tracking inside a &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;no_grad&amp;lt;/code&amp;gt; context, enabling fine-grained control. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;with torch.no_grad():
    x = model.encode(data)
    with torch.enable_grad():
        x.requires_grad_(True)
        loss = head(x)  # only this part tracked
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Useful for Partial Freeze Training (部分冻结训练), e.g., fine-tuning only the last layer.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;8. &lt;code&gt;register_hook()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Registers a hook function on a Tensor&apos;s backward pass, enabling inspection or modification of intermediate gradients. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;grads = []
def save_grad(g):
    grads.append(g.clone())

x = torch.rand(3, requires_grad=True)
y = (x**2).sum()
x.register_hook(save_grad)
y.backward()
print(grads[0])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Invaluable for debugging Gradient Vanishing/Explosion (梯度消失/爆炸) and implementing gradient penalties like WGAN-GP.&amp;lt;/div&amp;gt; &amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Always pair &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;backward()&amp;lt;/code&amp;gt; with &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;zero_grad()&amp;lt;/code&amp;gt;, wrap inference in &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;no_grad()&amp;lt;/code&amp;gt;, and use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;detach()&amp;lt;/code&amp;gt; to stop gradients from crossing module boundaries.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
</content:encoded></item><item><title>Neural Network Modules</title><link>https://lxy-alexander.github.io/blog/posts/pytorch/api/05neural-network-modules/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/pytorch/api/05neural-network-modules/</guid><description>Neural Network Modules</description><pubDate>Thu, 12 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;V. Neural Network Modules — &lt;code&gt;nn.Module&lt;/code&gt; (神经网络模块)&lt;/h1&gt;
&lt;h2&gt;1. &lt;code&gt;nn.Module&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; The base class for all neural networks in PyTorch. Manages parameters (参数), sub-modules (子模块), and defines the forward pass (前向传播) logic. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import torch.nn as nn

class MLP(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc1 = nn.Linear(784, 256)
        self.fc2 = nn.Linear(256, 10)

    def forward(self, x):
        x = torch.relu(self.fc1(x))
        return self.fc2(x)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Only implement &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;&lt;strong&gt;init&lt;/strong&gt;&amp;lt;/code&amp;gt; and &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;forward&amp;lt;/code&amp;gt;. The backward pass is handled automatically by autograd.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. &lt;code&gt;nn.Linear()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Fully Connected Layer (全连接层) / Affine Transformation (仿射变换): y = xW&amp;lt;sup&amp;gt;T&amp;lt;/sup&amp;gt; + b. The most fundamental learnable layer. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;fc = nn.Linear(in_features=128, out_features=64, bias=True)
x = torch.rand(32, 128)
out = fc(x)  # shape [32, 64]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Weight shape is &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;[out, in]&amp;lt;/code&amp;gt;. &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;bias=False&amp;lt;/code&amp;gt; is commonly paired with BatchNorm.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. &lt;code&gt;nn.Conv2d()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; 2D Convolutional Layer (二维卷积层). Extracts local spatial features; the core building block of CNNs (卷积神经网络). &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;conv = nn.Conv2d(in_channels=3, out_channels=64, kernel_size=3, stride=1, padding=1)
x = torch.rand(8, 3, 224, 224)
out = conv(x)  # [8, 64, 224, 224]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;padding=kernel_size//2&amp;lt;/code&amp;gt; preserves feature map size (Same Padding, 等尺寸填充).&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4. &lt;code&gt;nn.BatchNorm2d()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Normalizes each channel of a mini-batch (小批量归一化). Accelerates training and mitigates gradient vanishing (梯度消失). &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;bn = nn.BatchNorm2d(num_features=64)
x = torch.rand(8, 64, 28, 28)
out = bn(x)
# Standard order: Conv → BN → ReLU
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;BN is unstable when batch_size=1&amp;lt;/span&amp;gt;. Switch to GroupNorm or LayerNorm in that case.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;5. &lt;code&gt;nn.Dropout()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; During training, randomly zeros out a fraction of neurons — a Regularization (正则化) technique to prevent Overfitting (过拟合). &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dropout = nn.Dropout(p=0.5)
x = torch.rand(4, 128)
out = dropout(x)       # 50% elements zeroed during train mode

dropout.eval()
out_eval = dropout(x)  # identical to x in eval mode
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Forgetting &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;model.eval()&amp;lt;/code&amp;gt;&amp;lt;/span&amp;gt; is the #1 most common bug causing non-deterministic inference results.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;6. &lt;code&gt;nn.Sequential()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Chains a series of layers in order, executing each &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;forward&amp;lt;/code&amp;gt; call sequentially. Simplifies model definition. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;model = nn.Sequential(
    nn.Linear(784, 256),
    nn.ReLU(),
    nn.Dropout(0.3),
    nn.Linear(256, 10)
)
out = model(x)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;OrderedDict&amp;lt;/code&amp;gt; to name layers: &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;nn.Sequential(OrderedDict([(&apos;fc&apos;, nn.Linear(...))]))&amp;lt;/code&amp;gt;.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;7. &lt;code&gt;nn.ModuleList()&lt;/code&gt; / &lt;code&gt;nn.ModuleDict()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Registers sub-modules as a list or dictionary so that their parameters are correctly tracked and saved. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;layers = nn.ModuleList([nn.Linear(64, 64) for _ in range(6)])
for layer in layers:
    x = torch.relu(layer(x))

heads = nn.ModuleDict({
    &apos;cls&apos;: nn.Linear(64, 10),
    &apos;reg&apos;: nn.Linear(64, 1)
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Plain Python &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;list&amp;lt;/code&amp;gt; / &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;dict&amp;lt;/code&amp;gt; are not registered&amp;lt;/span&amp;gt; — &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;parameters()&amp;lt;/code&amp;gt; will miss them!&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;8. &lt;code&gt;nn.Embedding()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Maps integer indices to dense vectors (稠密向量). The standard Word Embedding Lookup Table (词向量查找表) in NLP. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;vocab_size, embed_dim = 10000, 128
emb = nn.Embedding(vocab_size, embed_dim)
ids = torch.randint(0, vocab_size, (16, 50))  # [batch, seq_len]
out = emb(ids)  # [16, 50, 128]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;padding_idx&amp;lt;/code&amp;gt; specifies a padding token whose embedding is excluded from gradient updates.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;9. &lt;code&gt;nn.LSTM()&lt;/code&gt; / &lt;code&gt;nn.GRU()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Long Short-Term Memory (长短时记忆) and Gated Recurrent Unit (门控循环单元) — classic recurrent layers for sequence data (序列数据). &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;lstm = nn.LSTM(input_size=128, hidden_size=256, num_layers=2, batch_first=True, dropout=0.2)
x = torch.rand(8, 50, 128)  # [batch, seq, feat]
out, (h, c) = lstm(x)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;batch_first=True&amp;lt;/code&amp;gt; sets the input format to [B, T, F], which is more intuitive. The default is [T, B, F].&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;10. &lt;code&gt;nn.MultiheadAttention()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Multi-head Self-Attention (多头自注意力机制) — the core component of the Transformer Architecture (Transformer架构). &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;attn = nn.MultiheadAttention(embed_dim=512, num_heads=8, batch_first=True)
x = torch.rand(4, 100, 512)
out, weights = attn(query=x, key=x, value=x)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;key_padding_mask&amp;lt;/code&amp;gt; to mask padding tokens; use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;attn_mask&amp;lt;/code&amp;gt; for Causal Masking (因果掩码) in decoders.&amp;lt;/div&amp;gt; &amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Every custom network inherits from &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;nn.Module&amp;lt;/code&amp;gt;; use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;ModuleList/Dict&amp;lt;/code&amp;gt; (not plain lists) to ensure parameters are tracked.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
</content:encoded></item><item><title>Activation Functions &amp; Loss Functions</title><link>https://lxy-alexander.github.io/blog/posts/pytorch/api/06activation-functions--loss-functions/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/pytorch/api/06activation-functions--loss-functions/</guid><description>Activation Functions &amp; Loss Functions</description><pubDate>Thu, 12 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;VI. Activation Functions &amp;amp; Loss Functions (激活函数与损失函数)&lt;/h1&gt;
&lt;h2&gt;1. &lt;code&gt;nn.ReLU()&lt;/code&gt; / &lt;code&gt;F.relu()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Rectified Linear Unit (修正线性单元): max(0, x). Alleviates gradient vanishing; the most widely used activation function. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import torch.nn.functional as F
x = torch.randn(4, 64)
out1 = F.relu(x)                     # functional call
relu = nn.ReLU(inplace=True)
out2 = relu(x)                       # module call (can go in Sequential)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;inplace=True&amp;lt;/code&amp;gt; saves memory but modifies the original tensor — &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;be careful when using autograd hooks&amp;lt;/span&amp;gt;.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. &lt;code&gt;nn.GELU()&lt;/code&gt; / &lt;code&gt;nn.SiLU()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;GELU&amp;lt;/code&amp;gt;: the Transformer standard activation. &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;SiLU&amp;lt;/code&amp;gt; (Swish): used in EfficientNet and mobile models. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gelu = nn.GELU()
silu = nn.SiLU()
x = torch.randn(4, 64)
print(gelu(x).shape)  # [4, 64]
print(silu(x).shape)  # [4, 64]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; BERT/GPT default to GELU; SiLU performs better on mobile-end models.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. &lt;code&gt;nn.Softmax()&lt;/code&gt; / &lt;code&gt;F.softmax()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Converts logits to a Probability Distribution (概率分布) summing to 1. Output layer for multi-class classification (多分类任务). &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;logits = torch.tensor([2.0, 1.0, 0.1])
probs = F.softmax(logits, dim=0)   # tensor([0.659, 0.242, 0.099])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Do NOT manually add Softmax when using &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;CrossEntropyLoss&amp;lt;/code&amp;gt;&amp;lt;/span&amp;gt; — it already includes it internally.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4. &lt;code&gt;nn.CrossEntropyLoss()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Multi-class Cross-Entropy Loss (多分类交叉熵损失) — internally fuses LogSoftmax + NLLLoss for numerical stability. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;criterion = nn.CrossEntropyLoss()
logits = torch.rand(8, 10)
labels = torch.randint(0, 10, (8,))
loss = criterion(logits, labels)
loss.backward()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; The &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;label_smoothing&amp;lt;/code&amp;gt; parameter (≥ PyTorch 1.8) effectively prevents Overconfidence (过度自信) and improves generalization.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;5. &lt;code&gt;nn.BCEWithLogitsLoss()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Binary / Multi-label Classification Loss (二分类/多标签分类损失). More numerically stable than applying Sigmoid then BCE. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;criterion = nn.BCEWithLogitsLoss()
logits = torch.rand(8, 1)                        # no sigmoid needed
targets = torch.randint(0, 2, (8, 1)).float()
loss = criterion(logits, targets)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; For multi-label classification, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;targets&amp;lt;/code&amp;gt; is a float matrix with each bit independent, not a class index.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;6. &lt;code&gt;nn.MSELoss()&lt;/code&gt; / &lt;code&gt;nn.L1Loss()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Mean Squared Error (均方误差) and Mean Absolute Error (平均绝对误差) — for continuous value prediction (连续值预测) / regression (回归). &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mse = nn.MSELoss()
mae = nn.L1Loss()
pred = torch.rand(4, 1)
target = torch.rand(4, 1)
print(mse(pred, target))
print(mae(pred, target))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; L1 is more robust to outliers. &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;SmoothL1Loss&amp;lt;/code&amp;gt; (Huber Loss) combines both — recommended for object detection (目标检测).&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;7. &lt;code&gt;nn.KLDivLoss()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; KL Divergence Loss (KL散度损失) — measures the difference between two probability distributions. Used in knowledge distillation (知识蒸馏) and VAE. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;kl = nn.KLDivLoss(reduction=&apos;batchmean&apos;)
log_p = F.log_softmax(student_logits, dim=-1)   # input: log prob
q = F.softmax(teacher_logits, dim=-1)            # target: prob
loss = kl(log_p, q)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Input must be log-probabilities; target must be probabilities&amp;lt;/span&amp;gt;. This matches the mathematical definition.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;8. &lt;code&gt;nn.LayerNorm()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Normalizes over the last N dimensions. Independent of batch size — the standard normalization in Transformers (Transformer标配). &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ln = nn.LayerNorm(normalized_shape=512)
x = torch.rand(4, 100, 512)
out = ln(x)  # shape [4, 100, 512]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Outperforms BatchNorm for variable-length NLP sequences (可变长序列) and small batch sizes.&amp;lt;/div&amp;gt; &amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;CrossEntropyLoss&amp;lt;/code&amp;gt; for multi-class, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;BCEWithLogitsLoss&amp;lt;/code&amp;gt; for multi-label, and &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;SmoothL1&amp;lt;/code&amp;gt; for regression — and never apply Softmax before CrossEntropy.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
</content:encoded></item><item><title>Optimizers &amp; Learning Rate Schedulers</title><link>https://lxy-alexander.github.io/blog/posts/pytorch/api/07optimizers--learning-rate-schedulers/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/pytorch/api/07optimizers--learning-rate-schedulers/</guid><description>Optimizers &amp; Learning Rate Schedulers</description><pubDate>Thu, 12 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;VII. Optimizers &amp;amp; Learning Rate Schedulers (优化器与学习率调度)&lt;/h1&gt;
&lt;h2&gt;1. &lt;code&gt;torch.optim.SGD()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Stochastic Gradient Descent (随机梯度下降). Supports momentum (动量), weight decay (权重衰减), and Nesterov momentum. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;optimizer = torch.optim.SGD(
    model.parameters(), lr=0.01, momentum=0.9,
    weight_decay=1e-4, nesterov=True
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; SGD+momentum is still common in CV; final accuracy sometimes surpasses Adam.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. &lt;code&gt;torch.optim.Adam()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Adaptive Moment Estimation (自适应矩估计). Combines AdaGrad and RMSProp. The default optimizer for most tasks. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;optimizer = torch.optim.Adam(
    model.parameters(), lr=1e-3, betas=(0.9, 0.999), weight_decay=1e-4
)
optimizer.zero_grad()
loss.backward()
optimizer.step()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; For NLP/Transformer scenarios, use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;AdamW&amp;lt;/code&amp;gt; (decoupled weight decay).&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. &lt;code&gt;torch.optim.AdamW()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Improved Adam with correctly decoupled L2 regularization (解耦L2正则). The go-to optimizer for training Transformers. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;optimizer = torch.optim.AdamW(
    model.parameters(), lr=5e-5, weight_decay=0.01
)  # Standard config for BERT/GPT fine-tuning
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; In original Adam, L2 regularization is entangled with adaptive learning rate scaling. AdamW fixes this by decoupling them.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4. &lt;code&gt;optimizer.zero_grad()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Clears all parameter gradient buffers. &amp;lt;strong&amp;gt;Must be called before each backward pass&amp;lt;/strong&amp;gt;. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;for epoch in range(10):
    for x, y in dataloader:
        optimizer.zero_grad()   # 1. clear
        pred = model(x)
        loss = criterion(pred, y)
        loss.backward()         # 3. backward
        optimizer.step()        # 4. update
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;zero_grad(set_to_none=True)&amp;lt;/code&amp;gt; uses less memory and is recommended for PyTorch ≥ 1.7.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;5. &lt;code&gt;lr_scheduler.StepLR()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Multiplies the learning rate by &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;gamma&amp;lt;/code&amp;gt; every &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;step_size&amp;lt;/code&amp;gt; epochs — stepwise decay (阶梯式衰减). &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from torch.optim import lr_scheduler
scheduler = lr_scheduler.StepLR(optimizer, step_size=30, gamma=0.1)
# Call at end of each epoch:
scheduler.step()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; In modern PyTorch, call &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;optimizer.step()&amp;lt;/code&amp;gt; &amp;lt;em&amp;gt;before&amp;lt;/em&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;scheduler.step()&amp;lt;/code&amp;gt;.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;6. &lt;code&gt;lr_scheduler.CosineAnnealingLR()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Cosine Annealing Decay (余弦退火调度): LR oscillates between [eta_min, lr] following a cosine curve. Excellent convergence. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;scheduler = lr_scheduler.CosineAnnealingLR(
    optimizer, T_max=50, eta_min=1e-6
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Combine with Warm Restarts (热重启, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;CosineAnnealingWarmRestarts&amp;lt;/code&amp;gt;) to escape local optima.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;7. &lt;code&gt;lr_scheduler.OneCycleLR()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Super-Convergence Training Strategy (超融合训练策略): LR rises then falls in a single cycle. Significantly reduces convergence time. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;scheduler = lr_scheduler.OneCycleLR(
    optimizer, max_lr=0.01,
    steps_per_epoch=len(loader), epochs=10
)
scheduler.step()  # call after every step (not epoch)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Set &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;max_lr&amp;lt;/code&amp;gt; to the highest stable LR found by a learning rate finder.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;8. &lt;code&gt;torch.optim.LBFGS()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Quasi-Newton second-order optimizer (拟牛顿二阶优化器). Suited for small datasets. Requires a &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;closure&amp;lt;/code&amp;gt; function. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;optimizer = torch.optim.LBFGS(model.parameters(), lr=1)

def closure():
    optimizer.zero_grad()
    loss = criterion(model(x), y)
    loss.backward()
    return loss

optimizer.step(closure)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Preferred for Neural Style Transfer (神经风格迁移) and other small-scale, high-precision convergence tasks.&amp;lt;/div&amp;gt; &amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Default to &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;AdamW&amp;lt;/code&amp;gt; + &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;CosineAnnealingLR&amp;lt;/code&amp;gt; for Transformers, and &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;SGD&amp;lt;/code&amp;gt; + &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;StepLR&amp;lt;/code&amp;gt; for classic CNN image tasks.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
</content:encoded></item><item><title>Data Loading &amp; Preprocessing</title><link>https://lxy-alexander.github.io/blog/posts/pytorch/api/08data-loading--preprocessing/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/pytorch/api/08data-loading--preprocessing/</guid><description>Data Loading &amp; Preprocessing</description><pubDate>Thu, 12 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;VIII. Data Loading &amp;amp; Preprocessing (数据加载与预处理)&lt;/h1&gt;
&lt;h2&gt;1. &lt;code&gt;torch.utils.data.Dataset&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Abstract base class for custom datasets. Must implement &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;&lt;strong&gt;len&lt;/strong&gt;&amp;lt;/code&amp;gt; and &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;&lt;strong&gt;getitem&lt;/strong&gt;&amp;lt;/code&amp;gt;. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from torch.utils.data import Dataset

class MyDataset(Dataset):
    def __init__(self, data, labels):
        self.data = data
        self.labels = labels

    def __len__(self):
        return len(self.data)

    def __getitem__(self, idx):
        return self.data[idx], self.labels[idx]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Put all preprocessing / augmentation inside &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;&lt;strong&gt;getitem&lt;/strong&gt;&amp;lt;/code&amp;gt; for Lazy Loading (懒加载).&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. &lt;code&gt;torch.utils.data.DataLoader&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Wraps a Dataset into an iterable batch loader with parallel reading (并行读取) and data shuffling (数据打乱). &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from torch.utils.data import DataLoader
loader = DataLoader(
    dataset=train_ds, batch_size=32,
    shuffle=True, num_workers=4, pin_memory=True
)
for x, y in loader:
    ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; On Windows, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;num_workers &amp;gt; 0&amp;lt;/code&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;requires &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;if &lt;strong&gt;name&lt;/strong&gt; == &apos;&lt;strong&gt;main&lt;/strong&gt;&apos;:&amp;lt;/code&amp;gt;&amp;lt;/span&amp;gt; guard.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. &lt;code&gt;torchvision.transforms&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Image preprocessing and data augmentation (数据增强) library. Chain multiple transforms with &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;Compose&amp;lt;/code&amp;gt;. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from torchvision import transforms
transform = transforms.Compose([
    transforms.Resize(256),
    transforms.RandomCrop(224),
    transforms.RandomHorizontalFlip(),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406],
                         std=[0.229, 0.224, 0.225])
])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; The Normalize parameters are ImageNet statistics. Keep them consistent when using Transfer Learning (迁移学习).&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4. &lt;code&gt;torchvision.datasets.ImageFolder&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Automatically builds an image classification dataset from directory structure — subdirectory names become class labels (类别标签). &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from torchvision.datasets import ImageFolder
# data/train/cat/*.jpg, data/train/dog/*.jpg
ds = ImageFolder(root=&apos;data/train&apos;, transform=transform)
print(ds.classes)   # [&apos;cat&apos;, &apos;dog&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Save the &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;class_to_idx&amp;lt;/code&amp;gt; dictionary alongside the model checkpoint.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;5. &lt;code&gt;torch.utils.data.random_split()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Randomly splits a dataset into train/validation subsets by specified lengths. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from torch.utils.data import random_split
n_val = int(len(dataset) * 0.2)
train_ds, val_ds = random_split(dataset, [len(dataset) - n_val, n_val])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Pass &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;generator=torch.Generator().manual_seed(42)&amp;lt;/code&amp;gt; for reproducible splits.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;6. &lt;code&gt;torchvision.models&lt;/code&gt; (pretrained)&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Provides many pre-trained models: ResNet, VGG, ViT, etc. Enables rapid Transfer Learning (迁移学习). &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import torchvision.models as models
model = models.resnet50(weights=&apos;IMAGENET1K_V2&apos;)
model.fc = nn.Linear(2048, 10)  # replace head for fine-tuning
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Freeze the backbone: &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;for p in model.parameters(): p.requires_grad = False&amp;lt;/code&amp;gt;.&amp;lt;/div&amp;gt; &amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;The data pipeline is: &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Dataset&amp;lt;/code&amp;gt; (what) → &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;transforms&amp;lt;/code&amp;gt; (how to augment) → &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;DataLoader&amp;lt;/code&amp;gt; (how to batch).&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
</content:encoded></item><item><title>Model Saving, Loading &amp; Deployment</title><link>https://lxy-alexander.github.io/blog/posts/pytorch/api/09model-saving-loading--deployment/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/pytorch/api/09model-saving-loading--deployment/</guid><description>Model Saving, Loading &amp; Deployment</description><pubDate>Thu, 12 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;IX. Model Saving, Loading &amp;amp; Deployment (模型保存、加载与部署)&lt;/h1&gt;
&lt;h2&gt;1. &lt;code&gt;torch.save()&lt;/code&gt; / &lt;code&gt;torch.load()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Serializes / deserializes any Python object (model, tensor, dict) to/from a file. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Recommended: save only the weights dict
torch.save(model.state_dict(), &apos;model_weights.pth&apos;)

# Load
state = torch.load(&apos;model_weights.pth&apos;, map_location=&apos;cpu&apos;)
model.load_state_dict(state)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Saving the entire model object couples code paths. &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Always save only &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;state_dict&amp;lt;/code&amp;gt;&amp;lt;/span&amp;gt;.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. &lt;code&gt;model.state_dict()&lt;/code&gt; / &lt;code&gt;load_state_dict()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Gets / loads an ordered dictionary of model parameters. The core interface for Transfer Learning (迁移学习) and checkpoint resuming (断点续训). &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;checkpoint = {
    &apos;epoch&apos;: epoch,
    &apos;model&apos;: model.state_dict(),
    &apos;optim&apos;: optimizer.state_dict(),
    &apos;loss&apos;: best_loss
}
torch.save(checkpoint, &apos;ckpt.pth&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;strict=False&amp;lt;/code&amp;gt; allows partial loading (skips missing keys) — common in transfer learning.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. &lt;code&gt;torch.jit.script()&lt;/code&gt; / &lt;code&gt;torch.jit.trace()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Compiles a model to TorchScript for deployment in Python-free environments (C++, mobile). &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# trace: follow execution path (no control flow)
traced = torch.jit.trace(model, torch.rand(1, 3, 224, 224))
traced.save(&apos;traced.pt&apos;)

# script: supports dynamic control flow
scripted = torch.jit.script(model)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Models with &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;if/for&amp;lt;/code&amp;gt; branches → use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;script&amp;lt;/code&amp;gt;. Pure forward-pass models → use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;trace&amp;lt;/code&amp;gt; (faster).&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4. &lt;code&gt;torch.onnx.export()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Exports a PyTorch model to ONNX format for cross-framework deployment (TensorRT, OpenVINO). &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;torch.onnx.export(
    model, torch.rand(1, 3, 224, 224), &apos;model.onnx&apos;,
    opset_version=17,
    input_names=[&apos;input&apos;], output_names=[&apos;output&apos;],
    dynamic_axes={&apos;input&apos;: {0: &apos;batch&apos;}}
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;dynamic_axes&amp;lt;/code&amp;gt; enables Dynamic Batch Size (动态批次大小) — essential for production deployment. Validate with &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;onnxruntime&amp;lt;/code&amp;gt;.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;5. &lt;code&gt;model.parameters()&lt;/code&gt; / &lt;code&gt;named_parameters()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Iterates over all learnable parameters. The &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;named_&amp;lt;/code&amp;gt; version also returns parameter names. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;total = sum(p.numel() for p in model.parameters() if p.requires_grad)
print(f&apos;Params: {total/1e6:.1f}M&apos;)

for name, p in model.named_parameters():
    print(name, p.shape)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Set layer-specific learning rates by passing &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;[{&apos;params&apos;: p, &apos;lr&apos;: lr}]&amp;lt;/code&amp;gt; list to the optimizer.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;6. &lt;code&gt;model.train()&lt;/code&gt; / &lt;code&gt;model.eval()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Switches between training and evaluation modes — affects Dropout and BatchNorm behavior. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;model.train()
for x, y in train_loader:
    loss = criterion(model(x), y)
    loss.backward(); optimizer.step()

model.eval()
with torch.no_grad():
    for x, y in val_loader:
        pred = model(x)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Forgetting &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;model.eval()&amp;lt;/code&amp;gt;&amp;lt;/span&amp;gt; is the most common reason for unstable inference results.&amp;lt;/div&amp;gt; &amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Always save &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;state_dict&amp;lt;/code&amp;gt; (not the model object), and remember the deploy path: PyTorch → TorchScript / ONNX → Runtime.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
</content:encoded></item><item><title>GPU Acceleration &amp; Distributed Training</title><link>https://lxy-alexander.github.io/blog/posts/pytorch/api/10gpu-acceleration--distributed-training/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/pytorch/api/10gpu-acceleration--distributed-training/</guid><description>GPU Acceleration &amp; Distributed Training</description><pubDate>Thu, 12 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;X. GPU Acceleration &amp;amp; Distributed Training (GPU加速与分布式训练)&lt;/h1&gt;
&lt;h2&gt;1. &lt;code&gt;Tensor.to()&lt;/code&gt; / &lt;code&gt;.cuda()&lt;/code&gt; / &lt;code&gt;.cpu()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Moves Tensors or models to a specified device (GPU/CPU). The fundamental operation for GPU training (GPU训练). &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;device = torch.device(&apos;cuda&apos; if torch.cuda.is_available() else &apos;cpu&apos;)
model = model.to(device)
x = x.to(device)
result = output.cpu().numpy()  # move back to CPU
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Model and data must be on the same device&amp;lt;/span&amp;gt;. Mixing CPU/GPU Tensors throws a runtime error.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. &lt;code&gt;torch.cuda.amp&lt;/code&gt; — Automatic Mixed Precision (自动混合精度)&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Automatically switches between FP16 and FP32, reducing VRAM usage and accelerating training. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from torch.cuda.amp import autocast, GradScaler
scaler = GradScaler()

with autocast():
    output = model(x)
    loss = criterion(output, y)

scaler.scale(loss).backward()
scaler.step(optimizer)
scaler.update()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;GradScaler&amp;lt;/code&amp;gt; prevents FP16 Gradient Underflow (梯度下溢). Recommended for all modern GPU training.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. &lt;code&gt;nn.DataParallel()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Single-machine multi-GPU Data Parallel (数据并行) training. Automatically splits batches and aggregates gradients. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if torch.cuda.device_count() &amp;gt; 1:
    model = nn.DataParallel(model)
model = model.to(&apos;cuda&apos;)

# Access original model
sd = model.module.state_dict() if isinstance(model, nn.DataParallel) else model.state_dict()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;DataParallel efficiency is limited by Python GIL&amp;lt;/span&amp;gt;. For large-scale training, use DistributedDataParallel (DDP).&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4. &lt;code&gt;nn.parallel.DistributedDataParallel()&lt;/code&gt; — DDP&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Distributed Data Parallel: one process per GPU. Communication efficiency far exceeds DataParallel. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import torch.distributed as dist
dist.init_process_group(&apos;nccl&apos;)
local_rank = int(os.environ[&apos;LOCAL_RANK&apos;])
model = model.to(local_rank)
model = nn.parallel.DistributedDataParallel(model, device_ids=[local_rank])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Launch with &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;torchrun --nproc_per_node=4 train.py&amp;lt;/code&amp;gt;. Pair with &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;DistributedSampler&amp;lt;/code&amp;gt;.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;5. &lt;code&gt;torch.cuda.memory_summary()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Prints detailed GPU VRAM usage to help diagnose Out-of-Memory (OOM, 显存溢出) issues. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;print(torch.cuda.memory_summary())

alloc = torch.cuda.memory_allocated()
total = torch.cuda.get_device_properties(0).total_memory
print(f&apos;{alloc/1e9:.1f}GB used&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; After OOM, call &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;torch.cuda.empty_cache()&amp;lt;/code&amp;gt; to release cached memory — but it cannot free memory in active use.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;6. &lt;code&gt;torch.compile()&lt;/code&gt; (PyTorch 2.0+)&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Compiles the model into optimized kernels using graph capture and Triton operators, dramatically accelerating training/inference. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;model = torch.compile(model)

# Different modes
model = torch.compile(model, mode=&apos;reduce-overhead&apos;, fullgraph=True)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; First run has Compilation Overhead (编译开销) (warmup). &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;fullgraph=True&amp;lt;/code&amp;gt; forbids graph breaks for maximum performance.&amp;lt;/div&amp;gt; &amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Use AMP for every GPU training job; prefer DDP over DataParallel for multi-GPU; add &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;torch.compile()&amp;lt;/code&amp;gt; as a one-line speed boost in PyTorch 2.x.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
</content:encoded></item><item><title>Advanced Features &amp; Utilities</title><link>https://lxy-alexander.github.io/blog/posts/pytorch/api/11advanced-features--utilities/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/pytorch/api/11advanced-features--utilities/</guid><description>Advanced Features &amp; Utilities</description><pubDate>Thu, 12 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;XI. Advanced Features &amp;amp; Utilities (高级特性与实用工具)&lt;/h1&gt;
&lt;h2&gt;1. &lt;code&gt;torch.nn.utils.clip_grad_norm_()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Clips the global L2 norm of all parameter gradients to prevent Gradient Explosion (梯度爆炸). Essential for RNN/Transformer training. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;optimizer.zero_grad()
loss.backward()
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
optimizer.step()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;max_norm=1.0&amp;lt;/code&amp;gt; is standard for Transformers. &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Must call after &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;backward()&amp;lt;/code&amp;gt;, before &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;step()&amp;lt;/code&amp;gt;&amp;lt;/span&amp;gt;.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. &lt;code&gt;torch.nn.utils.weight_norm()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Decomposes parameters into direction and magnitude, accelerating convergence. Used in WaveNet and generative models. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from torch.nn.utils import weight_norm, remove_weight_norm
wn_conv = weight_norm(nn.Conv1d(64, 64, 3, padding=1))
remove_weight_norm(wn_conv)  # merge before deployment
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Always call &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;remove_weight_norm&amp;lt;/code&amp;gt; before deployment to merge decomposed parameters.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. &lt;code&gt;torch.nn.functional.interpolate()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Upsamples or downsamples feature maps with bilinear, nearest, bicubic, etc. interpolation modes. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;x = torch.rand(1, 64, 28, 28)
up = F.interpolate(x, scale_factor=2, mode=&apos;bilinear&apos;, align_corners=False)
print(up.shape)  # [1, 64, 56, 56]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;align_corners=False&amp;lt;/code&amp;gt; matches TensorFlow&apos;s default behavior — important when migrating models.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4. &lt;code&gt;torch.nn.functional.grid_sample()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Samples from a feature map at normalized grid coordinates. The core of Spatial Transformer Networks (空间变换网络, STN). &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;theta = torch.eye(2, 3, dtype=torch.float).unsqueeze(0)
grid = F.affine_grid(theta, x.size())
out = F.grid_sample(x, grid, mode=&apos;bilinear&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Coordinate range is [-1, 1]. &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;padding_mode=&apos;reflection&apos;&amp;lt;/code&amp;gt; is more natural for image borders.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;5. &lt;code&gt;torch.einsum()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Einstein Summation (爱因斯坦求和): expresses complex tensor operations as a concise string equation. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;c = torch.einsum(&apos;ij,jk-&amp;gt;ik&apos;, a, b)  # matrix multiply

# Attention scores: Q:[B,H,L,D], K:[B,H,L,D]
scores = torch.einsum(&apos;bhld,bhmd-&amp;gt;bhlm&apos;, Q, K)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Mastering &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;einsum&amp;lt;/code&amp;gt; dramatically simplifies Transformer and graph neural network code.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;6. &lt;code&gt;torch.profiler.profile()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Performance profiler that records per-operator CPU/GPU time and memory usage to locate bottlenecks. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from torch.profiler import profile, ProfilerActivity
with profile(activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA]) as prof:
    model(x)
print(prof.key_averages().table(sort_by=&apos;cuda_time_total&apos;))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Export a Chrome trace with &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;prof.export_chrome_trace()&amp;lt;/code&amp;gt; for visual bottleneck analysis in a browser.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;7. &lt;code&gt;torch.nn.init.*&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Provides Xavier, Kaiming, Orthogonal and other Parameter Initialization (参数初始化) strategies. Directly impacts training stability. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def init_weights(m):
    if isinstance(m, nn.Linear):
        nn.init.kaiming_normal_(m.weight, mode=&apos;fan_out&apos;, nonlinearity=&apos;relu&apos;)
        nn.init.zeros_(m.bias)

model.apply(init_weights)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Sigmoid/Tanh → Xavier; ReLU family → Kaiming; Transformer → Orthogonal initialization.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;8. &lt;code&gt;torch.Tensor.item()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Converts a single-element Tensor to a Python scalar. Commonly used to log loss values. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;loss = criterion(output, label)
loss_val = loss.item()        # detaches from graph
print(f&apos;Loss: {loss_val:.4f}&apos;)

# WRONG: total_loss += loss  ← graph grows → OOM
# CORRECT:
total_loss += loss.item()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Accumulating &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;loss&amp;lt;/code&amp;gt; tensors directly causes the computation graph to grow unboundedly → OOM&amp;lt;/span&amp;gt;. Always use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;.item()&amp;lt;/code&amp;gt;.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;9. &lt;code&gt;torch.Tensor.clone()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Creates a deep copy (深拷贝) of a Tensor with fully independent data, while preserving gradient propagation. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;x = torch.rand(3, requires_grad=True)
y = x.clone()           # gradient can still propagate
z = x.detach().clone()  # gradient detached

buf = torch.empty(3)
buf.copy_(x)            # in-place copy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;clone().detach()&amp;lt;/code&amp;gt; for Target Network (目标网络) parameter copying in RL / momentum update.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;10. &lt;code&gt;torch.where()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Conditional selection: returns values from &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;x&amp;lt;/code&amp;gt; where condition is True, else from &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;y&amp;lt;/code&amp;gt;. Vectorized if-else. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;x = torch.tensor([-1., 2., -3., 4.])
y = torch.zeros_like(x)
out = torch.where(x &amp;gt; 0, x, y)  # tensor([0., 2., 0., 4.])  — manual ReLU
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Both branches participate in gradient computation; the gradient of the unselected branch is zero.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;11. &lt;code&gt;torch.gather()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Gathers values from a source Tensor by an index Tensor — enables irregular indexing (不规则索引) like NMS. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;logits = torch.rand(4, 10)
targets = torch.tensor([3, 7, 1, 5]).unsqueeze(1)  # [4, 1]
scores = logits.gather(dim=1, index=targets)         # [4, 1]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Core tool for sequence decoding, top-k sampling, and Q-value selection in Reinforcement Learning (强化学习).&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;12. &lt;code&gt;torch.scatter_()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Scatters values from &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;src&amp;lt;/code&amp;gt; into &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;self&amp;lt;/code&amp;gt; at positions specified by &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;index&amp;lt;/code&amp;gt; (in-place). &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;y = torch.zeros(4, 5)
labels = torch.tensor([[2], [0], [4], [1]])
y.scatter_(dim=1, index=labels, value=1.0)  # one-hot encoding
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;scatter_add_&amp;lt;/code&amp;gt; implements segment sum — the fundamental primitive for Graph Neural Network (图神经网络) message aggregation.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;13. &lt;code&gt;torch.masked_fill()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Fills positions where the mask is True with a specified value. Essential for Attention Masking (注意力掩码). &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;L = 5
mask = torch.triu(torch.ones(L, L), diagonal=1).bool()
scores = torch.rand(L, L)
scores = scores.masked_fill(mask, float(&apos;-inf&apos;))  # causal mask
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Transformer decoder&apos;s Causal Self-Attention (因果自注意力) must use this to block future information.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;14. &lt;code&gt;torch.nn.utils.rnn.pad_sequence()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Pads a list of variable-length sequences to a uniform-length tensor for NLP batch processing. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from torch.nn.utils.rnn import pad_sequence
seqs = [torch.tensor([1, 2, 3]), torch.tensor([4, 5]), torch.tensor([6])]
padded = pad_sequence(seqs, batch_first=True)  # [3, 3]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Combine with &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;pack_padded_sequence&amp;lt;/code&amp;gt; to skip LSTM computation on padding positions.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;15. &lt;code&gt;nn.TransformerEncoder()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Multi-layer Transformer Encoder with built-in Multi-head Attention + FFN + Residual Normalization. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;enc_layer = nn.TransformerEncoderLayer(d_model=512, nhead=8, dim_feedforward=2048, batch_first=True)
encoder = nn.TransformerEncoder(enc_layer, num_layers=6)
out = encoder(src, src_key_padding_mask=m)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;norm_first=True&amp;lt;/code&amp;gt; (Pre-LN) trains more stably — recommended for large models.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;16. &lt;code&gt;torch.Tensor.contiguous()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Returns a Contiguous Memory (连续内存) copy of the Tensor. Returns itself (zero overhead) if already contiguous. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;x = torch.rand(4, 5, 6)
y = x.permute(2, 0, 1)         # non-contiguous
print(y.is_contiguous())        # False
z = y.contiguous()
w = z.view(6, -1)               # safe to view now
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; In most cases, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;reshape&amp;lt;/code&amp;gt; handles this automatically. Call manually only when contiguous memory is truly required.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;17. &lt;code&gt;torch.Tensor.type()&lt;/code&gt; / &lt;code&gt;.to(dtype)&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Converts the Tensor&apos;s Data Type (数据类型): float32 ↔ float16 ↔ int64, etc. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;x = torch.tensor([1, 2, 3])
f = x.float()   # int → float32
h = x.half()    # float32 → float16
l = x.long()    # → int64
y = x.to(dtype=torch.float32)  # recommended
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Cross-entropy labels need &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;long()&amp;lt;/code&amp;gt;; normalized image pixels need &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;float()&amp;lt;/code&amp;gt;; inference acceleration uses &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;half()&amp;lt;/code&amp;gt;.&amp;lt;/div&amp;gt; &amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;The advanced toolkit: &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;clip_grad_norm_&amp;lt;/code&amp;gt; (stability), &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;einsum&amp;lt;/code&amp;gt; (clarity), &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;gather/scatter&amp;lt;/code&amp;gt; (indexing), &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;masked_fill&amp;lt;/code&amp;gt; (attention), &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;item()&amp;lt;/code&amp;gt; (memory safety).&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
</content:encoded></item><item><title>Convolution, Pooling &amp; Normalization Layers</title><link>https://lxy-alexander.github.io/blog/posts/pytorch/api/12convolution-pooling--normalization-layers/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/pytorch/api/12convolution-pooling--normalization-layers/</guid><description>Convolution, Pooling &amp; Normalization Layers</description><pubDate>Thu, 12 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;XII. Convolution, Pooling &amp;amp; Normalization Layers (卷积、池化与正则化层)&lt;/h1&gt;
&lt;h2&gt;1. &lt;code&gt;nn.MaxPool2d()&lt;/code&gt; / &lt;code&gt;nn.AvgPool2d()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; 2D Max / Average Pooling (最大/平均池化). Downsamples feature maps using a sliding window, reducing spatial size. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pool = nn.MaxPool2d(kernel_size=2, stride=2)
x = torch.rand(8, 64, 28, 28)
out = pool(x)  # [8, 64, 14, 14]

gap = nn.AdaptiveAvgPool2d((1, 1))
feat = gap(out)  # [8, 64, 1, 1] — global avg pool
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;AdaptiveAvgPool2d((1,1))&amp;lt;/code&amp;gt; is the standard Global Average Pooling (全局平均池化) in ResNet&apos;s classification head.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. &lt;code&gt;nn.ConvTranspose2d()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Transposed Convolution (转置卷积 / 反卷积) for upsampling. Core layer in U-Net and GAN Generators. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;deconv = nn.ConvTranspose2d(in_channels=64, out_channels=32, kernel_size=4, stride=2, padding=1)
x = torch.rand(4, 64, 14, 14)
out = deconv(x)  # [4, 32, 28, 28]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;kernel=4, stride=2, padding=1&amp;lt;/code&amp;gt; is the classic recipe that exactly doubles the spatial size.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. &lt;code&gt;nn.GroupNorm()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Splits channels into groups and normalizes within each group. Independent of batch size — outperforms BN in small-batch scenarios. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gn = nn.GroupNorm(num_groups=8, num_channels=32)
x = torch.rand(2, 32, 64, 64)
out = gn(x)  # shape unchanged
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Recommended for object detection / instance segmentation (small batch). &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;num_groups=1&amp;lt;/code&amp;gt; ≡ LayerNorm.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4. &lt;code&gt;nn.InstanceNorm2d()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Normalizes each sample and each channel independently. Standard normalization for Image Style Transfer (图像风格迁移). &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;inst = nn.InstanceNorm2d(num_features=64, affine=True)
x = torch.rand(4, 64, 256, 256)
out = inst(x)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;affine=True&amp;lt;/code&amp;gt; adds learnable scale/shift parameters for better style adaptation.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;5. &lt;code&gt;nn.Upsample()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Module-form wrapper around &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;F.interpolate&amp;lt;/code&amp;gt;. No learnable parameters; can be placed in &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;Sequential&amp;lt;/code&amp;gt;. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;up = nn.Upsample(scale_factor=2, mode=&apos;bilinear&apos;, align_corners=False)
x = torch.rand(4, 32, 14, 14)
out = up(x)  # [4, 32, 28, 28]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Combine with ConvTranspose2d for learnable upsampling control (learned vs fixed).&amp;lt;/div&amp;gt; &amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Normalization choice: BN (large batch) → GN (small batch/detection) → LN (NLP) → IN (style transfer).&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
</content:encoded></item><item><title>Indexing, Selection &amp; Advanced Operations</title><link>https://lxy-alexander.github.io/blog/posts/pytorch/api/13indexing-selection--advanced-operations/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/pytorch/api/13indexing-selection--advanced-operations/</guid><description>Indexing, Selection &amp; Advanced Operations</description><pubDate>Thu, 12 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;XIII. Indexing, Selection &amp;amp; Advanced Operations (索引、选择与高级操作)&lt;/h1&gt;
&lt;h2&gt;1. &lt;code&gt;torch.nonzero()&lt;/code&gt; / &lt;code&gt;torch.argwhere()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Returns the coordinates of all non-zero (or True) elements. Used for Sparse Operations (稀疏操作). &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;x = torch.tensor([[0, 1, 0], [2, 0, 3]])
idx = torch.nonzero(x)           # tensor([[0,1],[1,0],[1,2]])
idx2 = torch.argwhere(x &amp;gt; 0)     # PyTorch 1.9+
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;2. &lt;code&gt;torch.index_select()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Selects elements along a dimension by index tensor. Similar to NumPy fancy indexing. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;x = torch.rand(5, 4)
idx = torch.tensor([0, 2, 4])
out = torch.index_select(x, dim=0, index=idx)  # shape [3, 4]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Index must be a 1D LongTensor; more efficient than boolean masking for this case.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. &lt;code&gt;torch.masked_select()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Selects elements by boolean mask. Returns a flattened 1D Tensor. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;x = torch.randn(3, 3)
mask = x &amp;gt; 0
pos_vals = torch.masked_select(x, mask)  # all positive values, 1D
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Result is always 1D. Use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;masked_fill&amp;lt;/code&amp;gt; or &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;scatter_&amp;lt;/code&amp;gt; to reconstruct shape.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4. &lt;code&gt;torch.sort()&lt;/code&gt; / &lt;code&gt;torch.argsort()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Sorts a Tensor along a dimension, returning sorted values and original indices. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;x = torch.tensor([3., 1., 4., 1., 5., 9.])
vals, idx = torch.sort(x, descending=True)  # [9,5,4,3,1,1]
order = torch.argsort(x)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Key step in NMS (Non-Maximum Suppression, 非极大抑制): sort boxes by confidence descending.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;5. &lt;code&gt;torch.cumsum()&lt;/code&gt; / &lt;code&gt;torch.cumprod()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Cumulative sum (累积和) or cumulative product (累积积) along a dimension. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;x = torch.tensor([1., 2., 3., 4.])
print(torch.cumsum(x, dim=0))   # tensor([1., 3., 6., 10.])
print(torch.cumprod(x, dim=0))  # tensor([1., 2., 6., 24.])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;cumsum&amp;lt;/code&amp;gt; is an efficient alternative for generating causal attention masks (lower triangular).&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;6. &lt;code&gt;torch.flip()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Flips a Tensor along specified dimensions — mirror flip augmentation or reverse operation. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;x = torch.tensor([[1, 2, 3], [4, 5, 6]])
h = torch.flip(x, dims=[1])  # horizontal: [[3,2,1],[6,5,4]]
v = torch.flip(x, dims=[0])  # vertical: [[4,5,6],[1,2,3]]
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;7. torch.bucketize()`&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Assigns continuous values to discrete buckets (离散化) by given boundaries. Analogous to NumPy&apos;s &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;digitize&amp;lt;/code&amp;gt;. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;boundaries = torch.tensor([0.0, 0.5, 1.0])
x = torch.tensor([-0.1, 0.3, 0.7, 1.5])
bins = torch.bucketize(x, boundaries)  # tensor([0, 1, 2, 3])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Useful for Feature Engineering (特征工程) — binning continuous features and custom quantile normalization.&amp;lt;/div&amp;gt; &amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;gather&amp;lt;/code&amp;gt; picks values by index; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;scatter_&amp;lt;/code&amp;gt; puts values by index; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;masked_fill&amp;lt;/code&amp;gt; overwrites by condition.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
</content:encoded></item><item><title>Randomness &amp; Reproducibility</title><link>https://lxy-alexander.github.io/blog/posts/pytorch/api/14randomness--reproducibility/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/pytorch/api/14randomness--reproducibility/</guid><description>Randomness &amp; Reproducibility</description><pubDate>Thu, 12 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;XIV. Randomness &amp;amp; Reproducibility (随机性与可复现性)&lt;/h1&gt;
&lt;h2&gt;1. &lt;code&gt;torch.manual_seed()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Sets the global random seed to ensure consistent results across runs (实验可复现性). &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import random, numpy as np

def set_seed(seed=42):
    torch.manual_seed(seed)
    torch.cuda.manual_seed_all(seed)
    np.random.seed(seed)
    random.seed(seed)

set_seed(42)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Also set &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;torch.backends.cudnn.deterministic=True&amp;lt;/code&amp;gt; for fully deterministic behavior.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. &lt;code&gt;torch.Generator()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Creates an independent Random Number Generator (随机数生成器) object, avoiding interference with the global seed. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;g = torch.Generator()
g.manual_seed(42)
x = torch.rand(3, generator=g)  # independent state

loader = DataLoader(ds, shuffle=True, generator=g)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Safer than global seeds in multi-thread / multi-process scenarios.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. &lt;code&gt;torch.distributions.*&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Probability distribution library supporting sampling and &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;log_prob&amp;lt;/code&amp;gt; computation. Foundation for VAE and Reinforcement Learning (强化学习). &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from torch.distributions import Normal, Categorical

# VAE reparameterization trick (重参数化技巧)
dist = Normal(loc=mu, scale=torch.exp(logvar))
z = dist.rsample()      # differentiable sampling
log_p = dist.log_prob(z)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;rsample()&amp;lt;/code&amp;gt; is differentiable (reparameterization); &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;sample()&amp;lt;/code&amp;gt; is not (for policy gradients).&amp;lt;/div&amp;gt; &amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Always call &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;set_seed()&amp;lt;/code&amp;gt; at the start of every experiment and use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;rsample()&amp;lt;/code&amp;gt; for differentiable stochastic layers.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
</content:encoded></item><item><title>Utilities &amp; Performance Tips</title><link>https://lxy-alexander.github.io/blog/posts/pytorch/api/15utilities--performance-tips/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/pytorch/api/15utilities--performance-tips/</guid><description>Utilities &amp; Performance Tips</description><pubDate>Thu, 12 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;XV. Utilities &amp;amp; Performance Tips (实用工具与性能技巧)&lt;/h1&gt;
&lt;h2&gt;1. &lt;code&gt;torch.no_grad()&lt;/code&gt; vs &lt;code&gt;torch.inference_mode()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;inference_mode&amp;lt;/code&amp;gt; is more aggressive than &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;no_grad&amp;lt;/code&amp;gt;: it skips version counting entirely for faster pure inference. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;with torch.no_grad():
    out1 = model(x)

@torch.inference_mode()
def predict(x):
    return model(x)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; PyTorch 1.9+ recommends &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;inference_mode&amp;lt;/code&amp;gt; for pure inference — faster than &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;no_grad&amp;lt;/code&amp;gt;.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. &lt;code&gt;torch.Tensor.pin_memory()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Pins CPU Tensors to Page-locked Memory (页锁定内存), dramatically accelerating CPU→GPU data transfer. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;loader = DataLoader(dataset, pin_memory=True, num_workers=4)
for x, y in loader:
    x = x.to(&apos;cuda&apos;, non_blocking=True)  # async transfer
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;pin_memory=True&amp;lt;/code&amp;gt; + &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;non_blocking=True&amp;lt;/code&amp;gt; enables CPU data loading to overlap with GPU computation (Pipeline, 流水线并行).&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. &lt;code&gt;torch.utils.checkpoint.checkpoint()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Gradient Checkpointing (梯度检查点): trades recomputation for VRAM savings. Can save 50%+ VRAM for very large models. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from torch.utils.checkpoint import checkpoint

def forward(self, x):
    x = checkpoint(self.heavy_block, x)  # no intermediate activations saved
    return self.head(x)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Trades ~30% extra training time for drastically reduced VRAM — enables training models that would otherwise OOM.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4. &lt;code&gt;torch.nn.functional.one_hot()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Converts integer class indices to One-hot Encoding (独热编码) tensors. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;labels = torch.tensor([0, 2, 1, 3])
one_hot = F.one_hot(labels, num_classes=4).float()
# tensor([[1,0,0,0],[0,0,1,0],[0,1,0,0],[0,0,0,1]])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Output defaults to LongTensor. Call &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;.float()&amp;lt;/code&amp;gt; before participating in loss computation.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;5. &lt;code&gt;torch.nn.functional.cosine_similarity()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Computes Cosine Similarity (余弦相似度) between two groups of vectors. Core metric in Contrastive Learning (对比学习) — SimCLR, CLIP. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a = torch.randn(8, 128)
b = torch.randn(8, 128)
sim = F.cosine_similarity(a, b, dim=-1)  # shape [8], range [-1, 1]

# Similarity matrix for contrastive learning
mat = a @ b.T  # [8, 8]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Equivalent to (a/||a||) · (b/||b||). In contrastive learning, L2-normalize first then use dot product.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;6. &lt;code&gt;nn.SyncBatchNorm.convert_sync_batchnorm()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Replaces all BatchNorm layers with cross-GPU Synchronized BatchNorm (跨GPU同步批归一化). Essential for DDP training with BN. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;model = MyModel()
model = nn.SyncBatchNorm.convert_sync_batchnorm(model)  # before DDP wrap
model = nn.parallel.DistributedDataParallel(model, device_ids=[local_rank])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Must convert before wrapping with DDP&amp;lt;/span&amp;gt;. Without SyncBN, each GPU computes its own BN statistics — inaccurate.&amp;lt;/div&amp;gt; &amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Performance stack: &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;pin_memory + non_blocking&amp;lt;/code&amp;gt; → &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;AMP&amp;lt;/code&amp;gt; → &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;torch.compile&amp;lt;/code&amp;gt; → &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;gradient_checkpoint&amp;lt;/code&amp;gt; (if OOM).&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:20px 24px;margin-top:32px&quot;&amp;gt; &amp;lt;span style=&quot;color:#3B5BDB;font-weight:700;font-size:16px&quot;&amp;gt;🎯 Master Summary — 120 APIs in 6 Core Concepts&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt; &amp;lt;strong&amp;gt;1. Tensor Ops&amp;lt;/strong&amp;gt;: Create (&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;tensor/zeros/rand&amp;lt;/code&amp;gt;) → Shape (&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;reshape/permute/cat&amp;lt;/code&amp;gt;) → Math (&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;matmul/clamp/topk&amp;lt;/code&amp;gt;)&amp;lt;br&amp;gt; &amp;lt;strong&amp;gt;2. Autograd&amp;lt;/strong&amp;gt;: &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;requires_grad&amp;lt;/code&amp;gt; → &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;backward()&amp;lt;/code&amp;gt; → &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;no_grad / detach&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt; &amp;lt;strong&amp;gt;3. Networks&amp;lt;/strong&amp;gt;: &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;nn.Module&amp;lt;/code&amp;gt; → Layers (&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Linear/Conv2d/LSTM/Attention&amp;lt;/code&amp;gt;) → Norm + Dropout&amp;lt;br&amp;gt; &amp;lt;strong&amp;gt;4. Training&amp;lt;/strong&amp;gt;: Loss → Optimizer (&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;AdamW&amp;lt;/code&amp;gt;) → Scheduler → &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;clip_grad_norm_&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt; &amp;lt;strong&amp;gt;5. Data&amp;lt;/strong&amp;gt;: &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Dataset&amp;lt;/code&amp;gt; → &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;transforms&amp;lt;/code&amp;gt; → &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;DataLoader&amp;lt;/code&amp;gt; → pretrained models&amp;lt;br&amp;gt; &amp;lt;strong&amp;gt;6. Deploy&amp;lt;/strong&amp;gt;: &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;state_dict&amp;lt;/code&amp;gt; → TorchScript / ONNX → AMP / DDP / &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;torch.compile&amp;lt;/code&amp;gt; &amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Chunked Prefill</title><link>https://lxy-alexander.github.io/blog/posts/llm/chunked-prefill/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/llm/chunked-prefill/</guid><description>Chunked Prefill</description><pubDate>Tue, 10 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;I. Chunked Prefill (分块预填充)&lt;/h1&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-c69d652d2a0747fab9aad1fab48ff742.r2.dev/images/image-20260415031037674&quot; alt=&quot;image-20260415031037674&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;1. Background&lt;/h2&gt;
&lt;p&gt;In Large Language Model (大语言模型) inference, processing a request has two phases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Prefill (预填充)&lt;/strong&gt;: The model ==processes all input prompt tokens in parallel, building the KV Cache== (键值缓存). This is compute-bound (计算密集型).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Decode (解码)&lt;/strong&gt;: The ==model generates one output token per iteration==, autoregressively. This is memory-bandwidth-bound (内存带宽密集型).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;==Without chunked prefill, a single long-prompt prefill request can &lt;strong&gt;monopolize&lt;/strong&gt; the GPU for many milliseconds, blocking decode iterations for other requests== and inflating the Time to First Token (TTFT, 首个令牌时间) and Inter-Token Latency (ITL, 令牌间延迟) for concurrent users.&lt;/p&gt;
&lt;p&gt;==It improves GPU utilization, reduces latency, increases throughput, and ensures fair scheduling across requests.==&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. What Is Chunked Prefill?&lt;/h2&gt;
&lt;p&gt;==&lt;strong&gt;Chunked Prefill&lt;/strong&gt; splits a long prefill sequence into fixed-size pieces called &lt;strong&gt;chunks&lt;/strong&gt;, In the same GPU iteration, it processes: - decode tokens from some requests  - prefill chunks from other requests==.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Key Insight: Instead of &quot;finish all prefill, then decode,&quot; we interleave them so the GPU is always doing useful, mixed work.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;1) Core Idea&lt;/h3&gt;
&lt;p&gt;$$
\text{Iteration}&lt;em&gt;t = \underbrace{\text{Prefill Chunk}&lt;em&gt;k}&lt;/em&gt;{\text{partial prompt}} + \underbrace{\text{Decode Tokens}&lt;/em&gt;{r_1, r_2, \ldots}}_{\text{running requests}}
$$&lt;/p&gt;
&lt;p&gt;Each iteration processes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;chunk&lt;/strong&gt; of &lt;code&gt;C&lt;/code&gt; tokens from one (or more) prefilling requests.&lt;/li&gt;
&lt;li&gt;All current &lt;strong&gt;decode&lt;/strong&gt; tokens from already-started requests.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2) Chunk Size (块大小)&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;chunk_size&lt;/code&gt; (e.g., 512 or 1024 tokens) is a tunable hyperparameter (超参数):&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Chunk size&lt;/th&gt;
&lt;th&gt;Effect&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Smaller&lt;/td&gt;
&lt;td&gt;Lower TTFT jitter, better fairness, more scheduling overhead&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Larger&lt;/td&gt;
&lt;td&gt;Higher GPU utilization, less overhead, longer decode stalls&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;3. Algorithm&lt;/h2&gt;
&lt;h3&gt;1) Scheduler Logic (调度器逻辑)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# Pseudocode — runs once per GPU iteration
def schedule_iteration(prefill_queue, decode_queue, chunk_size):
    batch = []

    # Step 1: Add decode tokens for all running requests
    for req in decode_queue:
        batch.append(DecodeToken(req))          # 1 token per running request

    # Step 2: Fill remaining compute budget with one prefill chunk
    if prefill_queue:
        req = prefill_queue[0]
        start = req.processed_tokens            # where we left off
        end   = min(start + chunk_size, len(req.prompt))
        batch.append(PrefillChunk(req, start, end))
        req.processed_tokens = end
        if req.processed_tokens == len(req.prompt):
            prefill_queue.pop(0)                # prefill done → move to decode_queue

    return batch
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) KV Cache Construction (键值缓存构建)&lt;/h3&gt;
&lt;p&gt;Because prefill is chunked, the KV cache is filled &lt;strong&gt;incrementally&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;$$
\text{KV}[0:n] = \text{KV}[0:C] ;|; \text{KV}[C:2C] ;|; \cdots ;|; \text{KV}[\lfloor n/C \rfloor \cdot C : n]
$$&lt;/p&gt;
&lt;p&gt;Each chunk appends its computed key-value pairs to the existing cache. This is correct because attention (注意力机制) only requires the cache to be causally complete — past tokens never need updating.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4. Runnable Example&lt;/h2&gt;
&lt;p&gt;The following standalone example simulates chunked prefill scheduling:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# chunked_prefill_demo.py
# Simulates chunked prefill scheduling on a toy model.
# Requires: pip install numpy

import numpy as np

CHUNK_SIZE = 4          # tokens processed per prefill chunk per iteration
VOCAB = 32              # toy vocabulary size
D_MODEL = 8             # tiny embedding dimension

class Request:
    &quot;&quot;&quot;A single inference request (推理请求).&quot;&quot;&quot;
    def __init__(self, req_id: str, prompt_tokens: list[int]):
        self.req_id = req_id
        self.prompt = prompt_tokens
        self.processed = 0          # how many prompt tokens have been prefilled
        self.kv_cache: list = []    # accumulated KV pairs (simulated as ints)
        self.output_tokens: list[int] = []
        self.is_decoding = False

    def is_prefill_done(self) -&amp;gt; bool:
        return self.processed &amp;gt;= len(self.prompt)

def fake_attention(tokens: list[int], kv_cache: list) -&amp;gt; list:
    &quot;&quot;&quot;Simulates attention output — returns dummy KV entries.&quot;&quot;&quot;
    return [t * 2 for t in tokens]   # fake KV = token_id * 2

def fake_decode(kv_cache: list, rng: np.random.Generator) -&amp;gt; int:
    &quot;&quot;&quot;Samples a token given the completed KV cache.&quot;&quot;&quot;
    return int(rng.integers(0, VOCAB))

def run_chunked_prefill(requests: list[Request], max_new_tokens: int = 5):
    rng = np.random.default_rng(42)
    prefill_queue = list(requests)
    decode_queue: list[Request] = []
    iteration = 0

    while prefill_queue or decode_queue:
        iteration += 1
        print(f&quot;\n--- Iteration {iteration} ---&quot;)

        # ── Decode step (解码步骤): one token per running request ──────────
        for req in list(decode_queue):
            tok = fake_decode(req.kv_cache, rng)
            req.output_tokens.append(tok)
            print(f&quot;  [Decode] {req.req_id}: generated token {tok}&quot;)
            if len(req.output_tokens) &amp;gt;= max_new_tokens:
                decode_queue.remove(req)
                print(f&quot;  [Done]   {req.req_id} finished.&quot;)

        # ── Prefill chunk (预填充分块): one chunk from the head of the queue ─
        if prefill_queue:
            req = prefill_queue[0]
            start = req.processed
            end   = min(start + CHUNK_SIZE, len(req.prompt))
            chunk = req.prompt[start:end]
            kv    = fake_attention(chunk, req.kv_cache)
            req.kv_cache.extend(kv)
            req.processed = end
            print(f&quot;  [Prefill] {req.req_id}: tokens [{start}:{end}] → KV len={len(req.kv_cache)}&quot;)

            if req.is_prefill_done():
                prefill_queue.pop(0)
                decode_queue.append(req)
                print(f&quot;  [Ready]  {req.req_id}: prefill complete → decode queue&quot;)

if __name__ == &quot;__main__&quot;:
    reqs = [
        Request(&quot;R1&quot;, list(range(10))),   # 10-token prompt → needs 3 chunks
        Request(&quot;R2&quot;, list(range(6))),    # 6-token prompt  → needs 2 chunks
    ]
    run_chunked_prefill(reqs, max_new_tokens=3)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Expected output (abridged):&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;--- Iteration 1 ---
  [Prefill] R1: tokens [0:4] → KV len=4

--- Iteration 2 ---
  [Prefill] R1: tokens [4:8] → KV len=8

--- Iteration 3 ---
  [Prefill] R1: tokens [8:10] → KV len=12
  [Ready]  R1: prefill complete → decode queue

--- Iteration 4 ---
  [Decode] R1: generated token 17
  [Prefill] R2: tokens [0:4] → KV len=4
...
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;5. Benefits and Trade-offs (优缺点)&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Without Chunked Prefill&lt;/th&gt;
&lt;th&gt;With Chunked Prefill&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;TTFT (首个令牌时间)&lt;/td&gt;
&lt;td&gt;Unpredictable, spikes on long prompts&lt;/td&gt;
&lt;td&gt;More predictable, bounded by chunk size&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Decode stalls (解码停顿)&lt;/td&gt;
&lt;td&gt;Long stalls during large prefills&lt;/td&gt;
&lt;td&gt;Eliminated — decode runs every iteration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPU utilization (GPU利用率)&lt;/td&gt;
&lt;td&gt;Suboptimal during pure decode&lt;/td&gt;
&lt;td&gt;Higher — compute + memory-BW co-utilized&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Implementation complexity&lt;/td&gt;
&lt;td&gt;Simple&lt;/td&gt;
&lt;td&gt;Moderate (state tracking per request)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;6. Key Formula — Prefill Iterations per Request&lt;/h2&gt;
&lt;p&gt;$$
N_{\text{iters}} = \left\lceil \frac{L}{C} \right\rceil
$$&lt;/p&gt;
&lt;p&gt;Where $L$ is the prompt length (提示长度) and $C$ is the chunk size (块大小). The request enters the decode queue after $N_{\text{iters}}$ prefill iterations.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;7. Related Concepts (相关概念)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;PagedAttention (分页注意力)&lt;/strong&gt; — manages KV cache memory in pages; works naturally with chunked prefill.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Continuous Batching (连续批处理)&lt;/strong&gt; — iterates dynamically; chunked prefill is one scheduling strategy on top.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Speculative Decoding (推测解码)&lt;/strong&gt; — orthogonal technique to speed up the decode phase.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TTFT / ITL / TBT&lt;/strong&gt; — latency metrics affected by chunked prefill scheduling.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Offline Inference</title><link>https://lxy-alexander.github.io/blog/posts/vllm/offline-inference/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/vllm/offline-inference/</guid><description>Offline Inference</description><pubDate>Tue, 10 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;hr /&gt;
&lt;p&gt;&lt;strong&gt;1. Async LLM Streaming&lt;/strong&gt; 异步流式推理。用 &lt;code&gt;asyncio&lt;/code&gt; 非阻塞地逐 token 输出结果，适合 Web 服务实时返回响应，避免等待整个序列生成完才返回。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Audio Language&lt;/strong&gt; 音频+语言多模态推理。将音频输入（语音、声音）与文本结合送入模型，支持语音问答等场景。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Automatic Prefix Caching (APC)&lt;/strong&gt; 自动前缀缓存。对相同前缀的请求复用已计算的 KV Cache，避免重复计算 system prompt，显著降低首 token 延迟。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. Batch LLM Inference&lt;/strong&gt; 离线批量推理。一次性处理大量请求，最大化 GPU 吞吐，适合数据处理、评估等非实时场景。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. Chat With Tools&lt;/strong&gt; 工具调用推理。模型在对话中调用外部函数/API（Function Calling），实现搜索、计算等能力扩展。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6. Context Extension&lt;/strong&gt; 上下文窗口扩展。通过 RoPE 缩放等技术让模型处理超出训练长度的输入，支持长文档场景。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7. Data Parallel&lt;/strong&gt; 数据并行推理。多个 GPU/进程各自持有完整模型副本，并行处理不同请求，横向扩展吞吐量。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8. Disaggregated Prefill V1&lt;/strong&gt; 预填充分离 V1 版。将计算密集的 prefill 阶段和内存密集的 decode 阶段分配到不同实例，各自优化资源利用。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9. Disaggregated Prefill&lt;/strong&gt; 预填充分离基础版。同上，是早期/基础实现版本，概念相同但架构实现有差异。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10. Encoder Decoder Multimodal&lt;/strong&gt; 编码器-解码器多模态。使用 encoder-decoder 架构（如 T5、Whisper 类结构）处理多模态任务，与纯 decoder 架构不同。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;11. Extract Hidden States&lt;/strong&gt; 提取隐藏层状态。获取模型中间层的向量表示，用于 embedding、分类、RAG 检索等下游任务。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;12. KV Load Failure Recovery Test&lt;/strong&gt; KV Cache 加载失败恢复测试。验证当 KV Cache 读取失败时系统能否优雅降级、自动重算，保证服务稳定性。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;13. LLM Engine Example&lt;/strong&gt; LLM 引擎基础示例。直接使用 vLLM 底层 &lt;code&gt;LLMEngine&lt;/code&gt; API，展示最基本的请求提交与结果获取流程。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;14. LLM Engine Reset KV&lt;/strong&gt; 引擎运行时重置 KV Cache。演示如何在不重启服务的情况下清空缓存，用于状态管理和内存回收。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;15. Load Sharded State&lt;/strong&gt; 加载分片模型权重。将大模型按层/张量切分成多个文件分片加载，解决单文件过大或显存不足问题。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;16. Custom Logits Processors&lt;/strong&gt; 自定义 logits 处理器。在采样前对模型输出的原始分数进行自定义修改，实现关键词屏蔽、格式约束等控制。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;17. LoRA With Quantization Inference&lt;/strong&gt; LoRA + 量化联合推理。在量化（INT4/INT8）的基础模型上叠加 LoRA 适配器，兼顾显存节省和个性化能力。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;18. Metrics&lt;/strong&gt; 推理性能指标监控。暴露 throughput、latency、TTFT（首 token 时间）等指标，对接 Prometheus/Grafana 用于生产监控。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;19. Mistral-Small&lt;/strong&gt; Mistral Small 模型推理示例。展示如何加载和运行 Mistral 系列小参数量模型，适合资源受限场景。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20. MLPSpeculator&lt;/strong&gt; MLP 投机解码器。用一个轻量 MLP 网络预测后续多个 token，再由主模型并行验证，加速生成速度 2~3 倍。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;21. MultiLoRA Inference&lt;/strong&gt; 多 LoRA 并发推理。单个 vLLM 实例同时加载多个 LoRA 适配器，按请求动态切换，服务多租户场景。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;22. New Weight Syncing&lt;/strong&gt; 新权重同步机制。在训练-推理协同场景（如在线 RLHF）中，将最新训练好的权重热更新到推理引擎，无需重启。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;23. Offline Inference with OpenAI Batch File Format&lt;/strong&gt; 兼容 OpenAI Batch API 格式的离线推理。读取 &lt;code&gt;.jsonl&lt;/code&gt; 格式批量请求文件，输出兼容 OpenAI 格式的结果，便于迁移。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24. Pause Resume&lt;/strong&gt; 推理暂停与恢复。演示如何中断正在进行的推理任务并在之后恢复，用于资源调度和优先级抢占场景。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;25. Prefix Caching&lt;/strong&gt; 手动前缀缓存示例。与 APC 对应，展示显式控制缓存行为的方式，更灵活地管理共享前缀的缓存策略。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;26. Prompt Embed Inference&lt;/strong&gt; 直接输入 embedding 推理。跳过 tokenizer，直接传入预计算的向量作为模型输入，用于特殊的 embedding 注入场景。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;27. Qwen2.5-Omni Offline Inference&lt;/strong&gt; Qwen2.5-Omni 离线推理示例。展示阿里 Qwen 全模态模型（文本+图像+音频+视频）的本地批量推理用法。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;28. Qwen3 Omni&lt;/strong&gt; Qwen3 全模态推理示例。Qwen3 版本的多模态推理，支持更强的模态理解和更长上下文。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;29. Qwen 1M&lt;/strong&gt; Qwen 百万上下文推理。演示如何运行支持 100 万 token 超长上下文的 Qwen 模型，处理超长文档。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;30. Reproducibility&lt;/strong&gt; 推理结果可复现性。通过固定随机种子、采样参数等手段，确保相同输入每次产生相同输出，用于测试和调试。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;31. RLHF&lt;/strong&gt; 基于人类反馈的强化学习训练示例。展示 vLLM 作为 rollout 引擎参与 PPO 等 RLHF 训练流程的基本用法。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;32. RLHF Colocate&lt;/strong&gt; 训练推理共置的 RLHF。将训练器和推理引擎放在同一组 GPU 上交替运行，节省跨机通信开销和硬件成本。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;33. RLHF Online Quant&lt;/strong&gt; 在线量化 RLHF。训练过程中动态对模型量化后推理，降低 rollout 阶段显存占用，提升训练效率。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;34. RLHF Utils&lt;/strong&gt; RLHF 工具函数库。提供 reward 计算、数据格式转换、采样策略等 RLHF 流程中复用的通用工具。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;35. Run One Batch&lt;/strong&gt; 单批次运行示例。最简化的批量推理演示，用于快速验证环境配置和模型加载是否正常。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;36. Save Sharded State&lt;/strong&gt; 保存分片模型权重。将运行中的模型权重按分片格式导出保存，配合 Load Sharded State 使用，加速下次启动。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;37. Simple Profiling&lt;/strong&gt; 简单性能分析。使用 PyTorch Profiler 等工具记录推理各阶段耗时，定位性能瓶颈。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;38. Skip Loading Weights In Engine Init&lt;/strong&gt; 初始化时跳过权重加载。在测试或调试引擎逻辑时跳过耗时的权重加载步骤，加快启动速度。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;39. Spec Decode&lt;/strong&gt; 投机解码通用示例。用 draft 模型生成候选 token，主模型并行验证，显著提升生成吞吐，适合延迟敏感场景。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;40. Structured Outputs&lt;/strong&gt; 结构化输出约束。强制模型输出符合 JSON Schema 或正则表达式的格式，保证下游程序可靠解析。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;41. Torchrun DP Example&lt;/strong&gt; torchrun 数据并行示例。用 &lt;code&gt;torchrun&lt;/code&gt; 启动多进程数据并行推理，展示标准 PyTorch 分布式启动方式。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;42. Torchrun Example&lt;/strong&gt; torchrun 基础示例。展示用 &lt;code&gt;torchrun&lt;/code&gt; 启动 vLLM 的最基本分布式配置，不限于数据并行。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;43. Vision Language&lt;/strong&gt; 视觉语言单图推理。将单张图片与文本一起输入模型，支持图像问答、图像描述等 VQA 任务。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;44. Vision Language Multi Image&lt;/strong&gt; 视觉语言多图推理。支持在单次对话中传入多张图片，处理图像对比、多帧理解等更复杂的视觉任务。&lt;/p&gt;
</content:encoded></item><item><title>Continuous Batching</title><link>https://lxy-alexander.github.io/blog/posts/llm/continuous-batching/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/llm/continuous-batching/</guid><description>Continuous Batching</description><pubDate>Mon, 09 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;I. Continuous Batching (连续批处理)&lt;/h1&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-c69d652d2a0747fab9aad1fab48ff742.r2.dev/images/image-20260415031834502&quot; alt=&quot;image-20260415031834502&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;1. Background&lt;/h2&gt;
&lt;p&gt;Traditional LLM serving uses &lt;strong&gt;static batching (静态批处理)&lt;/strong&gt;: a fixed group of requests is loaded together, the GPU runs until &lt;em&gt;every&lt;/em&gt; request in the batch finishes, then the next batch starts. Two problems arise:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Padding waste (填充浪费)&lt;/strong&gt;: short requests must be padded to match the longest request in the batch, wasting compute.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Head-of-line blocking (队头阻塞)&lt;/strong&gt;: new requests wait for the entire current batch to complete, even if most slots are already idle.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Continuous Batching (also called &lt;em&gt;iteration-level scheduling&lt;/em&gt;, 迭代级调度) solves both by scheduling at the granularity of a &lt;strong&gt;single iteration&lt;/strong&gt; rather than a whole batch.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Core Idea&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;As soon as one request finishes, its GPU slot is freed and a new request is admitted — &lt;strong&gt;within the same next iteration&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The scheduler runs once per forward pass (前向传播). It looks at:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Which running requests still need decode steps.&lt;/li&gt;
&lt;li&gt;Whether any free capacity exists to admit a new prefill request.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This means the batch composition changes &lt;strong&gt;every iteration&lt;/strong&gt;, hence &quot;continuous.&quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. Algorithm&lt;/h2&gt;
&lt;h3&gt;1) Iteration-Level Scheduler (迭代级调度器)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# continuous_batching_demo.py
# Standalone simulation of continuous batching.
# No external dependencies required.

from collections import deque

class Request:
    &quot;&quot;&quot;One inference request (推理请求).&quot;&quot;&quot;
    def __init__(self, req_id: str, prompt_len: int, max_new_tokens: int):
        self.req_id = req_id
        self.prompt_len = prompt_len
        self.max_new_tokens = max_new_tokens
        self.generated = 0          # tokens generated so far
        self.prefill_done = False

    def is_done(self) -&amp;gt; bool:
        return self.prefill_done and self.generated &amp;gt;= self.max_new_tokens

    def step(self):
        &quot;&quot;&quot;Simulate one decode step (解码步骤).&quot;&quot;&quot;
        if not self.prefill_done:
            self.prefill_done = True    # single-step prefill (simplified)
        else:
            self.generated += 1

def run_continuous_batching(
    all_requests: list[Request],
    max_batch_size: int = 3,
    max_iterations: int = 20,
):
    waiting_queue = deque(all_requests)   # requests not yet admitted
    running: list[Request] = []           # currently active requests
    finished: list[Request] = []

    for iteration in range(1, max_iterations + 1):
        # ── 1. Evict finished requests (移除完成的请求) ──────────────────
        done = [r for r in running if r.is_done()]
        for r in done:
            running.remove(r)
            finished.append(r)
            print(f&quot;  [Done]    {r.req_id} finished at iteration {iteration}&quot;)

        # ── 2. Admit new requests to fill freed slots (补充新请求) ────────
        while waiting_queue and len(running) &amp;lt; max_batch_size:
            new_req = waiting_queue.popleft()
            running.append(new_req)
            print(f&quot;  [Admit]   {new_req.req_id} admitted at iteration {iteration}&quot;)

        if not running:
            print(f&quot;Iteration {iteration}: all done.&quot;)
            break

        # ── 3. Step every running request (执行一步) ──────────────────────
        print(f&quot;\n--- Iteration {iteration} | batch={[r.req_id for r in running]} ---&quot;)
        for r in running:
            r.step()

    print(&quot;\n=== Summary ===&quot;)
    for r in finished:
        print(f&quot;  {r.req_id}: prompt={r.prompt_len}, generated={r.generated}&quot;)

if __name__ == &quot;__main__&quot;:
    requests = [
        Request(&quot;R1&quot;, prompt_len=10, max_new_tokens=2),
        Request(&quot;R2&quot;, prompt_len=8,  max_new_tokens=5),
        Request(&quot;R3&quot;, prompt_len=12, max_new_tokens=3),
        Request(&quot;R4&quot;, prompt_len=6,  max_new_tokens=2),
        Request(&quot;R5&quot;, prompt_len=9,  max_new_tokens=4),
    ]
    run_continuous_batching(requests, max_batch_size=3)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Expected output (abridged):&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;--- Iteration 1 | batch=[&apos;R1&apos;, &apos;R2&apos;, &apos;R3&apos;] ---
  [Admit]   R1 admitted at iteration 1
  ...
--- Iteration 3 | batch=[&apos;R1&apos;, &apos;R2&apos;, &apos;R3&apos;] ---
  [Done]    R1 finished at iteration 4
  [Admit]   R4 admitted at iteration 4    ← slot freed, new request in immediately
--- Iteration 4 | batch=[&apos;R2&apos;, &apos;R3&apos;, &apos;R4&apos;] ---
...
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;2) Key Invariant (关键不变式)&lt;/h3&gt;
&lt;p&gt;At every iteration $t$:&lt;/p&gt;
&lt;p&gt;$$ |\text{running}&lt;em&gt;t| \leq B&lt;/em&gt;{\max} $$&lt;/p&gt;
&lt;p&gt;where $B_{\max}$ is the maximum batch size (最大批大小), constrained by GPU memory (显存) available for KV Cache (键值缓存).&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4. No Padding Needed&lt;/h2&gt;
&lt;p&gt;In static batching, all sequences in a batch must share the same length tensor, requiring padding tokens (填充令牌):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Static:  [A A A A _ _]   ← _ = wasted padding
         [B B _ _ _ _]
         [C C C C C C]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In continuous batching with &lt;strong&gt;PagedAttention (分页注意力)&lt;/strong&gt;, each request owns its own KV cache pages. The forward pass uses variable-length attention — no padding:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Continuous:  [A A A A]   ← exact length, no padding
             [B B]
             [C C C C C C]
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;5. Comparison Table&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Static Batching (静态批处理)&lt;/th&gt;
&lt;th&gt;Continuous Batching (连续批处理)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Scheduling granularity (调度粒度)&lt;/td&gt;
&lt;td&gt;Per-batch&lt;/td&gt;
&lt;td&gt;Per-iteration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Padding (填充)&lt;/td&gt;
&lt;td&gt;Required&lt;/td&gt;
&lt;td&gt;Not needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPU idle time (GPU空闲时间)&lt;/td&gt;
&lt;td&gt;High (slot waits for stragglers)&lt;/td&gt;
&lt;td&gt;Near zero&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Latency for new requests (新请求延迟)&lt;/td&gt;
&lt;td&gt;Full batch wait&lt;/td&gt;
&lt;td&gt;At most one iteration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Implementation complexity (实现复杂度)&lt;/td&gt;
&lt;td&gt;Simple&lt;/td&gt;
&lt;td&gt;Moderate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical throughput gain (吞吐量提升)&lt;/td&gt;
&lt;td&gt;Baseline&lt;/td&gt;
&lt;td&gt;2–5× higher&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;6. Key Metrics (关键指标)&lt;/h2&gt;
&lt;p&gt;$$ \text{Throughput (吞吐量)} = \frac{\text{Total output tokens}}{\text{Wall-clock time}} $$&lt;/p&gt;
&lt;p&gt;$$ \text{TTFT (首个令牌时间)} = t_{\text{first token}} - t_{\text{request arrival}} $$&lt;/p&gt;
&lt;p&gt;$$ \text{ITL (令牌间延迟)} = \frac{t_{\text{last token}} - t_{\text{first token}}}{\text{output tokens} - 1} $$&lt;/p&gt;
&lt;p&gt;Continuous batching primarily improves &lt;strong&gt;throughput&lt;/strong&gt; and reduces &lt;strong&gt;queuing latency (排队延迟)&lt;/strong&gt; for newly arriving requests.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;7. Related Concepts (相关概念)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Chunked Prefill (分块预填充)&lt;/strong&gt; — splits long prefills into chunks; a scheduling strategy that sits &lt;em&gt;on top of&lt;/em&gt; continuous batching.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PagedAttention (分页注意力)&lt;/strong&gt; — enables variable-length KV cache per request; prerequisite for efficient continuous batching.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Preemption (抢占)&lt;/strong&gt; — when KV cache is full, the scheduler may evict a low-priority request and recompute later.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;vLLM&lt;/strong&gt; — the open-source serving framework that popularized continuous batching + PagedAttention.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Python Decorator</title><link>https://lxy-alexander.github.io/blog/posts/python/python-decorator/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/python/python-decorator/</guid><description>Python Decorator</description><pubDate>Mon, 09 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;&lt;strong&gt;I. Decorator Pattern In Python&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; A &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Decorator (装饰器)&amp;lt;/span&amp;gt; is a design pattern in Python that allows you to &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;wrap (包裹)&amp;lt;/span&amp;gt; a function or class with additional behavior — without modifying its source code. Decorators are built on the concept that &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;functions are first-class objects (函数是一等对象)&amp;lt;/span&amp;gt;, meaning they can be passed as arguments, returned from other functions, and assigned to variables. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;1. Why Do We Need Decorators?&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;1.&amp;lt;/span&amp;gt; &lt;strong&gt;Code Reuse (代码复用):&lt;/strong&gt; Apply the same cross-cutting logic (logging, timing, auth) to many functions without copy-pasting.&lt;/p&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;2.&amp;lt;/span&amp;gt; &lt;strong&gt;Separation of Concerns (关注点分离):&lt;/strong&gt; Keep business logic clean; push auxiliary logic into decorators.&lt;/p&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;3.&amp;lt;/span&amp;gt; &lt;strong&gt;Readability (可读性):&lt;/strong&gt; The &lt;code&gt;@decorator&lt;/code&gt; syntax makes intent explicit at a glance.&lt;/p&gt;
&lt;h2&gt;2. Prerequisites — Functions as First-Class Objects&lt;/h2&gt;
&lt;p&gt;Before understanding decorators, you must understand three building blocks.&lt;/p&gt;
&lt;h3&gt;1) Functions Can Be Assigned to Variables&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;def greet(name):
    return f&quot;Hello, {name}!&quot;

say_hello = greet          # Assign function to a variable (赋值给变量)
print(say_hello(&quot;Alice&quot;))  # Output: Hello, Alice!
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) Functions Can Be Passed as Arguments&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;def apply(func, value):
    return func(value)     # Call the passed-in function (调用传入的函数)

result = apply(greet, &quot;Bob&quot;)
print(result)  # Output: Hello, Bob!
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) Functions Can Be Returned from Functions (Closures 闭包)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;def make_multiplier(factor):
    def multiplier(x):
        return x * factor  # Captures `factor` from enclosing scope (捕获外部变量)
    return multiplier      # Return the inner function (返回内部函数)

double = make_multiplier(2)
print(double(5))   # Output: 10
print(double(9))   # Output: 18
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; A &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Closure (闭包)&amp;lt;/span&amp;gt; is an inner function that &quot;remembers&quot; variables from its enclosing scope even after the outer function has returned. This is the foundation of every decorator.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; A decorator is essentially a &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Higher-Order Function (高阶函数)&amp;lt;/span&amp;gt; — it takes a function as input, wraps it in a new function that adds behavior, and returns the new function. The &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;@syntax&amp;lt;/code&amp;gt; is just syntactic sugar (语法糖) for &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;func = decorator(func)&amp;lt;/code&amp;gt;. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;1. The Simplest Decorator&lt;/h2&gt;
&lt;h3&gt;1) Manual Style (Without @ Syntax)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;def my_decorator(func):
    def wrapper():
        print(&quot;--- Before function call ---&quot;)  # Pre-logic (前置逻辑)
        func()                                  # Call original function (调用原函数)
        print(&quot;--- After function call ---&quot;)   # Post-logic (后置逻辑)
    return wrapper

def say_hi():
    print(&quot;Hi!&quot;)

# Manually wrap (手动包裹)
say_hi = my_decorator(say_hi)
say_hi()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;--- Before function call ---
Hi!
--- After function call ---
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) Using @ Syntax (语法糖)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;def my_decorator(func):
    def wrapper():
        print(&quot;--- Before function call ---&quot;)
        func()
        print(&quot;--- After function call ---&quot;)
    return wrapper

@my_decorator          # Equivalent to: say_hi = my_decorator(say_hi)
def say_hi():
    print(&quot;Hi!&quot;)

say_hi()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;@my_decorator&amp;lt;/code&amp;gt; placed above a function definition is 100% equivalent to writing &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;say_hi = my_decorator(say_hi)&amp;lt;/code&amp;gt; right after the definition.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Decorating Functions with Arguments&lt;/h2&gt;
&lt;p&gt;The &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;wrapper&amp;lt;/span&amp;gt; must accept and forward any arguments the original function takes, using &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;*args&amp;lt;/code&amp;gt; and &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;**kwargs&amp;lt;/code&amp;gt;.&lt;/p&gt;
&lt;h3&gt;1) Problem Without *args / **kwargs&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;def my_decorator(func):
    def wrapper():         # ← No parameters! Will crash if func takes arguments.
        func()
    return wrapper

@my_decorator
def add(a, b):
    return a + b

add(1, 2)  # ❌ TypeError: wrapper() takes 0 positional arguments but 2 were given
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) Correct: Use *args and **kwargs&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;def my_decorator(func):
    def wrapper(*args, **kwargs):           # Accept any arguments (接受任意参数)
        print(f&quot;Calling: {func.__name__}&quot;)
        result = func(*args, **kwargs)      # Forward to original function (转发给原函数)
        print(f&quot;Result: {result}&quot;)
        return result                       # Don&apos;t forget to return! (记得返回结果)
    return wrapper

@my_decorator
def add(a, b):
    return a + b

@my_decorator
def greet(name, greeting=&quot;Hello&quot;):
    return f&quot;{greeting}, {name}!&quot;

add(3, 4)
greet(&quot;Alice&quot;, greeting=&quot;Hi&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Calling: add
Result: 7
Calling: greet
Result: Hi, Alice!
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;3. Preserving Function Metadata with functools.wraps&lt;/h2&gt;
&lt;h3&gt;1) The Problem — Metadata Loss (元数据丢失)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;def my_decorator(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

@my_decorator
def add(a, b):
    &quot;&quot;&quot;Adds two numbers.&quot;&quot;&quot;  # Docstring (文档字符串)
    return a + b

print(add.__name__)   # Output: wrapper  ← WRONG! Should be &quot;add&quot;
print(add.__doc__)    # Output: None     ← WRONG! Lost the docstring
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) The Fix — @functools.wraps&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import functools

def my_decorator(func):
    @functools.wraps(func)           # Copies metadata from func to wrapper (复制元数据)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

@my_decorator
def add(a, b):
    &quot;&quot;&quot;Adds two numbers.&quot;&quot;&quot;
    return a + b

print(add.__name__)   # Output: add      ✅
print(add.__doc__)    # Output: Adds two numbers.  ✅
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Always use @functools.wraps(func) inside every decorator you write.&amp;lt;/span&amp;gt; Without it, debugging tools, documentation generators, and frameworks that inspect &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;&lt;strong&gt;name&lt;/strong&gt;&amp;lt;/code&amp;gt; / &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;&lt;strong&gt;doc&lt;/strong&gt;&amp;lt;/code&amp;gt; will silently receive wrong information.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4. Practical Decorator Examples&lt;/h2&gt;
&lt;h3&gt;1) Timing Decorator (计时装饰器)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import functools
import time

def timer(func):
    &quot;&quot;&quot;Measures execution time (测量执行时间) of a function.&quot;&quot;&quot;
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()          # High-resolution timer (高精度计时器)
        result = func(*args, **kwargs)
        elapsed = time.perf_counter() - start
        print(f&quot;[TIMER] {func.__name__} took {elapsed:.6f}s&quot;)
        return result
    return wrapper

@timer
def slow_sum(n):
    &quot;&quot;&quot;Sum of 0..n using a loop.&quot;&quot;&quot;
    return sum(range(n))

slow_sum(10_000_000)
# Output: [TIMER] slow_sum took 0.412381s
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) Logging Decorator (日志装饰器)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import functools

def logger(func):
    &quot;&quot;&quot;Logs function calls and their arguments (记录函数调用和参数).&quot;&quot;&quot;
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        args_repr = [repr(a) for a in args]
        kwargs_repr = [f&quot;{k}={v!r}&quot; for k, v in kwargs.items()]
        signature = &quot;, &quot;.join(args_repr + kwargs_repr)
        print(f&quot;[LOG] Calling {func.__name__}({signature})&quot;)
        result = func(*args, **kwargs)
        print(f&quot;[LOG] {func.__name__} returned {result!r}&quot;)
        return result
    return wrapper

@logger
def divide(a, b):
    return a / b

divide(10, 2)
divide(7, b=3)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[LOG] Calling divide(10, 2)
[LOG] divide returned 5.0
[LOG] Calling divide(7, b=3)
[LOG] divide returned 2.3333333333333335
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) Retry Decorator (重试装饰器)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import functools
import time

def retry(max_attempts=3, delay=1.0):
    &quot;&quot;&quot;Retries a function on exception (异常时重试).&quot;&quot;&quot;
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(1, max_attempts + 1):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    print(f&quot;[RETRY] Attempt {attempt}/{max_attempts} failed: {e}&quot;)
                    if attempt &amp;lt; max_attempts:
                        time.sleep(delay)
            raise RuntimeError(f&quot;{func.__name__} failed after {max_attempts} attempts.&quot;)
        return wrapper
    return decorator

@retry(max_attempts=3, delay=0.5)
def unstable_api_call(url):
    import random
    if random.random() &amp;lt; 0.7:   # 70% chance of failure (模拟不稳定网络)
        raise ConnectionError(&quot;Network timeout&quot;)
    return f&quot;200 OK from {url}&quot;

try:
    print(unstable_api_call(&quot;https://example.com&quot;))
except RuntimeError as e:
    print(e)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4) Cache / Memoization Decorator (缓存装饰器)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import functools

def memoize(func):
    &quot;&quot;&quot;Caches results of expensive function calls (缓存昂贵函数调用的结果).&quot;&quot;&quot;
    cache = {}   # Cache storage (缓存存储)
    @functools.wraps(func)
    def wrapper(*args):
        if args not in cache:
            cache[args] = func(*args)   # Compute and store (计算并存储)
        else:
            print(f&quot;[CACHE] Hit for args={args}&quot;)
        return cache[args]
    return wrapper

@memoize
def fibonacci(n):
    if n &amp;lt;= 1:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

print(fibonacci(10))   # Computes fresh
print(fibonacci(10))   # [CACHE] Hit for args=(10,)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Python&apos;s standard library provides &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;functools.lru_cache&amp;lt;/code&amp;gt; and &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;functools.cache&amp;lt;/code&amp;gt; (Python 3.9+) as production-ready memoization decorators. Prefer those over hand-rolling your own.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;5) Access Control Decorator (访问控制装饰器)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import functools

def requires_auth(func):
    &quot;&quot;&quot;Blocks calls if user is not authenticated (未认证则阻止调用).&quot;&quot;&quot;
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        user = kwargs.get(&quot;user&quot;) or (args[0] if args else None)
        if not getattr(user, &quot;is_authenticated&quot;, False):
            raise PermissionError(f&quot;Access denied: authentication required.&quot;)
        return func(*args, **kwargs)
    return wrapper

class User:
    def __init__(self, name, authenticated):
        self.name = name
        self.is_authenticated = authenticated

@requires_auth
def view_dashboard(user):
    return f&quot;Welcome to dashboard, {user.name}!&quot;

alice = User(&quot;Alice&quot;, authenticated=True)
bob   = User(&quot;Bob&quot;,   authenticated=False)

print(view_dashboard(alice))   # ✅ Welcome to dashboard, Alice!
try:
    print(view_dashboard(bob)) # ❌ PermissionError
except PermissionError as e:
    print(e)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;5. Decorators with Parameters (带参数的装饰器)&lt;/h2&gt;
&lt;p&gt;A decorator with parameters requires an &lt;strong&gt;extra layer of nesting (额外一层嵌套)&lt;/strong&gt;: an outer function receives the parameters and returns the actual decorator.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;call site:     @repeat(3)
what happens:  repeat(3)  →  returns decorator
               decorator(func)  →  returns wrapper
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;1) repeat(n) — Run a Function n Times&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import functools

def repeat(n):
    &quot;&quot;&quot;Runs the decorated function n times (运行被装饰函数 n 次).&quot;&quot;&quot;
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            for i in range(n):
                result = func(*args, **kwargs)
            return result          # Returns last result (返回最后一次结果)
        return wrapper
    return decorator

@repeat(3)
def say(message):
    print(message)

say(&quot;Hello!&quot;)
# Output:
# Hello!
# Hello!
# Hello!
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) validate_types — Runtime Type Checking (运行时类型检查)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import functools

def validate_types(**type_map):
    &quot;&quot;&quot;Validates argument types at runtime (运行时验证参数类型).&quot;&quot;&quot;
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            import inspect
            sig = inspect.signature(func)
            bound = sig.bind(*args, **kwargs)
            bound.apply_defaults()
            for param_name, expected_type in type_map.items():
                value = bound.arguments.get(param_name)
                if value is not None and not isinstance(value, expected_type):
                    raise TypeError(
                        f&quot;Argument &apos;{param_name}&apos; expected {expected_type.__name__}, &quot;
                        f&quot;got {type(value).__name__}&quot;
                    )
            return func(*args, **kwargs)
        return wrapper
    return decorator

@validate_types(a=int, b=int)
def add(a, b):
    return a + b

print(add(2, 3))       # ✅ 5
print(add(2.0, 3))     # ❌ TypeError: Argument &apos;a&apos; expected int, got float
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;6. Stacking Multiple Decorators (叠加多个装饰器)&lt;/h2&gt;
&lt;p&gt;You can apply several decorators to one function. They are applied &lt;strong&gt;bottom-up (从下到上)&lt;/strong&gt; at definition time, but execute &lt;strong&gt;top-down (从上到下)&lt;/strong&gt; at call time.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import functools

def decorator_A(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print(&quot;A: before&quot;)
        result = func(*args, **kwargs)
        print(&quot;A: after&quot;)
        return result
    return wrapper

def decorator_B(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print(&quot;B: before&quot;)
        result = func(*args, **kwargs)
        print(&quot;B: after&quot;)
        return result
    return wrapper

@decorator_A         # Applied second (第二个应用) → outermost wrapper
@decorator_B         # Applied first  (第一个应用) → innermost wrapper
def my_func():
    print(&quot;  → Running my_func&quot;)

my_func()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;A: before
B: before
  → Running my_func
B: after
A: after
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;@A @B def f&amp;lt;/code&amp;gt; is equivalent to &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;f = A(B(f))&amp;lt;/code&amp;gt;. Think of it as wrapping layers of an onion — &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;B wraps f first&amp;lt;/span&amp;gt;, then &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;A wraps the result&amp;lt;/span&amp;gt;.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;7. Class-Based Decorators (基于类的装饰器)&lt;/h2&gt;
&lt;p&gt;A class can act as a decorator by implementing &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;&lt;strong&gt;init&lt;/strong&gt;&amp;lt;/code&amp;gt; (to receive the function) and &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;&lt;strong&gt;call&lt;/strong&gt;&amp;lt;/code&amp;gt; (to act as the wrapper).&lt;/p&gt;
&lt;h3&gt;1) Call Counter Decorator&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import functools

class CountCalls:
    &quot;&quot;&quot;Counts how many times a function has been called (统计调用次数).&quot;&quot;&quot;

    def __init__(self, func):
        functools.update_wrapper(self, func)  # Equivalent to @functools.wraps
        self.func = func
        self.count = 0                        # Instance variable (实例变量)

    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f&quot;[COUNT] {self.func.__name__} has been called {self.count} time(s)&quot;)
        return self.func(*args, **kwargs)

@CountCalls
def say_hello():
    print(&quot;Hello!&quot;)

say_hello()
say_hello()
say_hello()
print(f&quot;Total calls: {say_hello.count}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[COUNT] say_hello has been called 1 time(s)
Hello!
[COUNT] say_hello has been called 2 time(s)
Hello!
[COUNT] say_hello has been called 3 time(s)
Hello!
Total calls: 3
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;8. Decorating Classes (装饰类)&lt;/h2&gt;
&lt;p&gt;Decorators can also be applied to &lt;strong&gt;entire classes (整个类)&lt;/strong&gt;, typically to add or modify class-level behavior.&lt;/p&gt;
&lt;h3&gt;1) Singleton Decorator (单例装饰器)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;def singleton(cls):
    &quot;&quot;&quot;Ensures only one instance of a class is created (确保类只有一个实例).&quot;&quot;&quot;
    instances = {}
    @functools.wraps(cls)
    def get_instance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    return get_instance

@singleton
class DatabaseConnection:
    def __init__(self, host):
        self.host = host
        print(f&quot;Creating connection to {host}&quot;)

db1 = DatabaseConnection(&quot;localhost&quot;)   # Creating connection to localhost
db2 = DatabaseConnection(&quot;localhost&quot;)   # (No output — returns existing instance)
print(db1 is db2)                       # True ✅
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;9. Built-in Decorators in Python (Python内置装饰器)&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Decorator (装饰器)&lt;/th&gt;
&lt;th&gt;Location&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;@staticmethod&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Built-in&lt;/td&gt;
&lt;td&gt;Method that doesn&apos;t receive &lt;code&gt;self&lt;/code&gt; or &lt;code&gt;cls&lt;/code&gt; (不接收self/cls的方法)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;@classmethod&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Built-in&lt;/td&gt;
&lt;td&gt;Method that receives the class as first arg &lt;code&gt;cls&lt;/code&gt; (接收类作为第一参数)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;@property&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Built-in&lt;/td&gt;
&lt;td&gt;Makes a method accessible like an attribute (方法变属性访问)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;@functools.wraps&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;functools&lt;/td&gt;
&lt;td&gt;Preserves metadata of wrapped function (保留被包裹函数的元数据)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;@functools.lru_cache&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;functools&lt;/td&gt;
&lt;td&gt;LRU memoization cache (LRU缓存)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;@functools.cache&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;functools (3.9+)&lt;/td&gt;
&lt;td&gt;Unbounded memoization cache (无界缓存)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;@dataclasses.dataclass&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;dataclasses&lt;/td&gt;
&lt;td&gt;Auto-generates &lt;code&gt;__init__&lt;/code&gt;, &lt;code&gt;__repr__&lt;/code&gt;, etc. (自动生成初始化方法等)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;@abstractmethod&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;abc&lt;/td&gt;
&lt;td&gt;Marks a method as abstract (标记方法为抽象方法)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;1) @property Example&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;class Circle:
    def __init__(self, radius):
        self._radius = radius

    @property
    def radius(self):
        &quot;&quot;&quot;Getter (读取器).&quot;&quot;&quot;
        return self._radius

    @radius.setter
    def radius(self, value):
        &quot;&quot;&quot;Setter with validation (带验证的写入器).&quot;&quot;&quot;
        if value &amp;lt; 0:
            raise ValueError(&quot;Radius cannot be negative (半径不能为负)&quot;)
        self._radius = value

    @property
    def area(self):
        &quot;&quot;&quot;Computed property (计算属性) — no setter needed.&quot;&quot;&quot;
        import math
        return math.pi * self._radius ** 2

c = Circle(5)
print(c.radius)   # 5      — accessed like an attribute, not c.radius()
print(c.area)     # 78.53...
c.radius = 10
print(c.area)     # 314.15...
c.radius = -1     # ❌ ValueError
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) @functools.lru_cache Example&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import functools

@functools.lru_cache(maxsize=128)   # Cache up to 128 results (缓存最多128个结果)
def expensive_query(user_id: int) -&amp;gt; str:
    print(f&quot;  [DB] Querying user {user_id}...&quot;)   # Only prints on cache miss (仅缓存未命中时打印)
    return f&quot;User #{user_id} data&quot;

print(expensive_query(1))   # [DB] Querying...   → cache miss
print(expensive_query(1))   # (no DB print)       → cache hit ✅
print(expensive_query(2))   # [DB] Querying...   → cache miss
print(expensive_query.cache_info())
# CacheInfo(hits=1, misses=2, maxsize=128, currsize=2)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;10. Common Pitfalls (常见陷阱)&lt;/h2&gt;
&lt;h3&gt;1) Forgetting to Return the Result&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# ❌ WRONG — silently returns None
def bad_decorator(func):
    def wrapper(*args, **kwargs):
        func(*args, **kwargs)   # Missing return!
    return wrapper

# ✅ CORRECT
def good_decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)  # Always return!
    return wrapper
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) Mutable Default in Cached Decorator&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# ❌ lru_cache requires hashable arguments (需要可哈希参数)
@functools.lru_cache(maxsize=128)
def process(data: list):   # list is unhashable (列表不可哈希)!
    pass
# TypeError: unhashable type: &apos;list&apos;

# ✅ Use tuple instead (使用元组代替)
@functools.lru_cache(maxsize=128)
def process(data: tuple):
    pass
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) Decorator Evaluated at Import Time (装饰器在导入时执行)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;def register(func):
    print(f&quot;Registering {func.__name__}...&quot;)  # Runs at import, not at call time!
    return func

@register
def my_task():
    pass

# Output when module is imported: &quot;Registering my_task...&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;The decorator body runs once at definition time (import time)&amp;lt;/span&amp;gt;, while the &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;wrapper&amp;lt;/code&amp;gt; body runs every time the decorated function is called. Keep heavy setup logic inside wrapper, not the decorator factory.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;11. Summary Comparison Table&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature (特性)&lt;/th&gt;
&lt;th&gt;Function Decorator&lt;/th&gt;
&lt;th&gt;Class Decorator&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Syntax (语法)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;def deco(func)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;class Deco: __init__ + __call__&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;State (状态)&lt;/td&gt;
&lt;td&gt;Via closure variables (通过闭包变量)&lt;/td&gt;
&lt;td&gt;Via instance variables (通过实例变量)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Readability (可读性)&lt;/td&gt;
&lt;td&gt;✅ More concise&lt;/td&gt;
&lt;td&gt;🟡 More explicit for complex state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Works with methods&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;⚠️ Needs extra care with &lt;code&gt;self&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Metadata preservation&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@functools.wraps&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;functools.update_wrapper&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;A &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Decorator (装饰器)&amp;lt;/span&amp;gt; is a closure that wraps a function to inject reusable behavior — always use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;@functools.wraps&amp;lt;/code&amp;gt;, always &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;return&amp;lt;/code&amp;gt; the result, and add a third nesting layer when your decorator needs its own parameters.&amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Python typing</title><link>https://lxy-alexander.github.io/blog/posts/python/python-typing/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/python/python-typing/</guid><description>Python typing</description><pubDate>Mon, 09 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;&lt;strong&gt;I. Python Typing (类型注解)&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt;
Python&apos;s &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;typing&amp;lt;/code&amp;gt; module adds &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;type hints (类型提示)&amp;lt;/span&amp;gt; to Python. While Python remains dynamically typed, type hints help with &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;code documentation, IDE autocompletion, and catching bugs&amp;lt;/span&amp;gt; before runtime using tools like &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;mypy&amp;lt;/code&amp;gt;.
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;1. Basic Type Annotations&lt;/h2&gt;
&lt;h3&gt;1) Variable Annotations&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# Basic types
name: str = &quot;Alice&quot;
age: int = 30
height: float = 1.75
is_student: bool = True

# Without initial value
address: str  # Just type annotation, no value yet
address = &quot;123 Main St&quot;  # Later assignment
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) Function Annotations&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;def greet(name: str) -&amp;gt; str:
    return f&quot;Hello, {name}!&quot;

def add(a: int, b: int) -&amp;gt; int:
    return a + b

def process_data(data: list) -&amp;gt; None:  # None means returns nothing
    print(f&quot;Processing {len(data)} items&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt;Type hints are &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;not enforced at runtime&amp;lt;/span&amp;gt;. They&apos;re for developers and tools only. Python won&apos;t stop you from passing an &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;int&amp;lt;/code&amp;gt; to a &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;str&amp;lt;/code&amp;gt; parameter!&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;2. Container Types&lt;/h2&gt;
&lt;h3&gt;1) List, Tuple, Set, Dict&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from typing import List, Tuple, Set, Dict

# List of strings
names: List[str] = [&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;]

# Tuple of int and str (fixed length, mixed types)
person: Tuple[int, str, bool] = (1, &quot;Alice&quot;, True)

# Set of integers
unique_ids: Set[int] = {101, 102, 103}

# Dictionary with string keys and int values
scores: Dict[str, int] = {&quot;Alice&quot;: 95, &quot;Bob&quot;: 87}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) Nested Containers&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from typing import List, Dict, Tuple

# List of dictionaries
users: List[Dict[str, str]] = [
    {&quot;name&quot;: &quot;Alice&quot;, &quot;email&quot;: &quot;alice@example.com&quot;},
    {&quot;name&quot;: &quot;Bob&quot;, &quot;email&quot;: &quot;bob@example.com&quot;}
]

# Complex nesting
matrix: List[List[int]] = [[1, 2, 3], [4, 5, 6]]

# Tuple with list inside
data: Tuple[int, List[str], bool] = (1, [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;], True)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;3. Optional and Union Types&lt;/h2&gt;
&lt;h3&gt;1) Optional (value or None)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from typing import Optional

def find_user(user_id: int) -&amp;gt; Optional[str]:
    # Returns name or None if not found
    users = {1: &quot;Alice&quot;, 2: &quot;Bob&quot;}
    return users.get(user_id)  # May return None

# Optional[str] means either str or None
result: Optional[str] = find_user(1)  # &quot;Alice&quot;
result2: Optional[str] = find_user(99)  # None
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) Union (multiple possible types)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from typing import Union

# Function accepts int OR float
def square(value: Union[int, float]) -&amp;gt; Union[int, float]:
    return value * value

# Modern syntax (Python 3.10+)
def square2(value: int | float) -&amp;gt; int | float:
    return value * value

# Multiple types
def process_id(id_value: int | str) -&amp;gt; str:
    return f&quot;ID: {id_value}&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt;
&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Python 3.10+&amp;lt;/span&amp;gt; introduced the &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;|&amp;lt;/code&amp;gt; syntax for Union types, making code cleaner!
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;4. Type Aliases&lt;/h2&gt;
&lt;p&gt;Create readable names for complex types:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from typing import List, Tuple, Dict

# Type alias
UserID = int
UserName = str
UserInfo = Tuple[UserID, UserName, bool]

# Using the alias
def get_user() -&amp;gt; UserInfo:
    return (1, &quot;Alice&quot;, True)

# More complex alias
Coordinate = Tuple[float, float]
Polygon = List[Coordinate]

def calculate_area(shape: Polygon) -&amp;gt; float:
    # shape is list of (x, y) tuples
    pass
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;5. Callable Types&lt;/h2&gt;
&lt;p&gt;Type functions that accept functions:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from typing import Callable

# Function that takes an int and returns a str
def apply_twice(func: Callable[[int], str], value: int) -&amp;gt; str:
    return func(func(value))  # First call returns str, second call fails!

# Fixed version - proper typing catches this!
def apply_twice_fixed(func: Callable[[int], int], value: int) -&amp;gt; int:
    return func(func(value))

# More complex callback
def process_items(
    items: List[int],
    callback: Callable[[int], str]
) -&amp;gt; List[str]:
    return [callback(item) for item in items]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;6. Any and TypeVar (Generics)&lt;/h2&gt;
&lt;h3&gt;1) Any - escape hatch&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from typing import Any

# Use sparingly - defeats type checking!
def debug_print(value: Any) -&amp;gt; None:
    print(f&quot;Value: {value}, Type: {type(value)}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) TypeVar - generic functions&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from typing import TypeVar, List

T = TypeVar(&apos;T&apos;)  # Generic type variable

def first_element(items: List[T]) -&amp;gt; T:
    &quot;&quot;&quot;Returns first element, type preserved&quot;&quot;&quot;
    return items[0]

# Works with any list type
num = first_element([1, 2, 3])        # num is int
text = first_element([&quot;a&quot;, &quot;b&quot;, &quot;c&quot;]) # text is str

# Multiple type variables
K = TypeVar(&apos;K&apos;)
V = TypeVar(&apos;V&apos;)

def get_value(dict: Dict[K, V], key: K, default: V) -&amp;gt; V:
    return dict.get(key, default)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;7. Special Forms&lt;/h2&gt;
&lt;h3&gt;1) Literal - exact values&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from typing import Literal

def set_status(status: Literal[&quot;active&quot;, &quot;inactive&quot;, &quot;pending&quot;]) -&amp;gt; None:
    print(f&quot;Status set to {status}&quot;)

set_status(&quot;active&quot;)   # OK
set_status(&quot;active&quot;)    # OK
# set_status(&quot;unknown&quot;) # Type checker would complain

# Multiple literal values
def move(direction: Literal[&quot;north&quot;, &quot;south&quot;, &quot;east&quot;, &quot;west&quot;]) -&amp;gt; None:
    pass
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) Final - constants&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from typing import Final

MAX_SIZE: Final[int] = 100
PI: Final[float] = 3.14159

# Type checker would warn against reassignment
# MAX_SIZE = 200  # Error!
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;8. Protocol (Structural Subtyping)&lt;/h2&gt;
&lt;p&gt;Like interfaces, but duck-typed:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from typing import Protocol

class Drawable(Protocol):
    def draw(self) -&amp;gt; None: ...

class Circle:
    def draw(self) -&amp;gt; None:
        print(&quot;Drawing circle&quot;)

class Square:
    def draw(self) -&amp;gt; None:
        print(&quot;Drawing square&quot;)
    def area(self) -&amp;gt; float:
        return 16.0

def render(obj: Drawable) -&amp;gt; None:
    obj.draw()  # Works with anything that has draw()

render(Circle())  # OK
render(Square())  # OK - Square has draw()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;9. TypedDict - Dictionary with fixed keys&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from typing import TypedDict

class Person(TypedDict):
    name: str
    age: int
    email: str

# Works like a dict, but with type checking
alice: Person = {
    &quot;name&quot;: &quot;Alice&quot;,
    &quot;age&quot;: 30,
    &quot;email&quot;: &quot;alice@example.com&quot;
}

# Error if missing keys or wrong types
# bob: Person = {&quot;name&quot;: &quot;Bob&quot;}  # Missing age, email
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;10. Practical Examples&lt;/h2&gt;
&lt;h3&gt;1) API Response Handler&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from typing import Dict, List, Optional, Union, TypedDict
import json

class User(TypedDict):
    id: int
    name: str
    email: str
    is_active: bool

class APIResponse(TypedDict):
    status: Literal[&quot;success&quot;, &quot;error&quot;]
    data: Optional[List[User]]
    message: Optional[str]

def parse_api_response(json_str: str) -&amp;gt; APIResponse:
    data = json.loads(json_str)
    return {
        &quot;status&quot;: data[&quot;status&quot;],
        &quot;data&quot;: data.get(&quot;users&quot;),
        &quot;message&quot;: data.get(&quot;message&quot;)
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) Data Processing Pipeline&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from typing import List, Callable, TypeVar

T = TypeVar(&apos;T&apos;)
U = TypeVar(&apos;U&apos;)

def pipeline(
    data: List[T],
    *transforms: Callable[[T], U]
) -&amp;gt; List[U]:
    &quot;&quot;&quot;Apply multiple transforms to data&quot;&quot;&quot;
    result: List[U] = []
    for item in data:
        current = item
        for transform in transforms:
            current = transform(current)  # type: ignore
        result.append(current)  # type: ignore
    return result

# Usage
numbers = [1, 2, 3, 4]
result = pipeline(
    numbers,
    lambda x: x * 2,
    lambda x: str(x)
)  # result is List[str]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) Configuration Manager&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from typing import Dict, Any, Optional
from dataclasses import dataclass

@dataclass
class DatabaseConfig:
    host: str
    port: int = 5432
    username: str
    password: str
    database: str

@dataclass
class AppConfig:
    debug: bool = False
    database: DatabaseConfig
    allowed_hosts: List[str]
    secret_key: Optional[str] = None

def load_config(config_dict: Dict[str, Any]) -&amp;gt; AppConfig:
    db_config = DatabaseConfig(**config_dict[&quot;database&quot;])
    return AppConfig(
        debug=config_dict.get(&quot;debug&quot;, False),
        database=db_config,
        allowed_hosts=config_dict[&quot;allowed_hosts&quot;],
        secret_key=config_dict.get(&quot;secret_key&quot;)
    )
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;11. Type Checking Tools&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;🔧 Tools to check types: &amp;lt;/span&amp;gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;mypy&lt;/strong&gt; - Most popular&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pip install mypy
mypy your_script.py
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;pydantic&lt;/strong&gt; - Runtime type checking + data validation&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pydantic import BaseModel

class User(BaseModel):
    name: str
    age: int

user = User(name=&quot;Alice&quot;, age=30)  # Validates at runtime!
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;VS Code/PyCharm&lt;/strong&gt; - Built-in type checking with Pylance/Pyright
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;12. Best Practices&lt;/h2&gt;
&lt;h3&gt;✅ DO:&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 1. Use type hints for public APIs
def calculate_total(prices: List[float]) -&amp;gt; float:
    return sum(prices)

# 2. Use Optional for values that can be None
def find_by_id(id: int) -&amp;gt; Optional[User]:
    pass

# 3. Use type aliases for complex types
JSON = Dict[str, Any]

# 4. Use Literal for limited options
def set_mode(mode: Literal[&quot;read&quot;, &quot;write&quot;, &quot;append&quot;]) -&amp;gt; None:
    pass
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;❌ DON&apos;T:&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 1. Don&apos;t overuse Any (defeats purpose)
def process(data: Any) -&amp;gt; Any:  # Better to be specific

# 2. Don&apos;t ignore type errors without reason
result = func()  # type: ignore  # Add comment explaining why

# 3. Don&apos;t use type hints for everything in simple scripts
# For small scripts, they can be overkill
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;13. Comparison Table&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Python 3.8-3.9&lt;/th&gt;
&lt;th&gt;Python 3.10+&lt;/th&gt;
&lt;th&gt;Python 3.11+&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Union&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Union[int, str]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;int | str&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;int | str&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Optional&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Optional[str]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;str | None&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;str | None&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;List type&lt;/td&gt;
&lt;td&gt;&lt;code&gt;List[int]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;list[int]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;list[int]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dict type&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Dict[str, int]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;dict[str, int]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;dict[str, int]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Self reference&lt;/td&gt;
&lt;td&gt;Forward ref &lt;code&gt;&apos;MyClass&apos;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Forward ref &lt;code&gt;&apos;MyClass&apos;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Self&lt;/code&gt; type&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Python&apos;s &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;typing&amp;lt;/code&amp;gt; adds optional &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;type hints (类型提示)&amp;lt;/span&amp;gt; that won&apos;t affect runtime but make code &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;self-documenting, IDE-friendly, and catch bugs early&amp;lt;/span&amp;gt; with tools like &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;mypy&amp;lt;/code&amp;gt;.&amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Python collections</title><link>https://lxy-alexander.github.io/blog/posts/python/python-collections/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/python/python-collections/</guid><description>Python collections</description><pubDate>Mon, 09 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;&lt;strong&gt;I. Python &lt;code&gt;collections&lt;/code&gt; Module — Complete Learning Manual&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Python&apos;s &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;collections&amp;lt;/span&amp;gt; module provides &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;specialized container datatypes (特殊容器数据类型)&amp;lt;/span&amp;gt; that extend the built-in &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;dict&amp;lt;/code&amp;gt;, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;list&amp;lt;/code&amp;gt;, and &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;tuple&amp;lt;/code&amp;gt;. The seven main classes are: &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;defaultdict&amp;lt;/span&amp;gt;, &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Counter&amp;lt;/span&amp;gt;, &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;OrderedDict&amp;lt;/span&amp;gt;, &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;deque&amp;lt;/span&amp;gt;, &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;namedtuple&amp;lt;/span&amp;gt;, &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;ChainMap&amp;lt;/span&amp;gt;, and &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;UserDict / UserList / UserString&amp;lt;/span&amp;gt;. Each solves a specific pain-point of the standard built-ins with minimal overhead. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1. defaultdict — Default Value Dict (默认值字典)&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; A &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;defaultdict (默认值字典)&amp;lt;/span&amp;gt; behaves exactly like a regular &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;dict&amp;lt;/code&amp;gt;, except that accessing a &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;missing key (缺失键)&amp;lt;/span&amp;gt; automatically creates it by calling the &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;default_factory (默认工厂函数)&amp;lt;/span&amp;gt; — eliminating &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;KeyError&amp;lt;/code&amp;gt; and verbose &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;setdefault()&amp;lt;/code&amp;gt; boilerplate. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;1) Constructor (构造函数)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;collections.defaultdict(default_factory=None, **kwargs)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;default_factory&amp;lt;/span&amp;gt; is any zero-argument callable: &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;int&amp;lt;/code&amp;gt;, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;list&amp;lt;/code&amp;gt;, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;set&amp;lt;/code&amp;gt;, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;dict&amp;lt;/code&amp;gt;, or a custom &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;lambda&amp;lt;/code&amp;gt;.&lt;/p&gt;
&lt;h3&gt;2) &lt;code&gt;defaultdict(int)&lt;/code&gt; — Frequency counter (频率计数)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import defaultdict

text = &quot;apple banana apple cherry banana apple&quot;

freq = defaultdict(int)        # missing key → 0

for word in text.split():
    freq[word] += 1            # no KeyError on first access

print(dict(freq))
# → {&apos;apple&apos;: 3, &apos;banana&apos;: 2, &apos;cherry&apos;: 1}

# Compare with plain dict (verbose):
freq2 = {}
for word in text.split():
    freq2[word] = freq2.get(word, 0) + 1   # needs .get()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) &lt;code&gt;defaultdict(list)&lt;/code&gt; — Grouping (分组)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import defaultdict

students = [
    (&quot;Alice&quot;, &quot;Math&quot;),
    (&quot;Bob&quot;,   &quot;Science&quot;),
    (&quot;Alice&quot;, &quot;Science&quot;),
    (&quot;Carol&quot;, &quot;Math&quot;),
    (&quot;Bob&quot;,   &quot;Math&quot;),
]

by_name = defaultdict(list)

for name, subject in students:
    by_name[name].append(subject)   # missing key → [] automatically

print(dict(by_name))
# → {&apos;Alice&apos;: [&apos;Math&apos;, &apos;Science&apos;], &apos;Bob&apos;: [&apos;Science&apos;, &apos;Math&apos;], &apos;Carol&apos;: [&apos;Math&apos;]}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4) &lt;code&gt;defaultdict(set)&lt;/code&gt; — Unique grouping (去重分组)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import defaultdict

edges = [(1, 2), (1, 3), (2, 3), (1, 2)]   # duplicate edge (1,2)

graph = defaultdict(set)

for u, v in edges:
    graph[u].add(v)
    graph[v].add(u)

print(dict(graph))
# → {1: {2, 3}, 2: {1, 3}, 3: {1, 2}}   (no duplicates)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5) &lt;code&gt;defaultdict(dict)&lt;/code&gt; — Nested dict (嵌套字典)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import defaultdict

# 2-level nested defaultdict
matrix = defaultdict(lambda: defaultdict(int))

matrix[&quot;row1&quot;][&quot;col1&quot;] += 10
matrix[&quot;row1&quot;][&quot;col2&quot;] += 20
matrix[&quot;row2&quot;][&quot;col1&quot;] += 30

for row, cols in matrix.items():
    print(f&quot;{row}: {dict(cols)}&quot;)
# → row1: {&apos;col1&apos;: 10, &apos;col2&apos;: 20}
# → row2: {&apos;col1&apos;: 30}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6) Custom &lt;code&gt;default_factory&lt;/code&gt; (自定义工厂函数)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import defaultdict

# Factory that returns a specific default value
dd = defaultdict(lambda: &quot;N/A&quot;)
dd[&quot;name&quot;] = &quot;Alice&quot;

print(dd[&quot;name&quot;])     # → Alice
print(dd[&quot;age&quot;])      # → N/A   (key created with &quot;N/A&quot;)
print(dd[&quot;city&quot;])     # → N/A

# Factory with counter
id_counter = [0]
def next_id():
    id_counter[0] += 1
    return id_counter[0]

registry = defaultdict(next_id)
print(registry[&quot;alice&quot;])   # → 1
print(registry[&quot;bob&quot;])     # → 2
print(registry[&quot;alice&quot;])   # → 1  (already exists)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;7) &lt;code&gt;default_factory&lt;/code&gt; attribute — Inspect and change&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import defaultdict

dd = defaultdict(list)
print(dd.default_factory)    # → &amp;lt;class &apos;list&apos;&amp;gt;

dd.default_factory = set     # change factory at runtime
dd[&quot;new_key&quot;].add(42)
print(dict(dd))              # → {&apos;new_key&apos;: {42}}

dd.default_factory = None    # disable factory → KeyError on missing keys
try:
    _ = dd[&quot;missing&quot;]
except KeyError as e:
    print(f&quot;KeyError: {e}&quot;)  # → KeyError: &apos;missing&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;8) &lt;code&gt;__missing__&lt;/code&gt; — How defaultdict works internally&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import defaultdict

class MyDefaultDict(dict):
    &quot;&quot;&quot;Manual implementation of defaultdict logic.&quot;&quot;&quot;

    def __init__(self, factory):
        super().__init__()
        self.factory = factory

    def __missing__(self, key):
        # Called automatically when key is not found
        value = self.factory()
        self[key] = value
        return value

d = MyDefaultDict(list)
d[&quot;x&quot;].append(1)
d[&quot;x&quot;].append(2)
d[&quot;y&quot;].append(3)
print(dict(d))   # → {&apos;x&apos;: [1, 2], &apos;y&apos;: [3]}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;&lt;strong&gt;missing&lt;/strong&gt;&amp;lt;/code&amp;gt; is only triggered by &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;d[key]&amp;lt;/code&amp;gt; access, NOT by &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;d.get(key)&amp;lt;/code&amp;gt;.&amp;lt;/span&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;get()&amp;lt;/code&amp;gt; always returns &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;None&amp;lt;/code&amp;gt; (or the provided default) without creating the key.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;9) Inherits all &lt;code&gt;dict&lt;/code&gt; methods&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import defaultdict

dd = defaultdict(int, a=1, b=2)

# All standard dict methods work
print(dd.keys())              # → dict_keys([&apos;a&apos;, &apos;b&apos;])
print(dd.values())            # → dict_values([1, 2])
print(dd.items())             # → dict_items([(&apos;a&apos;, 1), (&apos;b&apos;, 2)])
print(dd.get(&quot;x&quot;, 99))        # → 99  (no key created)
print(&quot;a&quot; in dd)              # → True
dd.update({&quot;c&quot;: 3})
print(dd.pop(&quot;a&quot;))            # → 1
print(dict(dd))               # → {&apos;b&apos;: 2, &apos;c&apos;: 3}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Counter — Multiset / Frequency Map (计数器)&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; A &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Counter (计数器)&amp;lt;/span&amp;gt; is a &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;dict&amp;lt;/code&amp;gt; subclass designed for &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;counting hashable objects (统计可哈希对象)&amp;lt;/span&amp;gt;. Missing keys return &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;0&amp;lt;/code&amp;gt; instead of raising &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;KeyError&amp;lt;/code&amp;gt;. It supports &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;arithmetic operations (算术运算)&amp;lt;/span&amp;gt; between counters. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;1) Constructor — Three ways to create&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import Counter

# From an iterable
c1 = Counter(&quot;abracadabra&quot;)
print(c1)   # → Counter({&apos;a&apos;: 5, &apos;b&apos;: 2, &apos;r&apos;: 2, &apos;c&apos;: 1, &apos;d&apos;: 1})

# From a dict
c2 = Counter({&quot;cats&quot;: 4, &quot;dogs&quot;: 8})
print(c2)   # → Counter({&apos;dogs&apos;: 8, &apos;cats&apos;: 4})

# From keyword arguments
c3 = Counter(red=3, blue=1, green=5)
print(c3)   # → Counter({&apos;green&apos;: 5, &apos;red&apos;: 3, &apos;blue&apos;: 1})
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) Missing key → 0 (缺失键返回0)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import Counter

c = Counter(&quot;hello&quot;)
print(c[&quot;l&quot;])    # → 2  (exists)
print(c[&quot;z&quot;])    # → 0  (missing — no KeyError!)
print(&quot;z&quot; in c) # → False  (not stored, just returns 0)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) &lt;code&gt;most_common(n)&lt;/code&gt; — Top N elements (最高频N个元素)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import Counter

words = &quot;the quick brown fox jumps over the lazy dog the fox&quot;.split()
c = Counter(words)

print(c.most_common(3))
# → [(&apos;the&apos;, 3), (&apos;fox&apos;, 2), (&apos;quick&apos;, 1)]

print(c.most_common())        # all elements, sorted by frequency
print(c.most_common()[:-4:-1])# least common 3 (tail trick)
# → [(&apos;dog&apos;, 1), (&apos;lazy&apos;, 1), (&apos;over&apos;, 1)]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4) &lt;code&gt;elements()&lt;/code&gt; — Expand back to iterable (展开为可迭代)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import Counter

c = Counter(a=3, b=1, c=2)

print(list(c.elements()))
# → [&apos;a&apos;, &apos;a&apos;, &apos;a&apos;, &apos;b&apos;, &apos;c&apos;, &apos;c&apos;]  (ordered by insertion)

# Reconstruct a sorted list
print(sorted(c.elements()))
# → [&apos;a&apos;, &apos;a&apos;, &apos;a&apos;, &apos;b&apos;, &apos;c&apos;, &apos;c&apos;]

# Elements with count ≤ 0 are excluded
c[&quot;x&quot;] = -1
print(list(c.elements()))    # &apos;x&apos; not included
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5) &lt;code&gt;subtract()&lt;/code&gt; / &lt;code&gt;update()&lt;/code&gt; — In-place operations (就地运算)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import Counter

inventory = Counter(apples=10, oranges=5, bananas=8)

# subtract: reduces counts (allows negatives)
sold = Counter(apples=3, oranges=5, bananas=10)
inventory.subtract(sold)
print(inventory)
# → Counter({&apos;apples&apos;: 7, &apos;bananas&apos;: -2, &apos;oranges&apos;: 0})

# update: adds counts (merges)
restocked = Counter(apples=5, bananas=15)
inventory.update(restocked)
print(inventory)
# → Counter({&apos;bananas&apos;: 13, &apos;apples&apos;: 12, &apos;oranges&apos;: 0})
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6) Arithmetic operators (算术运算符)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import Counter

a = Counter(x=4, y=2, z=0)
b = Counter(x=1, y=3, w=5)

print(a + b)    # add counts
# → Counter({&apos;x&apos;: 5, &apos;w&apos;: 5, &apos;y&apos;: 5})

print(a - b)    # subtract, keep only positives
# → Counter({&apos;x&apos;: 3})

print(a &amp;amp; b)    # intersection: min of each count
# → Counter({&apos;x&apos;: 1, &apos;y&apos;: 2})

print(a | b)    # union: max of each count
# → Counter({&apos;w&apos;: 5, &apos;x&apos;: 4, &apos;y&apos;: 3})

# Unary operators
print(+a)       # remove zero and negative counts
print(-a)       # negate — flip sign, keep negatives as positives
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;7) Total count and filtering (总计数与过滤)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import Counter

c = Counter(a=5, b=3, c=0, d=-2)

# Total of all positive counts (Python 3.10+)
print(c.total())       # → 8   (5+3+0 = 8, negatives excluded)

# Keep only positive counts
positive = +c
print(positive)        # → Counter({&apos;a&apos;: 5, &apos;b&apos;: 3})

# Keep only negative counts (useful for &quot;owed&quot; quantities)
negative = -c
print(negative)        # → Counter({&apos;d&apos;: 2})
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;8) Practical: anagram check, top-K, word frequency&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import Counter

# ── Anagram check (变位词检测) ──
def is_anagram(s1: str, s2: str) -&amp;gt; bool:
    return Counter(s1.lower()) == Counter(s2.lower())

print(is_anagram(&quot;listen&quot;, &quot;silent&quot;))   # → True
print(is_anagram(&quot;hello&quot;,  &quot;world&quot;))    # → False

# ── Character frequency difference ──
def missing_chars(have: str, need: str) -&amp;gt; Counter:
    deficit = Counter(need) - Counter(have)
    return deficit

print(missing_chars(&quot;aab&quot;, &quot;aaabbc&quot;))
# → Counter({&apos;a&apos;: 1, &apos;b&apos;: 1, &apos;c&apos;: 1})

# ── Top-K frequent words ──
import re

text = &quot;&quot;&quot;To be or not to be that is the question
          whether tis nobler in the mind to suffer&quot;&quot;&quot;

words  = re.findall(r&apos;\w+&apos;, text.lower())
top5   = Counter(words).most_common(5)
print(top5)
# → [(&apos;to&apos;, 3), (&apos;be&apos;, 2), (&apos;the&apos;, 2), (&apos;or&apos;, 1), (&apos;not&apos;, 1)]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;9) Inherits all &lt;code&gt;dict&lt;/code&gt; methods&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import Counter

c = Counter(&quot;mississippi&quot;)

print(c.keys())           # → dict_keys([&apos;m&apos;, &apos;i&apos;, &apos;s&apos;, &apos;p&apos;])
print(c.values())         # → dict_values([1, 4, 4, 2])
print(c.items())          # → dict_items([(&apos;m&apos;, 1), (&apos;i&apos;, 4), (&apos;s&apos;, 4), (&apos;p&apos;, 2)])
print(c.get(&quot;i&quot;))         # → 4
print(c.get(&quot;z&quot;))         # → None   (get() returns None, not 0)

# del sets count to 0 conceptually, but removes the key
del c[&quot;m&quot;]
print(&quot;m&quot; in c)           # → False
print(c[&quot;m&quot;])             # → 0  (missing key returns 0)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;3. OrderedDict — Ordered Dictionary (有序字典)&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Since Python 3.7, plain &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;dict&amp;lt;/code&amp;gt; preserves insertion order. &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;OrderedDict (有序字典)&amp;lt;/span&amp;gt; still offers unique advantages: &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;order-sensitive equality&amp;lt;/span&amp;gt;, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;move_to_end()&amp;lt;/code&amp;gt;, and &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;popitem(last=True/False)&amp;lt;/code&amp;gt; for implementing &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;LRU Cache (LRU缓存)&amp;lt;/span&amp;gt; and similar structures. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;1) Basic usage and order-sensitive equality&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import OrderedDict

od = OrderedDict()
od[&quot;banana&quot;] = 3
od[&quot;apple&quot;]  = 5
od[&quot;cherry&quot;] = 1

print(od)
# → OrderedDict([(&apos;banana&apos;, 3), (&apos;apple&apos;, 5), (&apos;cherry&apos;, 1)])

# Order-sensitive equality (顺序敏感的相等判断)
od1 = OrderedDict([(&quot;a&quot;, 1), (&quot;b&quot;, 2)])
od2 = OrderedDict([(&quot;b&quot;, 2), (&quot;a&quot;, 1)])
d1  = {&quot;a&quot;: 1, &quot;b&quot;: 2}

print(od1 == od2)   # → False  (same keys/values, different order)
print(od1 == d1)    # → True   (OrderedDict == dict ignores order)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) &lt;code&gt;move_to_end(key, last=True)&lt;/code&gt; — Reposition a key&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import OrderedDict

od = OrderedDict.fromkeys(&quot;ABCDE&quot;)

od.move_to_end(&quot;B&quot;)          # move B to end (last=True default)
print(list(od))              # → [&apos;A&apos;, &apos;C&apos;, &apos;D&apos;, &apos;E&apos;, &apos;B&apos;]

od.move_to_end(&quot;E&quot;, last=False)  # move E to front
print(list(od))              # → [&apos;E&apos;, &apos;A&apos;, &apos;C&apos;, &apos;D&apos;, &apos;B&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) &lt;code&gt;popitem(last=True)&lt;/code&gt; — LIFO / FIFO removal&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import OrderedDict

od = OrderedDict.fromkeys(&quot;ABCDE&quot;)

print(od.popitem(last=True))    # → (&apos;E&apos;, None)  LIFO (like a stack)
print(od.popitem(last=False))   # → (&apos;A&apos;, None)  FIFO (like a queue)
print(list(od))                 # → [&apos;B&apos;, &apos;C&apos;, &apos;D&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4) LRU Cache implementation (LRU缓存实现)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import OrderedDict

class LRUCache:
    &quot;&quot;&quot;
    Least Recently Used Cache (最近最少使用缓存)
    using OrderedDict for O(1) get and put.
    &quot;&quot;&quot;

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

    def get(self, key: int) -&amp;gt; int:
        if key not in self.cache:
            return -1
        self.cache.move_to_end(key)    # mark as recently used
        return self.cache[key]

    def put(self, key: int, value: int) -&amp;gt; None:
        if key in self.cache:
            self.cache.move_to_end(key)
        self.cache[key] = value
        if len(self.cache) &amp;gt; self.capacity:
            self.cache.popitem(last=False)  # evict least recently used

cache = LRUCache(3)
cache.put(1, 10)
cache.put(2, 20)
cache.put(3, 30)
print(cache.get(1))   # → 10  (1 moved to end)
cache.put(4, 40)      # evicts key 2 (least recently used)
print(cache.get(2))   # → -1  (evicted)
print(cache.get(3))   # → 30
print(cache.get(4))   # → 40
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5) &lt;code&gt;__reversed__()&lt;/code&gt; — Reverse iteration&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import OrderedDict

od = OrderedDict([(&quot;a&quot;, 1), (&quot;b&quot;, 2), (&quot;c&quot;, 3)])

for key in reversed(od):
    print(key, od[key])
# → c 3
# → b 2
# → a 1
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;4. deque — Double-Ended Queue (双端队列)&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; A &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;deque (双端队列)&amp;lt;/span&amp;gt; supports &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;O(1) append and pop from both ends&amp;lt;/span&amp;gt;. Unlike a &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;list&amp;lt;/code&amp;gt; (where &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;insert(0, x)&amp;lt;/code&amp;gt; is O(n)), deque is the correct data structure for &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;queues (队列)&amp;lt;/span&amp;gt;, &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;stacks (栈)&amp;lt;/span&amp;gt;, and &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;sliding windows (滑动窗口)&amp;lt;/span&amp;gt;. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;1) Constructor&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import deque

d1 = deque()                         # empty
d2 = deque([1, 2, 3, 4, 5])          # from iterable
d3 = deque(&quot;abcde&quot;)                  # from string
d4 = deque(range(10), maxlen=5)      # bounded deque (固定长度)

print(d2)   # → deque([1, 2, 3, 4, 5])
print(d4)   # → deque([5, 6, 7, 8, 9], maxlen=5)  (first 5 discarded)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) &lt;code&gt;append()&lt;/code&gt; / &lt;code&gt;appendleft()&lt;/code&gt; — Add to ends (两端添加)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import deque

d = deque([3, 4, 5])

d.append(6)         # right end  → deque([3, 4, 5, 6])
d.appendleft(2)     # left end   → deque([2, 3, 4, 5, 6])
d.appendleft(1)     #            → deque([1, 2, 3, 4, 5, 6])

print(d)            # → deque([1, 2, 3, 4, 5, 6])
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) &lt;code&gt;pop()&lt;/code&gt; / &lt;code&gt;popleft()&lt;/code&gt; — Remove from ends (两端弹出)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import deque

d = deque([1, 2, 3, 4, 5])

print(d.pop())       # → 5   (right)  → deque([1, 2, 3, 4])
print(d.popleft())   # → 1   (left)   → deque([2, 3, 4])
print(d)             # → deque([2, 3, 4])
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4) &lt;code&gt;extend()&lt;/code&gt; / &lt;code&gt;extendleft()&lt;/code&gt; — Batch add (批量添加)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import deque

d = deque([3, 4])

d.extend([5, 6, 7])          # right: → deque([3, 4, 5, 6, 7])
d.extendleft([2, 1, 0])      # left, each prepended individually
                             # 2 → [2,3..], 1 → [1,2,3..], 0 → [0,1,2,3..]
print(d)   # → deque([0, 1, 2, 3, 4, 5, 6, 7])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;extendleft([a, b, c])&amp;lt;/code&amp;gt; results in &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;[c, b, a, ...]&amp;lt;/code&amp;gt; because each element is prepended one by one — the iterable is effectively reversed.&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;5) &lt;code&gt;rotate(n)&lt;/code&gt; — Circular rotation (循环旋转)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import deque

d = deque([1, 2, 3, 4, 5])

d.rotate(2)     # rotate RIGHT by 2
print(d)        # → deque([4, 5, 1, 2, 3])

d.rotate(-2)    # rotate LEFT by 2 (undo)
print(d)        # → deque([1, 2, 3, 4, 5])

# Circular buffer simulation (循环缓冲区)
ring = deque(range(5))
for _ in range(8):
    print(ring[0], end=&quot; &quot;)
    ring.rotate(-1)
# → 0 1 2 3 4 0 1 2
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6) &lt;code&gt;maxlen&lt;/code&gt; — Bounded / sliding window (有界滑动窗口)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import deque

# Keep only the last 3 elements
window = deque(maxlen=3)

for i in range(7):
    window.append(i)
    print(f&quot;added {i}: {list(window)}&quot;)
# → added 0: [0]
# → added 1: [0, 1]
# → added 2: [0, 1, 2]
# → added 3: [1, 2, 3]   ← 0 dropped automatically
# → added 4: [2, 3, 4]
# → added 5: [3, 4, 5]
# → added 6: [4, 5, 6]

# Moving average (滑动平均)
def moving_average(data, window_size):
    w = deque(maxlen=window_size)
    result = []
    for val in data:
        w.append(val)
        result.append(sum(w) / len(w))
    return result

print(moving_average([1, 2, 3, 4, 5, 6], 3))
# → [1.0, 1.5, 2.0, 3.0, 4.0, 5.0]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;7) &lt;code&gt;insert()&lt;/code&gt; / &lt;code&gt;remove()&lt;/code&gt; / &lt;code&gt;count()&lt;/code&gt; / &lt;code&gt;index()&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import deque

d = deque([1, 2, 3, 2, 4])

d.insert(2, 99)       # insert 99 at position 2
print(d)              # → deque([1, 2, 99, 3, 2, 4])

d.remove(99)          # remove first occurrence
print(d)              # → deque([1, 2, 3, 2, 4])

print(d.count(2))     # → 2  (occurrences of 2)
print(d.index(3))     # → 2  (first index of 3)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;8) &lt;code&gt;reverse()&lt;/code&gt; / &lt;code&gt;copy()&lt;/code&gt; / &lt;code&gt;clear()&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import deque

d = deque([1, 2, 3, 4, 5])

d.reverse()
print(d)        # → deque([5, 4, 3, 2, 1])

d2 = d.copy()   # shallow copy
d2.append(0)
print(d)        # → deque([5, 4, 3, 2, 1])  (original unchanged)

d.clear()
print(d)        # → deque([])
print(len(d))   # → 0
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;9) Performance comparison vs list (与list性能对比)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import timeit
from collections import deque

# prepend 100_000 items
list_time  = timeit.timeit(lambda: [0] * 100_000, number=100)
deque_time = timeit.timeit(lambda: deque([0] * 100_000), number=100)

# insert at front
n = 10_000
t_list  = timeit.timeit(lambda: [None] + list(range(n)), number=1000)
t_deque = timeit.timeit(lambda: deque([None]) + deque(range(n)), number=1000)

print(f&quot;list  front-insert: {t_list:.4f}s&quot;)
print(f&quot;deque front-insert: {t_deque:.4f}s&quot;)
# deque is orders of magnitude faster for front operations
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;5. namedtuple — Immutable Record (具名元组)&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;namedtuple&amp;lt;/code&amp;gt; creates a &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;tuple subclass (元组子类)&amp;lt;/span&amp;gt; whose fields can be accessed by &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;name&amp;lt;/span&amp;gt; as well as by index. It is &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;immutable (不可变)&amp;lt;/span&amp;gt;, memory-efficient, and self-documenting. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;1) Factory function &lt;code&gt;namedtuple(typename, field_names)&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import namedtuple

# Three equivalent ways to define field names:
Point = namedtuple(&quot;Point&quot;, [&quot;x&quot;, &quot;y&quot;])
Point = namedtuple(&quot;Point&quot;, &quot;x y&quot;)
Point = namedtuple(&quot;Point&quot;, &quot;x, y&quot;)

p = Point(3, 4)
print(p)           # → Point(x=3, y=4)
print(p.x, p.y)    # → 3 4       (by name)
print(p[0], p[1])  # → 3 4       (by index)
print(p == (3, 4)) # → True      (is a tuple subclass)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) &lt;code&gt;_make()&lt;/code&gt; — Create from iterable (从可迭代对象创建)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import namedtuple

Employee = namedtuple(&quot;Employee&quot;, &quot;name age department salary&quot;)

data = [&quot;Alice&quot;, 30, &quot;Engineering&quot;, 95000]
emp  = Employee._make(data)
print(emp)
# → Employee(name=&apos;Alice&apos;, age=30, department=&apos;Engineering&apos;, salary=95000)

# From CSV row
import csv, io
csv_data = &quot;Bob,25,Marketing,60000&quot;
for row in csv.reader(io.StringIO(csv_data)):
    e = Employee._make(row)
    print(f&quot;{e.name} in {e.department}&quot;)
# → Bob in Marketing
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) &lt;code&gt;_asdict()&lt;/code&gt; — Convert to OrderedDict (转换为有序字典)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import namedtuple

Point3D = namedtuple(&quot;Point3D&quot;, &quot;x y z&quot;)
p = Point3D(1, 2, 3)

d = p._asdict()
print(d)            # → {&apos;x&apos;: 1, &apos;y&apos;: 2, &apos;z&apos;: 3}
print(type(d))      # → &amp;lt;class &apos;dict&apos;&amp;gt;

# Serialize to JSON
import json
print(json.dumps(p._asdict()))   # → {&quot;x&quot;: 1, &quot;y&quot;: 2, &quot;z&quot;: 3}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4) &lt;code&gt;_replace()&lt;/code&gt; — Create modified copy (创建修改副本)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import namedtuple

# namedtuple is IMMUTABLE — _replace() returns a new instance
Person = namedtuple(&quot;Person&quot;, &quot;name age city&quot;)
alice  = Person(&quot;Alice&quot;, 30, &quot;NYC&quot;)

# &quot;update&quot; one field
older_alice = alice._replace(age=31)
print(alice)        # → Person(name=&apos;Alice&apos;, age=30, city=&apos;NYC&apos;)  (unchanged)
print(older_alice)  # → Person(name=&apos;Alice&apos;, age=31, city=&apos;NYC&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5) &lt;code&gt;_fields&lt;/code&gt; / &lt;code&gt;_field_defaults&lt;/code&gt; — Introspection (内省)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import namedtuple

Config = namedtuple(&quot;Config&quot;, &quot;host port timeout&quot;, defaults=[&quot;localhost&quot;, 8080, 30])

print(Config._fields)          # → (&apos;host&apos;, &apos;port&apos;, &apos;timeout&apos;)
print(Config._field_defaults)  # → {&apos;host&apos;: &apos;localhost&apos;, &apos;port&apos;: 8080, &apos;timeout&apos;: 30}

c1 = Config()                  # all defaults
c2 = Config(&quot;example.com&quot;)     # override host only
print(c1)  # → Config(host=&apos;localhost&apos;, port=8080, timeout=30)
print(c2)  # → Config(host=&apos;example.com&apos;, port=8080, timeout=30)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6) &lt;code&gt;rename=True&lt;/code&gt; — Auto-rename invalid field names&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import namedtuple

# &apos;class&apos; and &apos;2bad&apos; are invalid Python identifiers
T = namedtuple(&quot;T&quot;, [&quot;class&quot;, &quot;2bad&quot;, &quot;ok&quot;], rename=True)
print(T._fields)   # → (&apos;_0&apos;, &apos;_1&apos;, &apos;ok&apos;)  (invalid names → _index)

t = T(1, 2, 3)
print(t._0, t._1, t.ok)   # → 1 2 3
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;7) Subclassing namedtuple — Adding methods&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import namedtuple
import math

class Vector(namedtuple(&quot;Vector&quot;, &quot;x y&quot;)):
    &quot;&quot;&quot;Extend namedtuple with custom methods.&quot;&quot;&quot;

    def magnitude(self) -&amp;gt; float:
        return math.sqrt(self.x**2 + self.y**2)

    def dot(self, other: &quot;Vector&quot;) -&amp;gt; float:
        return self.x * other.x + self.y * other.y

    def __add__(self, other):
        return Vector(self.x + other.x, self.y + other.y)

v1 = Vector(3, 4)
v2 = Vector(1, 2)

print(v1.magnitude())   # → 5.0
print(v1.dot(v2))       # → 11.0
print(v1 + v2)          # → Vector(x=4, y=6)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;6. ChainMap — Multi-scope Lookup (多层级查找映射)&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; A &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;ChainMap (链式映射)&amp;lt;/span&amp;gt; groups multiple dicts into a single, updateable view. Lookups search the dicts &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;from first to last&amp;lt;/span&amp;gt;, returning the first match. Writes always go to the &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;first map&amp;lt;/span&amp;gt;. Perfect for modeling &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;variable scopes (变量作用域)&amp;lt;/span&amp;gt; like Python&apos;s own LEGB rule. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;1) Basic lookup (基本查找)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import ChainMap

defaults  = {&quot;color&quot;: &quot;red&quot;,  &quot;user&quot;: &quot;guest&quot;, &quot;timeout&quot;: 30}
env_vars  = {&quot;color&quot;: &quot;blue&quot;, &quot;debug&quot;: True}
cli_args  = {&quot;timeout&quot;: 10}

# Priority: cli_args &amp;gt; env_vars &amp;gt; defaults
config = ChainMap(cli_args, env_vars, defaults)

print(config[&quot;color&quot;])    # → blue   (from env_vars, overrides defaults)
print(config[&quot;user&quot;])     # → guest  (only in defaults)
print(config[&quot;timeout&quot;])  # → 10     (from cli_args, highest priority)
print(config[&quot;debug&quot;])    # → True   (from env_vars)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) Writes go to first map only (写入仅影响第一个映射)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import ChainMap

base    = {&quot;x&quot;: 1, &quot;y&quot;: 2}
overlay = {}

cm = ChainMap(overlay, base)

cm[&quot;x&quot;] = 99      # written to overlay (first map)
cm[&quot;z&quot;] = 0       # new key also goes to overlay

print(overlay)    # → {&apos;x&apos;: 99, &apos;z&apos;: 0}
print(base)       # → {&apos;x&apos;: 1, &apos;y&apos;: 2}   (unchanged!)
print(cm[&quot;x&quot;])    # → 99   (overlay shadows base)
print(cm[&quot;y&quot;])    # → 2    (from base)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) &lt;code&gt;new_child(m=None)&lt;/code&gt; — Push a new scope (推入新作用域)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import ChainMap

# Simulate nested scopes (模拟嵌套作用域)
global_scope = ChainMap({&quot;x&quot;: 1, &quot;y&quot;: 2})
local_scope  = global_scope.new_child({&quot;x&quot;: 10, &quot;z&quot;: 3})

print(local_scope[&quot;x&quot;])   # → 10   (local shadows global)
print(local_scope[&quot;y&quot;])   # → 2    (falls through to global)
print(local_scope[&quot;z&quot;])   # → 3    (local only)

# Pop the local scope (返回父作用域)
parent_scope = local_scope.parents
print(parent_scope[&quot;x&quot;])  # → 1    (original global value)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4) &lt;code&gt;maps&lt;/code&gt; attribute — Access underlying dicts (访问底层字典列表)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import ChainMap

cm = ChainMap({&quot;a&quot;: 1}, {&quot;b&quot;: 2}, {&quot;c&quot;: 3})

print(cm.maps)
# → [{&apos;a&apos;: 1}, {&apos;b&apos;: 2}, {&apos;c&apos;: 3}]

# Modify underlying dicts directly
cm.maps[1][&quot;b&quot;] = 99
print(cm[&quot;b&quot;])   # → 99
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5) Practical: CLI argument + environment + defaults&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import ChainMap
import os

def build_config(cli_args: dict) -&amp;gt; ChainMap:
    &quot;&quot;&quot;Three-tier configuration (三层配置): CLI &amp;gt; ENV &amp;gt; defaults.&quot;&quot;&quot;
    defaults = {
        &quot;host&quot;:    &quot;localhost&quot;,
        &quot;port&quot;:    8080,
        &quot;debug&quot;:   False,
        &quot;workers&quot;: 4,
    }
    env_config = {
        k.lower().replace(&quot;app_&quot;, &quot;&quot;): v
        for k, v in os.environ.items()
        if k.startswith(&quot;APP_&quot;)
    }
    return ChainMap(cli_args, env_config, defaults)

config = build_config({&quot;port&quot;: 9090, &quot;debug&quot;: True})
print(config[&quot;host&quot;])     # → localhost (from defaults)
print(config[&quot;port&quot;])     # → 9090      (from cli_args)
print(config[&quot;debug&quot;])    # → True      (from cli_args)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;7. UserDict / UserList / UserString — Custom Containers (自定义容器基类)&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;UserDict&amp;lt;/span&amp;gt;, &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;UserList&amp;lt;/span&amp;gt;, and &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;UserString&amp;lt;/span&amp;gt; are wrapper classes designed for &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;safe subclassing&amp;lt;/span&amp;gt;. Subclassing built-in &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;dict&amp;lt;/code&amp;gt; / &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;list&amp;lt;/code&amp;gt; directly can miss overrides because C-level methods call each other without going through Python. &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;UserDict&amp;lt;/code&amp;gt; etc. route ALL operations through Python methods. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;1) &lt;code&gt;UserDict&lt;/code&gt; — Custom dict with validation (带验证的自定义字典)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import UserDict

class TypedDict(UserDict):
    &quot;&quot;&quot;A dict that only accepts string keys and int values.&quot;&quot;&quot;

    def __setitem__(self, key, value):
        if not isinstance(key, str):
            raise TypeError(f&quot;Key must be str, got {type(key).__name__}&quot;)
        if not isinstance(value, int):
            raise TypeError(f&quot;Value must be int, got {type(value).__name__}&quot;)
        super().__setitem__(key, value)   # delegate to UserDict

td = TypedDict()
td[&quot;score&quot;] = 100
td[&quot;count&quot;] = 42
print(td)            # → {&apos;score&apos;: 100, &apos;count&apos;: 42}

try:
    td[123] = 10     # invalid key
except TypeError as e:
    print(f&quot;Error: {e}&quot;)   # → Error: Key must be str, got int

try:
    td[&quot;x&quot;] = &quot;hello&quot;  # invalid value
except TypeError as e:
    print(f&quot;Error: {e}&quot;)   # → Error: Value must be int, got str
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) &lt;code&gt;UserList&lt;/code&gt; — Custom list with constraints (带约束的自定义列表)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import UserList

class BoundedList(UserList):
    &quot;&quot;&quot;A list that enforces a maximum length (最大长度限制).&quot;&quot;&quot;

    def __init__(self, maxlen: int, iterable=()):
        self.maxlen = maxlen
        super().__init__()
        for item in iterable:
            self.append(item)

    def append(self, item):
        if len(self.data) &amp;gt;= self.maxlen:
            raise OverflowError(f&quot;List is full (max {self.maxlen})&quot;)
        self.data.append(item)

    def insert(self, index, item):
        if len(self.data) &amp;gt;= self.maxlen:
            raise OverflowError(f&quot;List is full (max {self.maxlen})&quot;)
        self.data.insert(index, item)

bl = BoundedList(3, [1, 2, 3])
print(bl)   # → [1, 2, 3]

try:
    bl.append(4)
except OverflowError as e:
    print(f&quot;Error: {e}&quot;)   # → Error: List is full (max 3)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) &lt;code&gt;UserString&lt;/code&gt; — Custom string with transforms (带转换的自定义字符串)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import UserString

class SlugString(UserString):
    &quot;&quot;&quot;Auto-converts string to URL-safe slug (URL友好字符串).&quot;&quot;&quot;

    def __init__(self, seq=&quot;&quot;):
        import re
        slug = re.sub(r&apos;[^a-z0-9]+&apos;, &apos;-&apos;, str(seq).lower()).strip(&apos;-&apos;)
        super().__init__(slug)

    def __add__(self, other):
        return SlugString(self.data + &quot;-&quot; + str(other))

s = SlugString(&quot;Hello World! This is a Test.&quot;)
print(s)           # → hello-world-this-is-a-test

s2 = s + &quot;extra&quot;
print(s2)          # → hello-world-this-is-a-test-extra
print(len(s))      # → 28   (all str methods work)
print(s.upper())   # → HELLO-WORLD-THIS-IS-A-TEST
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;8. Comparison Table (对比总结)&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Class&lt;/th&gt;
&lt;th&gt;Based on&lt;/th&gt;
&lt;th&gt;Missing key&lt;/th&gt;
&lt;th&gt;Ordered&lt;/th&gt;
&lt;th&gt;Mutable&lt;/th&gt;
&lt;th&gt;Best use case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;defaultdict&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;dict&lt;/td&gt;
&lt;td&gt;auto-creates&lt;/td&gt;
&lt;td&gt;insertion&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Grouping, counting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Counter&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;dict&lt;/td&gt;
&lt;td&gt;returns 0&lt;/td&gt;
&lt;td&gt;insertion&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Frequency, multiset ops&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;OrderedDict&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;dict&lt;/td&gt;
&lt;td&gt;KeyError&lt;/td&gt;
&lt;td&gt;insertion&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;LRU cache, order-sensitive eq&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;deque&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;list-like&lt;/td&gt;
&lt;td&gt;IndexError&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Queue, stack, sliding window&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;namedtuple&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;tuple&lt;/td&gt;
&lt;td&gt;AttributeError&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;Immutable records, CSV rows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;ChainMap&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;dict view&lt;/td&gt;
&lt;td&gt;KeyError&lt;/td&gt;
&lt;td&gt;first-wins&lt;/td&gt;
&lt;td&gt;✅ (first)&lt;/td&gt;
&lt;td&gt;Config layers, scopes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;UserDict&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;dict&lt;/td&gt;
&lt;td&gt;KeyError&lt;/td&gt;
&lt;td&gt;insertion&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Safe dict subclassing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt; Use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;defaultdict&amp;lt;/code&amp;gt; to eliminate &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;KeyError&amp;lt;/code&amp;gt; boilerplate, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Counter&amp;lt;/code&amp;gt; for frequency analysis and multiset arithmetic, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;deque&amp;lt;/code&amp;gt; when you need O(1) operations on both ends, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;namedtuple&amp;lt;/code&amp;gt; for self-documenting immutable records, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;OrderedDict&amp;lt;/code&amp;gt; for LRU caches and order-sensitive comparisons, and &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;ChainMap&amp;lt;/code&amp;gt; for multi-tier configuration or scope simulation. &amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Python global</title><link>https://lxy-alexander.github.io/blog/posts/python/python-global/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/python/python-global/</guid><description>Python global</description><pubDate>Mon, 09 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;&lt;strong&gt;I. Understanding &lt;code&gt;global&lt;/code&gt; in Python&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt;
The &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;global&amp;lt;/code&amp;gt; keyword lets you modify &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;global variables (全局变量)&amp;lt;/span&amp;gt; inside functions. Without it, assignments create &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;local variables (局部变量)&amp;lt;/span&amp;gt; instead.
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;1. Scope Basics in Python&lt;/h2&gt;
&lt;p&gt;Every variable in Python has a defined &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;scope (作用域)&amp;lt;/span&amp;gt; – the region of code where it is accessible.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Global scope&lt;/strong&gt;: Variables defined outside any function&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Local scope&lt;/strong&gt;: Variables defined inside a function&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;# Global variable
x = 10

def my_function():
    # Local variable (different from global x)
    x = 5
    print(&quot;Inside function:&quot;, x)

my_function()  # Output: Inside function: 5
print(&quot;Outside function:&quot;, x)  # Output: Outside function: 10
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; In the example above, the &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;x&amp;lt;/code&amp;gt; inside the function is &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;completely separate&amp;lt;/span&amp;gt; from the global &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;x&amp;lt;/code&amp;gt;. This is Python&apos;s way of preventing accidental modification of global variables.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;2. The Problem: Modifying Global Variables&lt;/h2&gt;
&lt;p&gt;When you try to &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;modify&amp;lt;/span&amp;gt; a global variable inside a function without declaring it as &lt;code&gt;global&lt;/code&gt;, Python creates a new local variable instead.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;counter = 0

def increment():
    # This creates a NEW local variable &apos;counter&apos;
    counter += 1  # ❌ ERROR!

# increment()  # Uncommenting this line causes UnboundLocalError
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Pitfall: &amp;lt;/span&amp;gt;This code raises &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;UnboundLocalError: local variable &apos;counter&apos; referenced before assignment&amp;lt;/code&amp;gt; because Python sees the assignment and treats &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;counter&amp;lt;/code&amp;gt; as a local variable, but it&apos;s being referenced before it&apos;s defined.&lt;/p&gt;
&lt;h2&gt;3. The Solution: Using &lt;code&gt;global&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;global&amp;lt;/code&amp;gt; keyword tells Python: &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;&quot;This variable belongs to the global scope&quot;&amp;lt;/span&amp;gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;counter = 0  # Global variable

def increment():
    global counter  # Declare that we&apos;re using the global counter
    counter = counter + 1
    print(f&quot;Counter is now: {counter}&quot;)

increment()  # Output: Counter is now: 1
increment()  # Output: Counter is now: 2
print(f&quot;Global counter: {counter}&quot;)  # Output: Global counter: 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt;The &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;global&amp;lt;/code&amp;gt; statement must come &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;before&amp;lt;/span&amp;gt; any use of the variable in the function.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;4. Multiple Global Variables&lt;/h2&gt;
&lt;p&gt;You can declare multiple global variables in a single statement:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;name = &quot;Python&quot;
version = 3.9
year = 2023

def update_info():
    global name, version, year
    name = &quot;Python 3&quot;
    version = 3.11
    year = 2024

update_info()
print(f&quot;{name} {version} ({year})&quot;)  # Output: Python 3 3.11 (2024)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;5. Global vs Local: A Comparison&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Local Variable&lt;/th&gt;
&lt;th&gt;Global Variable&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;Scope (作用域)&amp;lt;/span&amp;gt;&lt;/td&gt;
&lt;td&gt;Inside function only&lt;/td&gt;
&lt;td&gt;Throughout the module&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;Declaration&amp;lt;/span&amp;gt;&lt;/td&gt;
&lt;td&gt;Automatic on assignment&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;global&amp;lt;/code&amp;gt; keyword required inside functions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;Read access&amp;lt;/span&amp;gt;&lt;/td&gt;
&lt;td&gt;Direct access&lt;/td&gt;
&lt;td&gt;Direct access (without &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;global&amp;lt;/code&amp;gt; for reading)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;Write access&amp;lt;/span&amp;gt;&lt;/td&gt;
&lt;td&gt;Direct assignment&lt;/td&gt;
&lt;td&gt;Requires &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;global&amp;lt;/code&amp;gt; declaration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;Memory (内存)&amp;lt;/span&amp;gt;&lt;/td&gt;
&lt;td&gt;Created when function runs&lt;/td&gt;
&lt;td&gt;Created when module loads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;Best practice&amp;lt;/span&amp;gt;&lt;/td&gt;
&lt;td&gt;Preferred for temporary values&lt;/td&gt;
&lt;td&gt;Use sparingly, prefer parameters&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;6. Reading Global Variables (Without &lt;code&gt;global&lt;/code&gt;)&lt;/h2&gt;
&lt;p&gt;Interesting fact: You can &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;read&amp;lt;/span&amp;gt; global variables without the &lt;code&gt;global&lt;/code&gt; keyword:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;message = &quot;Hello, World!&quot;

def show_message():
    # No &apos;global&apos; needed for reading
    print(message)  # This works!

show_message()  # Output: Hello, World!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Warning: &amp;lt;/span&amp;gt;This only works for &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;reading (读取)&amp;lt;/span&amp;gt;. The moment you try to assign a value, Python treats it as a local variable unless you use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;global&amp;lt;/code&amp;gt;.&lt;/p&gt;
&lt;h2&gt;7. Nested Functions and &lt;code&gt;global&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;global&lt;/code&gt; keyword always refers to the &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;module-level (模块级别)&amp;lt;/span&amp;gt; scope, not the enclosing function scope.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;x = &quot;global x&quot;

def outer():
    x = &quot;outer x&quot;
    
    def inner():
        global x  # This refers to the module-level &apos;x&apos;, not outer&apos;s &apos;x&apos;
        x = &quot;changed by inner&quot;
    
    inner()
    print(&quot;outer x:&quot;, x)  # Output: outer x: outer x

outer()
print(&quot;global x:&quot;, x)  # Output: global x: changed by inner
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt;To modify variables in an &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;enclosing (but non-global) scope (外部嵌套作用域)&amp;lt;/span&amp;gt;, use the &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;nonlocal&amp;lt;/code&amp;gt; keyword instead, which we&apos;ll cover in a separate note.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;8. Mutable Objects: A Special Case&lt;/h2&gt;
&lt;p&gt;For &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;mutable objects (可变对象)&amp;lt;/span&amp;gt; like lists and dictionaries, you can modify their &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;contents&amp;lt;/span&amp;gt; without using &lt;code&gt;global&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;my_list = [1, 2, 3]
my_dict = {&quot;count&quot;: 0}

def modify_mutable():
    # No &apos;global&apos; needed - we&apos;re modifying, not reassigning
    my_list.append(4)
    my_dict[&quot;count&quot;] += 1
    print(&quot;Inside function:&quot;, my_list, my_dict)

modify_mutable()
print(&quot;Outside function:&quot;, my_list, my_dict)
# Output: Outside function: [1, 2, 3, 4] {&apos;count&apos;: 1}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Pitfall: &amp;lt;/span&amp;gt;This works because we&apos;re &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;modifying (修改)&amp;lt;/span&amp;gt; the object, not &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;reassigning (重新赋值)&amp;lt;/span&amp;gt; the variable. If we tried &lt;code&gt;my_list = [4, 5, 6]&lt;/code&gt;, that would require &lt;code&gt;global&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;global&amp;lt;/code&amp;gt; only when you must modify a module-level variable from inside a function; otherwise, pass values as parameters and return results for cleaner, more maintainable code.&amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Python re</title><link>https://lxy-alexander.github.io/blog/posts/python/python-re/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/python/python-re/</guid><description>Python re</description><pubDate>Mon, 09 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;&lt;strong&gt;I. Python &lt;code&gt;re&lt;/code&gt; — Regular Expressions&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt;
A &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Regular Expression (正则表达式)&amp;lt;/span&amp;gt; is a &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;pattern (模式)&amp;lt;/span&amp;gt; used to find and manipulate text. Think of it as a &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;&quot;super-powered search&quot;&amp;lt;/span&amp;gt; that can match patterns, not just exact words. Python&apos;s &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;re&amp;lt;/code&amp;gt; module gives you tools to use regex.
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;**II. Pattern Syntax — The Complete Reference **&lt;/h1&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Every regex pattern is built from three kinds of building blocks: &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Literals (字面量)&amp;lt;/span&amp;gt; that match themselves, &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Metacharacters (元字符)&amp;lt;/span&amp;gt; that have special meaning, and &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Quantifiers (量词)&amp;lt;/span&amp;gt; that control repetition. Learn these 30-odd symbols and you can write any pattern. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;1. Literals and Metacharacters (字面量与元字符)&lt;/h2&gt;
&lt;h3&gt;1) Plain literals (普通字面量)&lt;/h3&gt;
&lt;p&gt;Most characters match themselves exactly.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import re

# Literal match — &apos;cat&apos; matches exactly the string &quot;cat&quot;
print(re.search(r&apos;cat&apos;, &apos;I have a cat&apos;))        # match
print(re.search(r&apos;cat&apos;, &apos;I have a CAT&apos;))        # None  (case-sensitive by default)
print(re.search(r&apos;cat&apos;, &apos;concatenate&apos;))         # match (found inside)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) The 14 metacharacters (14个元字符)&lt;/h3&gt;
&lt;p&gt;These characters have special meaning and must be &lt;strong&gt;escaped&lt;/strong&gt; with &lt;code&gt;\&lt;/code&gt; to match literally:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;. ^ $ * + ? { } [ ] \ | ( )
import re

# Matching a literal dot — must escape it
text = &quot;price: $3.99&quot;

print(re.search(r&apos;3.99&apos;,  text))    # matches &quot;3.99&quot; BUT also &quot;3X99&quot; (dot = any char!)
print(re.search(r&apos;3\.99&apos;, text))    # matches ONLY &quot;3.99&quot;  ← correct

# Matching a literal backslash
print(re.search(r&apos;C:\\Users&apos;, r&apos;C:\Users&apos;))   # matches C:\Users
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Always use raw strings &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;r&apos;pattern&apos;&amp;lt;/code&amp;gt; for regex patterns.&amp;lt;/span&amp;gt; Without &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;r&amp;lt;/code&amp;gt;, Python processes &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;\n&amp;lt;/code&amp;gt; as newline before the regex engine sees it. With &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;r&apos;\n&apos;&amp;lt;/code&amp;gt;, the regex engine receives the literal two characters &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;\n&amp;lt;/code&amp;gt; and interprets them as &quot;newline character&quot;.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. The Dot &lt;code&gt;.&lt;/code&gt; — Any character (任意字符)&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;.&lt;/code&gt; matches &lt;strong&gt;any single character&lt;/strong&gt; except a newline &lt;code&gt;\n&lt;/code&gt; (unless &lt;code&gt;re.DOTALL&lt;/code&gt; flag is set).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import re

# . matches exactly ONE character (any except \n)
print(re.findall(r&apos;c.t&apos;, &apos;cat cut c t c\nt coot&apos;))
# → [&apos;cat&apos;, &apos;cut&apos;, &apos;c t&apos;]   (&apos;c\nt&apos; skipped — \n not matched by dot)
# Note: &apos;coot&apos; not matched — dot matches exactly 1 char

# With re.DOTALL, dot matches newline too
text = &quot;first\nsecond&quot;
print(re.search(r&apos;first.second&apos;,  text))             # None
print(re.search(r&apos;first.second&apos;,  text, re.DOTALL))  # match
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;3. Anchors — Position matchers (锚点 — 位置匹配)&lt;/h2&gt;
&lt;p&gt;Anchors match &lt;strong&gt;positions&lt;/strong&gt;, not characters.&lt;/p&gt;
&lt;h3&gt;1) &lt;code&gt;^&lt;/code&gt; and &lt;code&gt;$&lt;/code&gt; — Start and end of string/line&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import re

text = &quot;hello world&quot;

print(re.search(r&apos;^hello&apos;, text))   # match  — &apos;hello&apos; is at start
print(re.search(r&apos;^world&apos;, text))   # None   — &apos;world&apos; is NOT at start
print(re.search(r&apos;world$&apos;, text))   # match  — &apos;world&apos; is at end
print(re.search(r&apos;hello$&apos;, text))   # None

# With re.MULTILINE: ^ and $ match start/end of EACH LINE
multiline = &quot;line1\nline2\nline3&quot;
print(re.findall(r&apos;^\w+&apos;, multiline, re.MULTILINE))
# → [&apos;line1&apos;, &apos;line2&apos;, &apos;line3&apos;]

print(re.findall(r&apos;\w+$&apos;, multiline, re.MULTILINE))
# → [&apos;line1&apos;, &apos;line2&apos;, &apos;line3&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) &lt;code&gt;\b&lt;/code&gt; and &lt;code&gt;\B&lt;/code&gt; — Word boundaries (单词边界)&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;\b&lt;/code&gt; matches the &lt;strong&gt;boundary between a word character and a non-word character&lt;/strong&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import re

# \b matches word boundary — prevents partial matches
print(re.findall(r&apos;\bcat\b&apos;, &apos;cat cats concatenate scatter&apos;))
# → [&apos;cat&apos;]   (only the standalone word)

print(re.findall(r&apos;cat&apos;,     &apos;cat cats concatenate scatter&apos;))
# → [&apos;cat&apos;, &apos;cat&apos;, &apos;cat&apos;, &apos;cat&apos;]  (too many!)

# \B matches NON-word boundary (inside a word)
print(re.findall(r&apos;\Bcat\B&apos;, &apos;cat cats concatenate&apos;))
# → [&apos;cat&apos;]   (only the &apos;cat&apos; inside &apos;concatenate&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) &lt;code&gt;\A&lt;/code&gt;, &lt;code&gt;\Z&lt;/code&gt; — Absolute start/end of string (字符串绝对首尾)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import re

# \A and \Z are NOT affected by re.MULTILINE — always match string start/end
text = &quot;line1\nline2&quot;

print(re.search(r&apos;\Aline1&apos;, text))   # match — absolute start
print(re.search(r&apos;\Aline2&apos;, text))   # None  — line2 is NOT at absolute start
print(re.search(r&apos;line2\Z&apos;, text))   # match — absolute end
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;4. Character Classes &lt;code&gt;[ ]&lt;/code&gt; (字符类)&lt;/h2&gt;
&lt;h3&gt;1) Basic character class (基本字符类)&lt;/h3&gt;
&lt;p&gt;A character class matches &lt;strong&gt;one character&lt;/strong&gt; that is any of the listed characters.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import re

# [aeiou] matches any single vowel
print(re.findall(r&apos;[aeiou]&apos;, &apos;hello world&apos;))
# → [&apos;e&apos;, &apos;o&apos;, &apos;o&apos;]

# [a-z] matches any lowercase letter (range syntax)
print(re.findall(r&apos;[a-z]+&apos;, &apos;Hello World 123&apos;))
# → [&apos;ello&apos;, &apos;orld&apos;]

# [A-Za-z0-9] matches any alphanumeric
print(re.findall(r&apos;[A-Za-z0-9]+&apos;, &apos;foo_bar-123!&apos;))
# → [&apos;foo&apos;, &apos;bar&apos;, &apos;123&apos;]

# [0-9] is equivalent to \d
print(re.findall(r&apos;[0-9]+&apos;, &apos;abc 123 def 456&apos;))
# → [&apos;123&apos;, &apos;456&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) Negated character class &lt;code&gt;[^ ]&lt;/code&gt; (否定字符类)&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;[^...]&lt;/code&gt; matches any character &lt;strong&gt;NOT&lt;/strong&gt; in the class.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import re

# [^aeiou] matches any consonant (non-vowel)
print(re.findall(r&apos;[^aeiou\s]+&apos;, &apos;hello world&apos;))
# → [&apos;h&apos;, &apos;ll&apos;, &apos;w&apos;, &apos;rld&apos;]

# [^0-9] matches any non-digit character
print(re.findall(r&apos;[^0-9]+&apos;, &apos;abc123def456&apos;))
# → [&apos;abc&apos;, &apos;def&apos;]

# Strip all non-alphanumeric characters
cleaned = re.sub(r&apos;[^A-Za-z0-9]&apos;, &apos;&apos;, &apos;Hello, World! 123&apos;)
print(cleaned)   # → HelloWorld123
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) Special sequences inside &lt;code&gt;[ ]&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import re

# Inside [], most metacharacters lose special meaning
# - (dash) is literal if first, last, or escaped
print(re.findall(r&apos;[-+*/]&apos;, &apos;3+4-2*1/5&apos;))  # → [&apos;+&apos;, &apos;-&apos;, &apos;*&apos;, &apos;/&apos;]

# ^ is literal unless it is the FIRST character
print(re.findall(r&apos;[a^b]&apos;, &apos;a^b c&apos;))       # → [&apos;a&apos;, &apos;^&apos;, &apos;b&apos;]  (literal ^)

# ] must be escaped or placed first
print(re.findall(r&apos;[]a-z]&apos;, &apos;a]b&apos;))        # → [&apos;a&apos;, &apos;]&apos;, &apos;b&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;5. Predefined Character Classes (预定义字符类)&lt;/h2&gt;
&lt;p&gt;These are shorthand for common character sets:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Shorthand&lt;/th&gt;
&lt;th&gt;Equivalent&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;\d&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[0-9]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Any digit (数字)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;\D&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[^0-9]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Any non-digit (非数字)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;\w&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[A-Za-z0-9_]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Word character (单词字符)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;\W&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[^A-Za-z0-9_]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Non-word character (非单词字符)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;\s&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[ \t\n\r\f\v]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Whitespace (空白字符)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;\S&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[^ \t\n\r\f\v]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Non-whitespace (非空白字符)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre&gt;&lt;code&gt;import re

text = &quot;Hello, World! 42 items at $3.99 each.\n&quot;

print(re.findall(r&apos;\d+&apos;,  text))  # → [&apos;42&apos;, &apos;3&apos;, &apos;99&apos;]
print(re.findall(r&apos;\w+&apos;,  text))  # → [&apos;Hello&apos;, &apos;World&apos;, &apos;42&apos;, &apos;items&apos;, &apos;at&apos;, &apos;3&apos;, &apos;99&apos;, &apos;each&apos;]
print(re.findall(r&apos;\s+&apos;,  text))  # → [&apos; &apos;, &apos; &apos;, &apos; &apos;, &apos; &apos;, &apos; &apos;, &apos;\n&apos;]
print(re.findall(r&apos;\W+&apos;,  text))  # → [&apos;, &apos;, &apos;! &apos;, &apos; &apos;, &apos; $&apos;, &apos;.&apos;, &apos;\n&apos;]

# Combining: \w+ matches whole words
print(re.findall(r&apos;\b\w{5}\b&apos;, text))  # words of exactly 5 chars
# → [&apos;Hello&apos;, &apos;World&apos;, &apos;items&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;6. Quantifiers — Repetition (量词 — 重复)&lt;/h2&gt;
&lt;h3&gt;1) Basic quantifiers (基本量词)&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Quantifier&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;*&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;0 or more (零次或多次)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;+&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;1 or more (一次或多次)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;?&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;0 or 1 (零次或一次，可选)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;{n}&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Exactly n times (恰好n次)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;{n,}&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;n or more times (n次或更多)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;{n,m}&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Between n and m times (n到m次)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre&gt;&lt;code&gt;import re

s = &quot;colour   color   colouur&quot;

print(re.findall(r&apos;colou?r&apos;,    s))   # ? → u is optional
# → [&apos;colour&apos;, &apos;color&apos;]

print(re.findall(r&apos;colou*r&apos;,    s))   # * → 0 or more u&apos;s
# → [&apos;colour&apos;, &apos;color&apos;, &apos;colouur&apos;]

print(re.findall(r&apos;colou+r&apos;,    s))   # + → 1 or more u&apos;s
# → [&apos;colour&apos;, &apos;colouur&apos;]

print(re.findall(r&apos;colou{2}r&apos;,  s))   # exactly 2 u&apos;s
# → [&apos;colouur&apos;]

print(re.findall(r&apos;colou{1,2}r&apos;,s))   # 1 or 2 u&apos;s
# → [&apos;colour&apos;, &apos;colouur&apos;]

# Phone number: exactly 10 digits
print(re.findall(r&apos;\d{10}&apos;, &apos;1234567890 12345&apos;))
# → [&apos;1234567890&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) Greedy vs Non-greedy (贪婪 vs 非贪婪)&lt;/h3&gt;
&lt;p&gt;By default, quantifiers are &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;greedy (贪婪)&amp;lt;/span&amp;gt; — they match &lt;strong&gt;as much as possible&lt;/strong&gt;. Adding &lt;code&gt;?&lt;/code&gt; makes them &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;non-greedy (非贪婪/懒惰)&amp;lt;/span&amp;gt; — they match &lt;strong&gt;as little as possible&lt;/strong&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import re

html = &quot;&amp;lt;b&amp;gt;bold&amp;lt;/b&amp;gt; and &amp;lt;i&amp;gt;italic&amp;lt;/i&amp;gt;&quot;

# Greedy: .* expands as far right as possible
print(re.findall(r&apos;&amp;lt;.*&amp;gt;&apos;,  html))
# → [&apos;&amp;lt;b&amp;gt;bold&amp;lt;/b&amp;gt; and &amp;lt;i&amp;gt;italic&amp;lt;/i&amp;gt;&apos;]   ← one huge match (too greedy)

# Non-greedy: .*? stops at the FIRST &amp;gt;
print(re.findall(r&apos;&amp;lt;.*?&amp;gt;&apos;,  html))
# → [&apos;&amp;lt;b&amp;gt;&apos;, &apos;&amp;lt;/b&amp;gt;&apos;, &apos;&amp;lt;i&amp;gt;&apos;, &apos;&amp;lt;/i&amp;gt;&apos;]        ← each tag separately

# Extracting content between tags
print(re.findall(r&apos;&amp;lt;b&amp;gt;(.*?)&amp;lt;/b&amp;gt;&apos;, html))
# → [&apos;bold&apos;]

# More examples
text = &apos;&quot;first&quot; and &quot;second&quot;&apos;
print(re.findall(r&apos;&quot;.*&quot;&apos;,  text))   # → [&apos;&quot;first&quot; and &quot;second&quot;&apos;]  greedy
print(re.findall(r&apos;&quot;.*?&quot;&apos;, text))   # → [&apos;&quot;first&quot;&apos;, &apos;&quot;second&quot;&apos;]   non-greedy
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Matches&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;.*&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Greedy&lt;/td&gt;
&lt;td&gt;As many chars as possible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;.*?&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Non-greedy&lt;/td&gt;
&lt;td&gt;As few chars as possible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;.+&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Greedy&lt;/td&gt;
&lt;td&gt;1+ chars, maximum&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;.+?&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Non-greedy&lt;/td&gt;
&lt;td&gt;1+ chars, minimum&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;7. Groups — Capturing and Non-capturing (分组 — 捕获与非捕获)&lt;/h2&gt;
&lt;h3&gt;1) Capturing group &lt;code&gt;( )&lt;/code&gt; (捕获组)&lt;/h3&gt;
&lt;p&gt;Groups serve two purposes: &lt;strong&gt;grouping&lt;/strong&gt; for quantifiers, and &lt;strong&gt;capturing&lt;/strong&gt; the matched text.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import re

# Grouping: (ab)+ repeats the whole &quot;ab&quot;
print(re.findall(r&apos;(ab)+&apos;, &apos;ab abab ababab&apos;))
# → [&apos;ab&apos;, &apos;ab&apos;, &apos;ab&apos;]  (returns last captured group)

# Capturing: extract the content inside ()
dates = &quot;2024-01-15, 2023-12-31, 2025-06-01&quot;
print(re.findall(r&apos;(\d{4})-(\d{2})-(\d{2})&apos;, dates))
# → [(&apos;2024&apos;, &apos;01&apos;, &apos;15&apos;), (&apos;2023&apos;, &apos;12&apos;, &apos;31&apos;), (&apos;2025&apos;, &apos;06&apos;, &apos;01&apos;)]
#   ↑ each match returns a tuple of all captured groups

# .group() on a Match object
m = re.search(r&apos;(\d{4})-(\d{2})-(\d{2})&apos;, &apos;2024-01-15&apos;)
print(m.group(0))   # → 2024-01-15  (entire match)
print(m.group(1))   # → 2024        (group 1)
print(m.group(2))   # → 01          (group 2)
print(m.group(3))   # → 15          (group 3)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) Named group &lt;code&gt;(?P&amp;lt;name&amp;gt;...)&lt;/code&gt; (命名捕获组)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import re

# Named groups — access by name instead of index
pattern = r&apos;(?P&amp;lt;year&amp;gt;\d{4})-(?P&amp;lt;month&amp;gt;\d{2})-(?P&amp;lt;day&amp;gt;\d{2})&apos;
m = re.search(pattern, &apos;2024-01-15&apos;)

print(m.group(&apos;year&apos;))    # → 2024
print(m.group(&apos;month&apos;))   # → 01
print(m.group(&apos;day&apos;))     # → 15
print(m.groupdict())      # → {&apos;year&apos;: &apos;2024&apos;, &apos;month&apos;: &apos;01&apos;, &apos;day&apos;: &apos;15&apos;}

# Named groups in re.sub — backreference by name
result = re.sub(
    r&apos;(?P&amp;lt;year&amp;gt;\d{4})-(?P&amp;lt;month&amp;gt;\d{2})-(?P&amp;lt;day&amp;gt;\d{2})&apos;,
    r&apos;\g&amp;lt;day&amp;gt;/\g&amp;lt;month&amp;gt;/\g&amp;lt;year&amp;gt;&apos;,    # reorder: DD/MM/YYYY
    &apos;2024-01-15&apos;
)
print(result)   # → 15/01/2024
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) Non-capturing group &lt;code&gt;(?:...)&lt;/code&gt; (非捕获组)&lt;/h3&gt;
&lt;p&gt;When you need grouping for quantifiers but &lt;strong&gt;don&apos;t&lt;/strong&gt; want the group in your results:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import re

# Without (?:...) — capturing group pollutes findall results
print(re.findall(r&apos;(\d+)(?:px|em|rem)&apos;, &apos;12px 3em 100rem&apos;))
# → [&apos;12&apos;, &apos;3&apos;, &apos;100&apos;]   ← only numbers, units NOT captured ✅

# With capturing group — units would also appear
print(re.findall(r&apos;(\d+)(px|em|rem)&apos;, &apos;12px 3em 100rem&apos;))
# → [(&apos;12&apos;, &apos;px&apos;), (&apos;3&apos;, &apos;em&apos;), (&apos;100&apos;, &apos;rem&apos;)]  ← units captured too

# (?:...) for grouping quantifiers
print(re.findall(r&apos;(?:ha)+&apos;, &apos;hahaha haha ha h&apos;))
# → [&apos;hahaha&apos;, &apos;haha&apos;, &apos;ha&apos;]   (group &apos;ha&apos; as a unit for +)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4) Backreferences &lt;code&gt;\1&lt;/code&gt; &lt;code&gt;\2&lt;/code&gt; (反向引用)&lt;/h3&gt;
&lt;p&gt;Refer to a previously captured group &lt;strong&gt;within the same pattern&lt;/strong&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import re

# Find repeated words
text = &quot;the the quick brown fox fox jumps&quot;
print(re.findall(r&apos;\b(\w+)\s+\1\b&apos;, text))
# → [&apos;the&apos;, &apos;fox&apos;]   (\1 refers back to group 1)

# Find doubled characters
print(re.findall(r&apos;(.)\1&apos;, &apos;aabcddee&apos;))
# → [&apos;a&apos;, &apos;d&apos;, &apos;e&apos;]

# HTML tag matching: opening and closing tags must match
html = &quot;&amp;lt;h1&amp;gt;Title&amp;lt;/h1&amp;gt; &amp;lt;h2&amp;gt;Subtitle&amp;lt;/h2&amp;gt;&quot;
print(re.findall(r&apos;&amp;lt;(\w+)&amp;gt;(.*?)&amp;lt;/\1&amp;gt;&apos;, html))
# → [(&apos;h1&apos;, &apos;Title&apos;), (&apos;h2&apos;, &apos;Subtitle&apos;)]
#   \1 ensures the closing tag matches the opening tag
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;8. Lookahead and Lookbehind — Zero-width assertions (零宽断言)&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Lookaround (环视断言)&amp;lt;/span&amp;gt; matches a position based on what is around it, &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;without consuming characters (不消耗字符)&amp;lt;/span&amp;gt;. They are &quot;zero-width&quot; — the match position doesn&apos;t advance. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;1) Positive lookahead &lt;code&gt;(?=...)&lt;/code&gt; (正向先行断言)&lt;/h3&gt;
&lt;p&gt;&quot;Match X only if followed by Y&quot; — Y is NOT included in the match.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import re

# Match a number only if followed by &quot;px&quot;
print(re.findall(r&apos;\d+(?=px)&apos;, &apos;12px 3em 100px 5rem&apos;))
# → [&apos;12&apos;, &apos;100&apos;]   (px NOT included in results)

# Match word only if followed by a colon
text = &quot;name: Alice age: 30 city: NYC&quot;
print(re.findall(r&apos;\w+(?=:)&apos;, text))
# → [&apos;name&apos;, &apos;age&apos;, &apos;city&apos;]

# Password validation: must contain a digit
import re
def has_digit(pw): return bool(re.search(r&apos;(?=.*\d)&apos;, pw))
print(has_digit(&quot;abc123&quot;))   # → True
print(has_digit(&quot;abcdef&quot;))   # → False
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) Negative lookahead &lt;code&gt;(?!...)&lt;/code&gt; (负向先行断言)&lt;/h3&gt;
&lt;p&gt;&quot;Match X only if NOT followed by Y&quot;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import re

# Match a number only if NOT followed by &quot;px&quot;
print(re.findall(r&apos;\d+(?!px)\b&apos;, &apos;12px 3em 100px 5rem&apos;))
# → [&apos;3&apos;, &apos;5&apos;]

# Match &apos;foo&apos; not followed by &apos;bar&apos;
print(re.findall(r&apos;foo(?!bar)&apos;, &apos;foobar foobaz foo&apos;))
# → [&apos;foo&apos;, &apos;foo&apos;]   (&apos;foobar&apos; excluded, &apos;foobaz&apos; and &apos;foo&apos; included)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) Positive lookbehind &lt;code&gt;(?&amp;lt;=...)&lt;/code&gt; (正向后行断言)&lt;/h3&gt;
&lt;p&gt;&quot;Match X only if preceded by Y&quot; — Y is NOT included in the match.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import re

# Match digits only if preceded by &apos;$&apos;
prices = &quot;items: $10, €20, £30, $50&quot;
print(re.findall(r&apos;(?&amp;lt;=\$)\d+&apos;, prices))
# → [&apos;10&apos;, &apos;50&apos;]   ($ NOT included in results)

# Match word after a colon and space
text = &quot;name: Alice, city: NYC, age: 30&quot;
print(re.findall(r&apos;(?&amp;lt;=: )\w+&apos;, text))
# → [&apos;Alice&apos;, &apos;NYC&apos;, &apos;30&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4) Negative lookbehind &lt;code&gt;(?&amp;lt;!...)&lt;/code&gt; (负向后行断言)&lt;/h3&gt;
&lt;p&gt;&quot;Match X only if NOT preceded by Y&quot;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import re

# Match digits NOT preceded by &apos;$&apos;
prices = &quot;items: $10, 20, $50, 100&quot;
print(re.findall(r&apos;(?&amp;lt;!\$)\b\d+\b&apos;, prices))
# → [&apos;20&apos;, &apos;100&apos;]

# Match &apos;ing&apos; not preceded by &apos;run&apos;
words = &quot;running swimming singing&quot;
print(re.findall(r&apos;(?&amp;lt;!run)ning\b&apos;, words))
# → [&apos;ning&apos;, &apos;ning&apos;]   (swim→ming yes, sing→ning yes, runNING excluded)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5) Lookaround summary table (环视断言总结)&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Syntax&lt;/th&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;(?=Y)&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Positive lookahead (正向先行)&lt;/td&gt;
&lt;td&gt;Followed by Y&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;(?!Y)&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Negative lookahead (负向先行)&lt;/td&gt;
&lt;td&gt;NOT followed by Y&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;(?&amp;lt;=Y)&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Positive lookbehind (正向后行)&lt;/td&gt;
&lt;td&gt;Preceded by Y&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;(?&amp;lt;!Y)&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Negative lookbehind (负向后行)&lt;/td&gt;
&lt;td&gt;NOT preceded by Y&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;9. Alternation &lt;code&gt;|&lt;/code&gt; — OR operator (或运算符)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import re

# | matches either the left or right pattern
print(re.findall(r&apos;cat|dog|fish&apos;, &apos;I have a cat and a dog&apos;))
# → [&apos;cat&apos;, &apos;dog&apos;]

# With groups: (cat|dog) scopes the alternation
print(re.findall(r&apos;(cat|dog)s?&apos;, &apos;cats dogs cat dog&apos;))
# → [&apos;cat&apos;, &apos;dog&apos;, &apos;cat&apos;, &apos;dog&apos;]

# Alternation of longer patterns
log = &quot;ERROR: disk full  WARNING: low memory  INFO: started&quot;
print(re.findall(r&apos;ERROR|WARNING|INFO&apos;, log))
# → [&apos;ERROR&apos;, &apos;WARNING&apos;, &apos;INFO&apos;]

# Order matters: first match wins
print(re.search(r&apos;cat|catch&apos;, &apos;I catch cats&apos;))   # matches &apos;cat&apos; (not &apos;catch&apos;!)
print(re.search(r&apos;catch|cat&apos;, &apos;I catch cats&apos;))   # matches &apos;catch&apos; ← correct order
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;10. Flags — Modifying match behavior (标志位)&lt;/h2&gt;
&lt;h3&gt;1) All flags (所有标志位)&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Flag (short)&lt;/th&gt;
&lt;th&gt;Flag (long)&lt;/th&gt;
&lt;th&gt;Effect&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;re.I&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;re.IGNORECASE&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Case-insensitive matching (忽略大小写)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;re.M&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;re.MULTILINE&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;^&lt;/code&gt;/&lt;code&gt;$&lt;/code&gt; match each line (多行模式)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;re.S&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;re.DOTALL&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.&lt;/code&gt; matches &lt;code&gt;\n&lt;/code&gt; too (点号匹配换行)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;re.X&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;re.VERBOSE&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Allow whitespace/comments in pattern (详细模式)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;re.A&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;re.ASCII&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;\w \d \s&lt;/code&gt; match ASCII only (ASCII模式)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;re.L&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;re.LOCALE&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Locale-dependent matching (本地化模式)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;2) &lt;code&gt;re.IGNORECASE&lt;/code&gt; (re.I)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import re

print(re.findall(r&apos;hello&apos;, &apos;Hello HELLO hello&apos;, re.I))
# → [&apos;Hello&apos;, &apos;HELLO&apos;, &apos;hello&apos;]

# Case-insensitive word boundary
print(re.findall(r&apos;\bpython\b&apos;, &apos;Python PYTHON python&apos;, re.IGNORECASE))
# → [&apos;Python&apos;, &apos;PYTHON&apos;, &apos;python&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) &lt;code&gt;re.MULTILINE&lt;/code&gt; (re.M)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import re

log = &quot;&quot;&quot;ERROR: disk full
WARNING: low memory
ERROR: timeout
INFO: done&quot;&quot;&quot;

# Without re.M: ^ only matches start of entire string
print(re.findall(r&apos;^ERROR.*&apos;,  log))
# → [&apos;ERROR: disk full&apos;]

# With re.M: ^ matches start of EACH line
print(re.findall(r&apos;^ERROR.*&apos;,  log, re.M))
# → [&apos;ERROR: disk full&apos;, &apos;ERROR: timeout&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4) &lt;code&gt;re.DOTALL&lt;/code&gt; (re.S)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import re

html = &quot;&amp;lt;div&amp;gt;\n  &amp;lt;p&amp;gt;Hello&amp;lt;/p&amp;gt;\n&amp;lt;/div&amp;gt;&quot;

# Without re.S: . does not match \n
print(re.search(r&apos;&amp;lt;div&amp;gt;.*&amp;lt;/div&amp;gt;&apos;,  html))          # None

# With re.S: . matches everything including \n
print(re.search(r&apos;&amp;lt;div&amp;gt;.*&amp;lt;/div&amp;gt;&apos;,  html, re.S))    # match
print(re.search(r&apos;&amp;lt;div&amp;gt;.*?&amp;lt;/div&amp;gt;&apos;, html, re.S).group())
# → &amp;lt;div&amp;gt;\n  &amp;lt;p&amp;gt;Hello&amp;lt;/p&amp;gt;\n&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5) &lt;code&gt;re.VERBOSE&lt;/code&gt; (re.X) — Readable complex patterns (可读的复杂模式)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import re

# Without re.X — hard to read
email_pattern_compact = r&apos;^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$&apos;

# With re.X — add whitespace and comments freely
email_pattern_verbose = re.compile(r&apos;&apos;&apos;
    ^                       # start of string
    [a-zA-Z0-9._%+-]+       # local part (user name)
    @                       # @ symbol
    [a-zA-Z0-9.-]+          # domain name
    \.                      # literal dot
    [a-zA-Z]{2,}            # top-level domain (2+ letters)
    $                       # end of string
&apos;&apos;&apos;, re.VERBOSE)

print(email_pattern_verbose.match(&apos;user@example.com&apos;))   # match
print(email_pattern_verbose.match(&apos;bad@&apos;))               # None
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6) Combining flags (组合标志位)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import re

# Combine with | (bitwise OR)
text = &quot;Hello\nWorld&quot;
print(re.findall(r&apos;^.+$&apos;, text, re.M | re.I))
# re.M → ^ and $ per line
# re.I → case-insensitive
# → [&apos;Hello&apos;, &apos;World&apos;]

# Inline flags in the pattern (?flags) — scoped to pattern
print(re.findall(r&apos;(?i)hello&apos;, &apos;Hello HELLO hello&apos;))
# → [&apos;Hello&apos;, &apos;HELLO&apos;, &apos;hello&apos;]

# Inline flags for part of pattern
print(re.findall(r&apos;(?i:hello) world&apos;, &apos;HELLO world hello World&apos;))
# → [&apos;HELLO world&apos;]   (only &apos;hello&apos; is case-insensitive, &apos;world&apos; is not)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h1&gt;&lt;strong&gt;III. The &lt;code&gt;re&lt;/code&gt; Module API — Complete Reference&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; The &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;re&amp;lt;/code&amp;gt; module has two usage modes: &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;① module-level functions&amp;lt;/span&amp;gt; like &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;re.search()&amp;lt;/code&amp;gt; (convenient for one-off use) and &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;② compiled Pattern objects&amp;lt;/span&amp;gt; via &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;re.compile()&amp;lt;/code&amp;gt; (preferred when the same pattern is used repeatedly — avoids recompilation overhead). &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;1. &lt;code&gt;re.compile()&lt;/code&gt; — Pre-compile a pattern (预编译模式)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import re

# compile() returns a Pattern object
pattern = re.compile(r&apos;\d{4}-\d{2}-\d{2}&apos;, re.IGNORECASE)

# Call methods on the Pattern object (same names as module-level functions)
print(pattern.search(&apos;date: 2024-01-15&apos;))
print(pattern.findall(&apos;from 2024-01-01 to 2024-12-31&apos;))
# → [&apos;2024-01-01&apos;, &apos;2024-12-31&apos;]

# Pattern attributes
print(pattern.pattern)    # → \d{4}-\d{2}-\d{2}
print(pattern.flags)      # → 34  (2 = default + 32 = IGNORECASE)
print(pattern.groups)     # → 0   (no capturing groups)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Module-level functions like &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;re.search(pattern, string)&amp;lt;/code&amp;gt; use an internal cache of the last 512 compiled patterns. For hot loops, prefer explicit &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;re.compile()&amp;lt;/code&amp;gt; to guarantee no cache misses and to make intent clear.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. &lt;code&gt;re.search()&lt;/code&gt; — Find first match anywhere (查找第一个匹配)&lt;/h2&gt;
&lt;p&gt;Returns a &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Match object&amp;lt;/span&amp;gt; if found anywhere in the string, or &lt;code&gt;None&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import re

text = &quot;The price is $42.99 for 3 items&quot;

m = re.search(r&apos;\$(\d+\.\d{2})&apos;, text)
if m:
    print(m.group())    # → $42.99   (full match)
    print(m.group(1))   # → 42.99    (group 1 — no $)
    print(m.start())    # → 13       (start index)
    print(m.end())      # → 19       (end index)
    print(m.span())     # → (13, 19) (start, end)
    print(m.string)     # → &quot;The price is $42.99 for 3 items&quot;  (original)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;3. &lt;code&gt;re.match()&lt;/code&gt; — Match at string start (从字符串开头匹配)&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Warning: &lt;code&gt;re.match()&lt;/code&gt; only matches at the BEGINNING of the string — NOT the same as &lt;code&gt;re.search()&lt;/code&gt;!&amp;lt;/span&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import re

# match() — only succeeds if pattern starts at position 0
print(re.match(r&apos;\d+&apos;, &apos;123 abc&apos;))    # match   — starts at position 0
print(re.match(r&apos;\d+&apos;, &apos;abc 123&apos;))    # None    — &apos;abc&apos; is not \d+
print(re.search(r&apos;\d+&apos;, &apos;abc 123&apos;))   # match   — search finds it anywhere

# match() with ^ is redundant (both restrict to start)
print(re.match(r&apos;hello&apos;, &apos;hello world&apos;))    # match
print(re.match(r&apos;hello&apos;, &apos;say hello&apos;))      # None

# Practical: validate that a string is ENTIRELY a number
def is_integer(s):
    return bool(re.match(r&apos;^\d+$&apos;, s))

print(is_integer(&quot;12345&quot;))    # → True
print(is_integer(&quot;123a5&quot;))    # → False
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;4. &lt;code&gt;re.fullmatch()&lt;/code&gt; — Match entire string (匹配整个字符串)&lt;/h2&gt;
&lt;p&gt;Requires the pattern to match the &lt;strong&gt;complete&lt;/strong&gt; string from start to end.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import re

# fullmatch() equivalent to match() with ^ and $ anchors
print(re.fullmatch(r&apos;\d+&apos;, &apos;12345&apos;))     # match   — entire string is digits
print(re.fullmatch(r&apos;\d+&apos;, &apos;123abc&apos;))    # None    — not ALL digits
print(re.fullmatch(r&apos;\d+&apos;, &apos;  123  &apos;))   # None    — spaces don&apos;t match \d

# Validate formats completely
ip_pattern  = re.compile(r&apos;(\d{1,3}\.){3}\d{1,3}&apos;)
zip_pattern = re.compile(r&apos;\d{5}(-\d{4})?&apos;)
email_pat   = re.compile(r&apos;[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}&apos;)

tests = [&apos;192.168.1.1&apos;, &apos;12345&apos;, &apos;user@example.com&apos;, &apos;bad_input&apos;]
for t in tests:
    results = {
        &apos;ip&apos;:    bool(ip_pattern.fullmatch(t)),
        &apos;zip&apos;:   bool(zip_pattern.fullmatch(t)),
        &apos;email&apos;: bool(email_pat.fullmatch(t)),
    }
    print(f&quot;{t:&amp;lt;25} → {results}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;5. &lt;code&gt;re.findall()&lt;/code&gt; — Find all matches (查找所有匹配)&lt;/h2&gt;
&lt;p&gt;Returns a &lt;strong&gt;list&lt;/strong&gt; of all non-overlapping matches.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import re

text = &quot;2024-01-15, 2023-12-31, 2025-06-01&quot;

# No groups → returns list of strings
print(re.findall(r&apos;\d{4}-\d{2}-\d{2}&apos;, text))
# → [&apos;2024-01-15&apos;, &apos;2023-12-31&apos;, &apos;2025-06-01&apos;]

# One group → returns list of group contents
print(re.findall(r&apos;(\d{4})-\d{2}-\d{2}&apos;, text))
# → [&apos;2024&apos;, &apos;2023&apos;, &apos;2025&apos;]   (only the year group)

# Multiple groups → returns list of tuples
print(re.findall(r&apos;(\d{4})-(\d{2})-(\d{2})&apos;, text))
# → [(&apos;2024&apos;, &apos;01&apos;, &apos;15&apos;), (&apos;2023&apos;, &apos;12&apos;, &apos;31&apos;), (&apos;2025&apos;, &apos;06&apos;, &apos;01&apos;)]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;The return type of &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;findall()&amp;lt;/code&amp;gt; changes based on groups: no groups → &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;List[str]&amp;lt;/code&amp;gt;, one group → &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;List[str]&amp;lt;/code&amp;gt;, multiple groups → &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;List[tuple]&amp;lt;/code&amp;gt;. This is a common source of bugs.&amp;lt;/span&amp;gt; Use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;finditer()&amp;lt;/code&amp;gt; for consistent Match objects.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;6. &lt;code&gt;re.finditer()&lt;/code&gt; — Iterator of Match objects (匹配对象迭代器)&lt;/h2&gt;
&lt;p&gt;Returns an &lt;strong&gt;iterator&lt;/strong&gt; of Match objects. More powerful than &lt;code&gt;findall()&lt;/code&gt; because each Match has &lt;code&gt;.start()&lt;/code&gt;, &lt;code&gt;.end()&lt;/code&gt;, &lt;code&gt;.group()&lt;/code&gt;, etc.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import re

text = &quot;Alice scored 95, Bob scored 87, Carol scored 100&quot;

for m in re.finditer(r&apos;(\w+) scored (\d+)&apos;, text):
    name  = m.group(1)
    score = int(m.group(2))
    print(f&quot;{name}: {score} pts  | span={m.span()}&quot;)
# → Alice: 95 pts  | span=(0, 16)
# → Bob: 87 pts    | span=(18, 32)
# → Carol: 100 pts | span=(34, 49)

# Collect all spans for highlighting
positions = [(m.start(), m.end()) for m in re.finditer(r&apos;\d+&apos;, text)]
print(positions)   # → [(13, 15), (28, 30), (44, 47)]
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;7. &lt;code&gt;re.sub()&lt;/code&gt; — Substitute matches (替换匹配)&lt;/h2&gt;
&lt;h3&gt;1) Basic substitution (基本替换)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import re

text = &quot;Hello   World   Python&quot;

# Replace multiple spaces with single space
result = re.sub(r&apos;\s+&apos;, &apos; &apos;, text)
print(result)   # → Hello World Python

# count parameter: replace only first N occurrences
result = re.sub(r&apos;\s+&apos;, &apos; &apos;, text, count=1)
print(result)   # → Hello World   Python  (only first replaced)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) Backreferences in replacement (替换中的反向引用)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import re

# \1, \2 refer to captured groups in the replacement string
# Reformat date from YYYY-MM-DD to DD/MM/YYYY
dates = &quot;Born: 2024-01-15, Died: 2099-12-31&quot;
result = re.sub(r&apos;(\d{4})-(\d{2})-(\d{2})&apos;, r&apos;\3/\2/\1&apos;, dates)
print(result)   # → Born: 15/01/2024, Died: 31/12/2099

# Wrap all numbers in &amp;lt;b&amp;gt; tags
result = re.sub(r&apos;(\d+)&apos;, r&apos;&amp;lt;b&amp;gt;\1&amp;lt;/b&amp;gt;&apos;, &apos;I have 3 cats and 2 dogs&apos;)
print(result)   # → I have &amp;lt;b&amp;gt;3&amp;lt;/b&amp;gt; cats and &amp;lt;b&amp;gt;2&amp;lt;/b&amp;gt; dogs

# Named group backreference \g&amp;lt;name&amp;gt;
result = re.sub(
    r&apos;(?P&amp;lt;last&amp;gt;\w+), (?P&amp;lt;first&amp;gt;\w+)&apos;,
    r&apos;\g&amp;lt;first&amp;gt; \g&amp;lt;last&amp;gt;&apos;,
    &apos;Smith, John&apos;
)
print(result)   # → John Smith
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) Replacement function (替换函数)&lt;/h3&gt;
&lt;p&gt;Pass a &lt;strong&gt;callable&lt;/strong&gt; as the replacement — it receives the Match object and returns the replacement string.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import re

# Convert all numbers to their double
def double(m):
    return str(int(m.group()) * 2)

result = re.sub(r&apos;\d+&apos;, double, &apos;I have 3 cats and 10 dogs&apos;)
print(result)   # → I have 6 cats and 20 dogs

# Normalize different date formats to ISO 8601
def normalize_date(m):
    month_map = {&apos;Jan&apos;:1,&apos;Feb&apos;:2,&apos;Mar&apos;:3,&apos;Apr&apos;:4,&apos;May&apos;:5,&apos;Jun&apos;:6,
                 &apos;Jul&apos;:7,&apos;Aug&apos;:8,&apos;Sep&apos;:9,&apos;Oct&apos;:10,&apos;Nov&apos;:11,&apos;Dec&apos;:12}
    month = month_map.get(m.group(&apos;month_name&apos;),
                          int(m.group(&apos;month_num&apos;) or 0))
    day   = int(m.group(&apos;day&apos;))
    year  = int(m.group(&apos;year&apos;))
    return f&quot;{year:04d}-{month:02d}-{day:02d}&quot;

pattern = re.compile(r&apos;&apos;&apos;
    (?:(?P&amp;lt;month_name&amp;gt;Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)
       \s+(?P&amp;lt;day&amp;gt;\d{1,2}),\s+(?P&amp;lt;year&amp;gt;\d{4}))
    |
    (?:(?P&amp;lt;month_num&amp;gt;\d{1,2})/(?P&amp;lt;day2&amp;gt;\d{1,2})/(?P&amp;lt;year2&amp;gt;\d{4}))
&apos;&apos;&apos;, re.VERBOSE)

# Just demonstrate the function approach:
text = &quot;Meeting on Jan 15, 2024&quot;
result = re.sub(
    r&apos;(?P&amp;lt;month_name&amp;gt;Jan|Feb|Mar)\s+(?P&amp;lt;day&amp;gt;\d{1,2}),\s+(?P&amp;lt;year&amp;gt;\d{4})&apos;,
    normalize_date,
    text
)
print(result)   # → Meeting on 2024-01-15
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;8. &lt;code&gt;re.subn()&lt;/code&gt; — Substitute and count (替换并计数)&lt;/h2&gt;
&lt;p&gt;Like &lt;code&gt;re.sub()&lt;/code&gt; but returns a tuple &lt;code&gt;(new_string, count)&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import re

text = &quot;foo bar foo baz foo&quot;
result, n = re.subn(r&apos;foo&apos;, &apos;qux&apos;, text)
print(result)   # → qux bar qux baz qux
print(n)        # → 3  (number of substitutions made)

# Useful for detecting if any replacements occurred
text2 = &quot;no matches here&quot;
_, count = re.subn(r&apos;foo&apos;, &apos;qux&apos;, text2)
if count == 0:
    print(&quot;No substitutions made&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;9. &lt;code&gt;re.split()&lt;/code&gt; — Split by pattern (按模式分割)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import re

# Split on any non-alphanumeric sequence
text = &quot;one,two;;three   four\tfive&quot;
print(re.split(r&apos;[^a-zA-Z0-9]+&apos;, text))
# → [&apos;one&apos;, &apos;two&apos;, &apos;three&apos;, &apos;four&apos;, &apos;five&apos;]

# Split on commas with optional surrounding whitespace
csv = &quot;Alice , Bob,Carol ,  Dave&quot;
print(re.split(r&apos;\s*,\s*&apos;, csv))
# → [&apos;Alice&apos;, &apos;Bob&apos;, &apos;Carol&apos;, &apos;Dave&apos;]

# maxsplit: only split N times
print(re.split(r&apos;\s+&apos;, &apos;a b c d e&apos;, maxsplit=2))
# → [&apos;a&apos;, &apos;b&apos;, &apos;c d e&apos;]

# Capturing group: delimiters are INCLUDED in the result
text = &quot;one+two-three*four&quot;
print(re.split(r&apos;([+\-*])&apos;, text))
# → [&apos;one&apos;, &apos;+&apos;, &apos;two&apos;, &apos;-&apos;, &apos;three&apos;, &apos;*&apos;, &apos;four&apos;]  ← operators kept
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;10. &lt;code&gt;re.escape()&lt;/code&gt; — Escape special characters (转义特殊字符)&lt;/h2&gt;
&lt;p&gt;Escapes all non-alphanumeric characters so a raw string can be used as a literal pattern.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import re

# When user input is used as part of a pattern — MUST escape it
user_input = &quot;hello.world (test)&quot;
safe_pattern = re.escape(user_input)
print(safe_pattern)   # → hello\.world\ \(test\)

# Safe search
text = &quot;I said: hello.world (test) today&quot;
m = re.search(re.escape(user_input), text)
print(bool(m))   # → True

# Dangerous without escape:
print(re.search(user_input, text))   # . and () have special meaning!

# Common use: build a pattern from a list of keywords
keywords = [&apos;c++&apos;, &apos;c#&apos;, &apos;.net&apos;, &apos;node.js&apos;]
pattern  = &apos;|&apos;.join(re.escape(k) for k in keywords)
print(pattern)   # → c\+\+|c\#|\.net|node\.js

found = re.findall(pattern, &apos;I know c++ and .net and node.js&apos;, re.I)
print(found)   # → [&apos;c++&apos;, &apos;.net&apos;, &apos;node.js&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;11. Match Object — Complete API (匹配对象完整API)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import re

text = &quot;2024-01-15 is a Monday in New York&quot;
m    = re.search(r&apos;(?P&amp;lt;year&amp;gt;\d{4})-(?P&amp;lt;month&amp;gt;\d{2})-(?P&amp;lt;day&amp;gt;\d{2})&apos;, text)

# ── Accessing matched text ─────────────────────────────────
print(m.group())           # → 2024-01-15  (full match, same as group(0))
print(m.group(0))          # → 2024-01-15
print(m.group(1))          # → 2024         (group 1 by index)
print(m.group(2, 3))       # → (&apos;01&apos;, &apos;15&apos;) (multiple groups)
print(m.group(&apos;year&apos;))     # → 2024         (group by name)
print(m.groupdict())       # → {&apos;year&apos;: &apos;2024&apos;, &apos;month&apos;: &apos;01&apos;, &apos;day&apos;: &apos;15&apos;}
print(m.groups())          # → (&apos;2024&apos;, &apos;01&apos;, &apos;15&apos;)  (all groups as tuple)
print(m.groups(default=&apos;N/A&apos;))  # groups() with default for non-participating groups

# ── Position information ───────────────────────────────────
print(m.start())           # → 0    (start of full match)
print(m.end())             # → 10   (end of full match)
print(m.span())            # → (0, 10)
print(m.start(1))          # → 0    (start of group 1)
print(m.end(&apos;month&apos;))      # → 7    (end of named group)
print(m.span(&apos;day&apos;))       # → (8, 10)

# ── Context ────────────────────────────────────────────────
print(m.string)            # → full original string
print(m.re)                # → compiled pattern object
print(m.pos)               # → 0    (start position passed to search)
print(m.endpos)            # → 34   (end position passed to search)
print(m.lastindex)         # → 3    (index of last matched group)
print(m.lastgroup)         # → &apos;day&apos; (name of last matched group)

# ── Expand — backreferences in a template string ───────────
print(m.expand(r&apos;\g&amp;lt;day&amp;gt;/\g&amp;lt;month&amp;gt;/\g&amp;lt;year&amp;gt;&apos;))
# → 15/01/2024
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h1&gt;&lt;strong&gt;IV. Practical Patterns — Production-Ready Recipes (生产级常用模式)&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; This section provides &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;ready-to-use, battle-tested patterns&amp;lt;/span&amp;gt; for the most common real-world tasks. Each pattern is annotated and tested. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;1. Validation Patterns (验证模式)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import re

patterns = {

    # Email (simplified RFC 5321 compliant)
    &apos;email&apos;: re.compile(r&apos;&apos;&apos;
        ^[a-zA-Z0-9._%+\-]+     # local part
        @
        [a-zA-Z0-9.\-]+          # domain
        \.[a-zA-Z]{2,}$          # TLD (2+ chars)
    &apos;&apos;&apos;, re.VERBOSE),

    # Phone: +1 (555) 123-4567 / 555-123-4567 / 5551234567
    &apos;phone_us&apos;: re.compile(
        r&apos;^(\+1[-.\s]?)?&apos;
        r&apos;(\(?\d{3}\)?[-.\s]?)&apos;
        r&apos;\d{3}[-.\s]?\d{4}$&apos;
    ),

    # IPv4 address
    &apos;ipv4&apos;: re.compile(
        r&apos;^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}&apos;
        r&apos;(25[0-5]|2[0-4]\d|[01]?\d\d?)$&apos;
    ),

    # URL (http/https)
    &apos;url&apos;: re.compile(
        r&apos;^https?://&apos;
        r&apos;(([a-zA-Z0-9\-]+\.)+[a-zA-Z]{2,})&apos;
        r&apos;(:\d+)?&apos;
        r&apos;(/[^\s]*)?$&apos;
    ),

    # Date: YYYY-MM-DD
    &apos;date_iso&apos;: re.compile(
        r&apos;^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$&apos;
    ),

    # Strong password: 8+ chars, upper, lower, digit, special
    &apos;strong_password&apos;: re.compile(
        r&apos;^(?=.*[a-z])&apos;          # at least one lowercase
        r&apos;(?=.*[A-Z])&apos;           # at least one uppercase
        r&apos;(?=.*\d)&apos;              # at least one digit
        r&apos;(?=.*[!@#$%^&amp;amp;*])&apos;     # at least one special char
        r&apos;.{8,}$&apos;                # at least 8 chars total
    ),

    # Credit card (Visa/MC/Amex, with/without spaces)
    &apos;credit_card&apos;: re.compile(
        r&apos;^(?:4\d{12}(?:\d{3})?&apos;     # Visa
        r&apos;|5[1-5]\d{14}&apos;             # MasterCard
        r&apos;|3[47]\d{13})$&apos;            # Amex
    ),

    # ZIP code (US)
    &apos;zip_us&apos;: re.compile(r&apos;^\d{5}(-\d{4})?$&apos;),

    # Hex color
    &apos;hex_color&apos;: re.compile(r&apos;^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$&apos;),

    # Semantic version: 1.2.3 or 1.2.3-alpha.1
    &apos;semver&apos;: re.compile(
        r&apos;^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)&apos;
        r&apos;(-[a-zA-Z0-9.\-]+)?(\+[a-zA-Z0-9.\-]+)?$&apos;
    ),
}

# Test them
tests = {
    &apos;email&apos;:           [&apos;user@example.com&apos;, &apos;bad@&apos;, &apos;no-at-sign&apos;],
    &apos;ipv4&apos;:            [&apos;192.168.1.1&apos;, &apos;256.0.0.1&apos;, &apos;10.0.0&apos;],
    &apos;date_iso&apos;:        [&apos;2024-01-15&apos;, &apos;2024-13-01&apos;, &apos;24-1-1&apos;],
    &apos;strong_password&apos;: [&apos;Abc@1234&apos;, &apos;weakpass&apos;, &apos;NoSpecial1&apos;],
    &apos;hex_color&apos;:       [&apos;#FF5733&apos;, &apos;#abc&apos;, &apos;#GGGGGG&apos;],
    &apos;semver&apos;:          [&apos;1.2.3&apos;, &apos;1.0.0-alpha.1&apos;, &apos;1.2&apos;],
}

for field, values in tests.items():
    pat = patterns[field]
    print(f&quot;\n{field}:&quot;)
    for v in values:
        ok = &apos;✅&apos; if pat.fullmatch(v) else &apos;❌&apos;
        print(f&quot;  {ok} {v!r}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Extraction Patterns (提取模式)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import re

# ── Extract all URLs from text ──────────────────────────────
def extract_urls(text):
    pattern = r&apos;https?://[^\s&amp;lt;&amp;gt;&quot;{}|\\^`\[\]]+&apos;
    return re.findall(pattern, text)

html = &apos;Visit &amp;lt;a href=&quot;https://example.com/path?q=1&quot;&amp;gt;site&amp;lt;/a&amp;gt; or http://other.org&apos;
print(extract_urls(html))
# → [&apos;https://example.com/path?q=1&apos;, &apos;http://other.org&apos;]


# ── Extract all emails ──────────────────────────────────────
def extract_emails(text):
    pattern = r&apos;[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}&apos;
    return re.findall(pattern, text)

text = &quot;Contact alice@example.com or bob.smith@company.co.uk for info&quot;
print(extract_emails(text))
# → [&apos;alice@example.com&apos;, &apos;bob.smith@company.co.uk&apos;]


# ── Parse log lines ─────────────────────────────────────────
def parse_log(line):
    pattern = re.compile(r&apos;&apos;&apos;
        (?P&amp;lt;ip&amp;gt;[\d.]+)          \s+   # IP address
        \S+                     \s+   # ident
        \S+                     \s+   # auth user
        \[(?P&amp;lt;time&amp;gt;[^\]]+)\]    \s+   # timestamp
        &quot;(?P&amp;lt;method&amp;gt;\w+)        \s+
         (?P&amp;lt;path&amp;gt;[^\s&quot;]+)      \s+
         \S+&quot;                   \s+   # HTTP version
        (?P&amp;lt;status&amp;gt;\d{3})       \s+   # status code
        (?P&amp;lt;size&amp;gt;\d+)                 # bytes
    &apos;&apos;&apos;, re.VERBOSE)
    m = pattern.match(line)
    return m.groupdict() if m else None

log_line = &apos;127.0.0.1 - frank [10/Oct/2000:13:55:36 -0700] &quot;GET /apache_pb.gif HTTP/1.0&quot; 200 2326&apos;
print(parse_log(log_line))
# → {&apos;ip&apos;: &apos;127.0.0.1&apos;, &apos;time&apos;: &apos;10/Oct/2000:13:55:36 -0700&apos;,
#    &apos;method&apos;: &apos;GET&apos;, &apos;path&apos;: &apos;/apache_pb.gif&apos;, &apos;status&apos;: &apos;200&apos;, &apos;size&apos;: &apos;2326&apos;}


# ── Extract numbers with units ──────────────────────────────
def extract_measurements(text):
    pattern = r&apos;(\d+(?:\.\d+)?)\s*(px|em|rem|%|pt|vh|vw)&apos;
    return [(float(v), u) for v, u in re.findall(pattern, text)]

css = &quot;width: 100px; margin: 1.5em; font-size: 16px; height: 50vh&quot;
print(extract_measurements(css))
# → [(100.0, &apos;px&apos;), (1.5, &apos;em&apos;), (16.0, &apos;px&apos;), (50.0, &apos;vh&apos;)]
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;3. Cleaning and Normalization Patterns (清理与标准化模式)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import re

# ── Normalize whitespace ────────────────────────────────────
def normalize_whitespace(text):
    return re.sub(r&apos;\s+&apos;, &apos; &apos;, text).strip()

print(normalize_whitespace(&quot;  Hello   World  \n\t  Python  &quot;))
# → Hello World Python


# ── Remove HTML tags ────────────────────────────────────────
def strip_html(html):
    clean = re.sub(r&apos;&amp;lt;[^&amp;gt;]+&amp;gt;&apos;, &apos;&apos;, html)
    return re.sub(r&apos;\s+&apos;, &apos; &apos;, clean).strip()

html = &quot;&amp;lt;h1&amp;gt;Title&amp;lt;/h1&amp;gt;&amp;lt;p&amp;gt;Some &amp;lt;b&amp;gt;bold&amp;lt;/b&amp;gt; and &amp;lt;em&amp;gt;italic&amp;lt;/em&amp;gt; text.&amp;lt;/p&amp;gt;&quot;
print(strip_html(html))
# → Title Some bold and italic text.


# ── Slugify a string ────────────────────────────────────────
def slugify(text):
    text = text.lower()
    text = re.sub(r&apos;[^\w\s-]&apos;, &apos;&apos;,  text)   # remove non-word chars
    text = re.sub(r&apos;[\s_]+&apos;,   &apos;-&apos;, text)   # spaces/underscores → dash
    text = re.sub(r&apos;-+&apos;,       &apos;-&apos;, text)   # multiple dashes → one
    return text.strip(&apos;-&apos;)

print(slugify(&quot;Hello, World! This is Python 3.12&quot;))
# → hello-world-this-is-python-312


# ── Camel case to snake case ────────────────────────────────
def camel_to_snake(name):
    name = re.sub(r&apos;([A-Z]+)([A-Z][a-z])&apos;, r&apos;\1_\2&apos;, name)  # ABCDef → ABC_Def
    name = re.sub(r&apos;([a-z\d])([A-Z])&apos;,      r&apos;\1_\2&apos;, name)  # fooBar → foo_Bar
    return name.lower()

print(camel_to_snake(&apos;camelCaseString&apos;))    # → camel_case_string
print(camel_to_snake(&apos;parseHTMLContent&apos;))   # → parse_html_content
print(camel_to_snake(&apos;MyClassName&apos;))        # → my_class_name


# ── Mask sensitive data ─────────────────────────────────────
def mask_credit_card(text):
    return re.sub(r&apos;\b(\d{4})\d{8}(\d{4})\b&apos;, r&apos;\1 **** **** \2&apos;, text)

def mask_email(text):
    return re.sub(r&apos;(\w{2})\w+(@[^\s]+)&apos;, r&apos;\1***\2&apos;, text)

print(mask_credit_card(&quot;Card: 4111111111111111&quot;))
# → Card: 4111 **** **** 1111
print(mask_email(&quot;Email alice@example.com to bob@test.org&quot;))
# → Email al***@example.com to bo***@test.org
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;4. Common Pitfalls (常见陷阱)&lt;/h2&gt;
&lt;h3&gt;1) Catastrophic backtracking (灾难性回溯)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import re, time

# ⚠️ DANGEROUS pattern: (a+)+ causes exponential backtracking
evil_pattern   = r&apos;^(a+)+$&apos;
safe_pattern   = r&apos;^a+$&apos;

test_string = &apos;a&apos; * 25 + &apos;X&apos;  # no match — forces max backtracking

# Safe pattern — fast
t = time.time()
re.search(safe_pattern, test_string)
print(f&quot;Safe:  {time.time()-t:.6f}s&quot;)   # → ~0.000001s

# Evil pattern — hangs for long inputs!
# (DO NOT run with &apos;a&apos; * 30 + &apos;X&apos;)
t = time.time()
re.search(evil_pattern, &apos;a&apos; * 20 + &apos;X&apos;)
print(f&quot;Evil:  {time.time()-t:.6f}s&quot;)   # → much longer

# FIX: use atomic groups or possessive quantifiers, or restructure
# In Python 3.11+: use re.POSSESSIVE or regex module
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) &lt;code&gt;re.match()&lt;/code&gt; vs &lt;code&gt;re.search()&lt;/code&gt; confusion&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import re

# COMMON MISTAKE: using match() when search() is needed
data = &quot;  123 some text&quot;

# Incorrect — thinking match() searches anywhere
result = re.match(r&apos;\d+&apos;, data)   # → None!  (leading spaces)

# Correct
result = re.search(r&apos;\d+&apos;, data)  # → &apos;123&apos;

# Or anchor explicitly
result = re.match(r&apos;\s*(\d+)&apos;, data)  # → group(1) = &apos;123&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) &lt;code&gt;findall()&lt;/code&gt; group return type surprise&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import re

text = &quot;2024-01 2024-02&quot;

# Bug: adding a group changes return type
print(re.findall(r&apos;\d{4}-\d{2}&apos;,       text))  # → [&apos;2024-01&apos;, &apos;2024-02&apos;]
print(re.findall(r&apos;(\d{4})-\d{2}&apos;,     text))  # → [&apos;2024&apos;, &apos;2024&apos;]  (years only!)
print(re.findall(r&apos;(\d{4})-(\d{2})&apos;,   text))  # → [(&apos;2024&apos;,&apos;01&apos;), (&apos;2024&apos;,&apos;02&apos;)]

# Fix: use non-capturing group when you don&apos;t need the group value
print(re.findall(r&apos;(?:\d{4})-(?:\d{2})&apos;, text))  # → [&apos;2024-01&apos;, &apos;2024-02&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4) Forgetting raw strings&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import re

# WRONG: \b interpreted by Python as backspace character (ASCII 8)
print(re.findall(&apos;\bword\b&apos;, &apos;word in a sentence&apos;))   # → []  WRONG

# CORRECT: raw string
print(re.findall(r&apos;\bword\b&apos;, &apos;word in a sentence&apos;))  # → [&apos;word&apos;]

# WRONG: \d interpreted as literal &apos;d&apos; in some contexts
print(re.findall(&apos;\d+&apos;, &apos;abc 123&apos;))   # may work but is fragile
# CORRECT:
print(re.findall(r&apos;\d+&apos;, &apos;abc 123&apos;))  # → [&apos;123&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h1&gt;&lt;strong&gt;V. Complete API Quick Reference (完整API速查表)&lt;/strong&gt;&lt;/h1&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function / Method&lt;/th&gt;
&lt;th&gt;Returns&lt;/th&gt;
&lt;th&gt;Use when&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;re.compile(pat, flags)&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Pattern&lt;/td&gt;
&lt;td&gt;Pattern reused multiple times&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;re.search(pat, s)&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Match or None&lt;/td&gt;
&lt;td&gt;Find first match anywhere&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;re.match(pat, s)&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Match or None&lt;/td&gt;
&lt;td&gt;Match only at position 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;re.fullmatch(pat, s)&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Match or None&lt;/td&gt;
&lt;td&gt;Pattern must cover entire string&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;re.findall(pat, s)&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;List[str or tuple]&lt;/td&gt;
&lt;td&gt;All matches as a list&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;re.finditer(pat, s)&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Iterator[Match]&lt;/td&gt;
&lt;td&gt;All matches with position info&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;re.sub(pat, repl, s)&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;str&lt;/td&gt;
&lt;td&gt;Replace matches&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;re.subn(pat, repl, s)&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;(str, int)&lt;/td&gt;
&lt;td&gt;Replace + count substitutions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;re.split(pat, s)&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;List[str]&lt;/td&gt;
&lt;td&gt;Split string by pattern&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;re.escape(s)&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;str&lt;/td&gt;
&lt;td&gt;Treat literal string as pattern&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;m.group(n)&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;str&lt;/td&gt;
&lt;td&gt;Get captured group text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;m.groups()&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;tuple&lt;/td&gt;
&lt;td&gt;All groups as tuple&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;m.groupdict()&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;dict&lt;/td&gt;
&lt;td&gt;Named groups as dict&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;m.start() / m.end()&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;td&gt;Match position&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;m.span()&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;(int, int)&lt;/td&gt;
&lt;td&gt;(start, end) tuple&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;m.expand(template)&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;str&lt;/td&gt;
&lt;td&gt;Backreference expansion&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt; Master regex in four steps: &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;① know the 5 building blocks&amp;lt;/span&amp;gt; (literals, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;.&amp;lt;/code&amp;gt;, classes &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;[]&amp;lt;/code&amp;gt;, quantifiers &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;&lt;em&gt;+?{}&amp;lt;/code&amp;gt;, anchors &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;^$\b&amp;lt;/code&amp;gt;) → &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;② use groups &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;()&amp;lt;/code&amp;gt; to capture, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;(?:)&amp;lt;/code&amp;gt; to group without capturing&amp;lt;/span&amp;gt; → &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;③ add lookaround &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;(?=)(?!)&amp;lt;/code&amp;gt; for context-sensitive matching&amp;lt;/span&amp;gt; → &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;④ always use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;r&apos;&apos;&amp;lt;/code&amp;gt; raw strings, prefer non-greedy &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;.&lt;/em&gt;?&amp;lt;/code&amp;gt;, and &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;re.VERBOSE&amp;lt;/code&amp;gt; for complex patterns&amp;lt;/span&amp;gt;. &amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Python text.split()</title><link>https://lxy-alexander.github.io/blog/posts/python/python-textsplit/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/python/python-textsplit/</guid><description>Python text.split()</description><pubDate>Mon, 09 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;&lt;strong&gt;I. &lt;code&gt;text.split()&lt;/code&gt; in Python&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt;
&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;split()&amp;lt;/code&amp;gt; is a Python &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;string method (字符串方法)&amp;lt;/span&amp;gt; that divides a string into a list of substrings. It&apos;s one of the most commonly used tools for text processing.
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;1. Basic Usage&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;text = &quot;Python is awesome&quot;
result = text.split()
print(result)  # Output: [&apos;Python&apos;, &apos;is&apos;, &apos;awesome&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt;
By default, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;split()&amp;lt;/code&amp;gt; uses &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;whitespace characters (空白字符)&amp;lt;/span&amp;gt; as delimiters: spaces, newlines &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;\n&amp;lt;/code&amp;gt;, tabs &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;\t&amp;lt;/code&amp;gt;, etc.
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;2. Parameter Details&lt;/h2&gt;
&lt;h3&gt;1) &lt;code&gt;split()&lt;/code&gt; vs &lt;code&gt;split(&apos; &apos;)&lt;/code&gt; Difference&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;text = &quot;Python   is   awesome&quot;  # Multiple spaces

# Default split() - handles any amount of whitespace
print(text.split())   # Output: [&apos;Python&apos;, &apos;is&apos;, &apos;awesome&apos;]

# split(&apos; &apos;) - strictly splits on single space
print(text.split(&apos; &apos;))  # Output: [&apos;Python&apos;, &apos;&apos;, &apos;&apos;, &apos;is&apos;, &apos;&apos;, &apos;&apos;, &apos;awesome&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) Specifying Separator &lt;code&gt;sep&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;data = &quot;apple,banana,orange&quot;
print(data.split(&apos;,&apos;))  # Output: [&apos;apple&apos;, &apos;banana&apos;, &apos;orange&apos;]

path = &quot;user/local/bin&quot; # slash/backslash
print(path.split(&apos;/&apos;))  # Output: [&apos;user&apos;, &apos;local&apos;, &apos;bin&apos;]

sentence = &quot;Hello-World-Python&quot;
print(sentence.split(&apos;-&apos;))  # Output: [&apos;Hello&apos;, &apos;World&apos;, &apos;Python&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) Limiting Splits with &lt;code&gt;maxsplit&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;The &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;*message&amp;lt;/code&amp;gt; syntax is used for &lt;strong&gt;Extended Unpacking (扩展解包)&lt;/strong&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;date&lt;/code&gt;, &lt;code&gt;time&lt;/code&gt;, &lt;code&gt;level&lt;/code&gt;&lt;/strong&gt;: These take the first 3 elements of the list respectively.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;*message&lt;/code&gt;&lt;/strong&gt;: This collects &lt;strong&gt;all remaining elements&lt;/strong&gt; into a &lt;strong&gt;List (列表)&lt;/strong&gt;. &lt;code&gt;message = parts[3:]  # [&apos;Connection&apos;, &apos;failed&apos;] ← all remaining as list&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;text = &quot;one two three four five&quot;

# Split only first 2 times
print(text.split(maxsplit=2))  # Output: [&apos;one&apos;, &apos;two&apos;, &apos;three four five&apos;]

# Equivalent syntax
print(text.split(&apos; &apos;, 2))  # Output: [&apos;one&apos;, &apos;two&apos;, &apos;three four five&apos;]

# Practical example: parsing simple logs
log = &quot;2024-01-15 10:30:45 ERROR Connection failed&quot;
date, time, level, *message = log.split(maxsplit=3)
print(f&quot;Date: {date}, Time: {time}, Level: {level}, Message: {message}&quot;)
# Output: Date: 2024-01-15, Time: 10:30:45, Level: ERROR, Message: [&apos;Connection&apos;, &apos;failed&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;3. Common Use Cases&lt;/h2&gt;
&lt;h3&gt;1) Word Frequency Counting&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from collections import Counter

text = &quot;Python is awesome. Python is powerful!&quot;
words = text.lower().split()  # Convert to lowercase then split
# Note: Punctuation remains! Output: [&apos;python&apos;, &apos;is&apos;, &apos;awesome.&apos;, &apos;python&apos;, &apos;is&apos;, &apos;powerful!&apos;]

# Better approach: clean punctuation
import re
words = re.findall(r&apos;\w+&apos;, text.lower())
print(Counter(words))  # Output: Counter({&apos;python&apos;: 2, &apos;is&apos;: 2, &apos;awesome&apos;: 1, &apos;powerful&apos;: 1})
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;Explanation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;re&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;regex module&lt;/td&gt;
&lt;td&gt;Python&apos;s &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;regular expression library (正则表达式库)&amp;lt;/span&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.findall()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;find all matches&lt;/td&gt;
&lt;td&gt;Returns &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;all non-overlapping matches&amp;lt;/span&amp;gt; as a list&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;r&apos;&apos;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;raw string&lt;/td&gt;
&lt;td&gt;&amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;Raw string (原始字符串)&amp;lt;/span&amp;gt; - backslashes are treated literally&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\w&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;word character&lt;/td&gt;
&lt;td&gt;Matches &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;letters, digits, and underscore (字母、数字、下划线)&amp;lt;/span&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;+&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;one or more&lt;/td&gt;
&lt;td&gt;&amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;Quantifier (量词)&amp;lt;/span&amp;gt; - match 1 or more occurrences&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;text.lower()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;lowercase&lt;/td&gt;
&lt;td&gt;Converts everything to &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;lowercase (小写)&amp;lt;/span&amp;gt; for case-insensitive counting&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;2) Parsing CSV (Simple Cases)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;csv_line = &quot;John,25,Engineer,New York&quot;
name, age, job, city = csv_line.split(&apos;,&apos;)
print(f&quot;{name} is {age} years old, works as {job} in {city}&quot;)
# Output: John is 25 years old, works as Engineer in New York
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) Handling User Input&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# Parsing commands
command = &quot;save document.txt&quot;
action, filename = command.split(maxsplit=1)
print(f&quot;Action: {action}, File: {filename}&quot;)  # Output: Action: save, File: document.txt

# Processing multiple inputs
user_input = &quot;5 10 15&quot;
numbers = [int(x) for x in user_input.split()]
print(sum(numbers))  # Output: 30
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;4. Important Notes&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;⚠️ Common Pitfalls: &amp;lt;/span&amp;gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Empty string&lt;/strong&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;text = &quot;&quot;
print(text.split())  # Output: [] (empty list)
print(text.split(&apos;,&apos;))  # Output: [&apos;&apos;] (list with one element)
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Separator not found&lt;/strong&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;text = &quot;hello world&quot;
print(text.split(&apos;,&apos;))  # Output: [&apos;hello world&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Consecutive separators&lt;/strong&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;text = &quot;a,,b,c&quot;
print(text.split(&apos;,&apos;))  # Output: [&apos;a&apos;, &apos;&apos;, &apos;b&apos;, &apos;c&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Return value is always a list&lt;/strong&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;text = &quot;python&quot;
result = text.split()
print(type(result))  # Output: &amp;lt;class &apos;list&apos;&amp;gt;
print(result)  # Output: [&apos;python&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;5. Method Comparison&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;split()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Split by whitespace&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&quot;a b c&quot;.split()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[&apos;a&apos;, &apos;b&apos;, &apos;c&apos;]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;split(&apos; &apos;)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Split by single space&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&quot;a  b&quot;.split(&apos; &apos;)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[&apos;a&apos;, &apos;&apos;, &apos;b&apos;]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rsplit()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Split from right&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&quot;a-b-c&quot;.rsplit(&apos;-&apos;,1)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[&apos;a-b&apos;, &apos;c&apos;]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;splitlines()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Split by line breaks&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&quot;a\nb&quot;.splitlines()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[&apos;a&apos;, &apos;b&apos;]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;partition()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Split into 3 parts&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&quot;a-b-c&quot;.partition(&apos;-&apos;)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;(&apos;a&apos;, &apos;-&apos;, &apos;b-c&apos;)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;6. Practical Example: Parsing Configuration Files&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;config = &quot;&quot;&quot;
host=localhost
port=8080
debug=true
&quot;&quot;&quot;

settings = {}
for line in config.strip().split(&apos;\n&apos;):
    if &apos;=&apos; in line:
        key, value = line.split(&apos;=&apos;, 1)
        settings[key] = value

print(settings)
# Output: {&apos;host&apos;: &apos;localhost&apos;, &apos;port&apos;: &apos;8080&apos;, &apos;debug&apos;: &apos;true&apos;}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;7. Advanced Techniques&lt;/h2&gt;
&lt;h3&gt;1) Using &lt;code&gt;split()&lt;/code&gt; with List Comprehension&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# Extract numbers from mixed string
data = &quot;age:25,score:95,weight:70&quot;
values = [item.split(&apos;:&apos;)[1] for item in data.split(&apos;,&apos;)]
print(values)  # Output: [&apos;25&apos;, &apos;95&apos;, &apos;70&apos;]

# Convert to appropriate types
numeric_values = [int(item.split(&apos;:&apos;)[1]) for item in data.split(&apos;,&apos;)]
print(numeric_values)  # Output: [25, 95, 70]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) Handling Multiple Delimiters&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import re

text = &quot;apple;banana,orange|grape&quot;
# Split on ; , or |
fruits = re.split(&apos;[;,]&apos;, text)  # Simple case
fruits = re.split(&apos;[;,\|]&apos;, text)  # With escape for |
print(fruits)  # Output: [&apos;apple&apos;, &apos;banana&apos;, &apos;orange&apos;, &apos;grape&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) Preserving Delimiters&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# Using re.split() with capturing group keeps delimiters
text = &quot;hello-world-python&quot;
parts = re.split(&apos;(-)&apos;, text)
print(parts)  # Output: [&apos;hello&apos;, &apos;-&apos;, &apos;world&apos;, &apos;-&apos;, &apos;python&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;text.split()&amp;lt;/code&amp;gt; splits strings into word lists using whitespace; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;text.split(sep)&amp;lt;/code&amp;gt; splits by a specified delimiter; and the &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;maxsplit&amp;lt;/code&amp;gt; parameter controls how many splits to perform.&amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Python threading</title><link>https://lxy-alexander.github.io/blog/posts/python/python-threading/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/python/python-threading/</guid><description>Python threading</description><pubDate>Mon, 09 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;&lt;strong&gt;I. Python Multithreading — Complete API Reference Manual&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Python&apos;s &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;threading&amp;lt;/span&amp;gt; module provides a high-level interface for &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Multithreading (多线程编程)&amp;lt;/span&amp;gt; built on top of the lower-level &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;_thread&amp;lt;/code&amp;gt; module. Because of the &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;GIL (Global Interpreter Lock, 全局解释器锁)&amp;lt;/span&amp;gt;, threads do not achieve true CPU parallelism for pure Python code — but they excel at &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;IO-bound tasks (IO密集型任务)&amp;lt;/span&amp;gt; such as network requests, file operations, and database calls. This manual covers every public API with runnable examples. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1. Thread — Core Thread Object (核心线程对象)&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;threading.Thread&amp;lt;/code&amp;gt; is the fundamental building block. A thread can be created by passing a &amp;lt;strong&amp;gt;callable target&amp;lt;/strong&amp;gt; or by &amp;lt;strong&amp;gt;subclassing&amp;lt;/strong&amp;gt; and overriding &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;run()&amp;lt;/code&amp;gt;. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;1) Constructor (构造函数)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;threading.Thread(
    group=None,      # reserved, always None
    target=None,     # callable to run in thread
    name=None,       # thread name string
    args=(),         # positional args tuple for target
    kwargs=None,     # keyword args dict for target
    daemon=None      # True → daemon thread (守护线程)
)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) &lt;code&gt;Thread.start()&lt;/code&gt; — Launch the thread&lt;/h3&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;Schedules&amp;lt;/span&amp;gt; the thread for execution. Must be called exactly once per Thread object.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import threading
import time

def worker(name, delay):
    time.sleep(delay)
    print(f&quot;[{name}] finished after {delay}s&quot;)

t1 = threading.Thread(target=worker, args=(&quot;Alpha&quot;, 1))
t2 = threading.Thread(target=worker, args=(&quot;Beta&quot;,  2))

t1.start()   # ← launches t1
t2.start()   # ← launches t2 concurrently

print(&quot;Main thread continues immediately&quot;)
# Output order (non-deterministic):
# Main thread continues immediately
# [Alpha] finished after 1s
# [Beta]  finished after 2s
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Calling &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;start()&amp;lt;/code&amp;gt; twice on the same Thread raises &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;RuntimeError&amp;lt;/code&amp;gt;.&amp;lt;/span&amp;gt; If you need to rerun a task, create a new Thread instance.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;3) &lt;code&gt;Thread.join(timeout=None)&lt;/code&gt; — Wait for completion (等待线程结束)&lt;/h3&gt;
&lt;p&gt;Blocks the calling thread until the target thread terminates, or ==until &lt;code&gt;timeout&lt;/code&gt; seconds elapse.==&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import threading, time

def slow_task():
    print(&quot;Task started&quot;)
    time.sleep(3)
    print(&quot;Task done&quot;)

t = threading.Thread(target=slow_task)
t.start()

t.join(timeout=5)   # wait up to 5 seconds

if t.is_alive():
    print(&quot;Thread still running after timeout!&quot;)
else:
    print(&quot;Thread completed successfully&quot;)
# → Task started
# → Task done
# → Thread completed successfully
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4) &lt;code&gt;Thread.is_alive()&lt;/code&gt; — Check thread status (检查线程状态)&lt;/h3&gt;
&lt;p&gt;Returns &lt;code&gt;True&lt;/code&gt; between &lt;code&gt;start()&lt;/code&gt; and thread termination.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import threading, time

def task():
    time.sleep(2)

t = threading.Thread(target=task)
print(t.is_alive())   # → False  (not started yet)
t.start()
print(t.is_alive())   # → True   (running)
t.join()
print(t.is_alive())   # → False  (terminated)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5) &lt;code&gt;Thread.name&lt;/code&gt; / &lt;code&gt;Thread.getName()&lt;/code&gt; / &lt;code&gt;Thread.setName()&lt;/code&gt; — Thread name (线程名)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

def task():
    # Access name inside the thread
    print(f&quot;Running as: {threading.current_thread().name}&quot;)

t = threading.Thread(target=task, name=&quot;WorkerThread-1&quot;)
print(t.name)          # → WorkerThread-1
t.setName(&quot;Renamed&quot;)
print(t.getName())     # → Renamed
t.start()
t.join()
# → Running as: Renamed
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6) &lt;code&gt;Thread.daemon&lt;/code&gt; — Daemon threads (守护线程)&lt;/h3&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;A daemon thread is automatically killed when ALL non-daemon threads exit — it does NOT block program shutdown.&amp;lt;/span&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import threading, time

def background_monitor():
    while True:
        print(&quot;[Monitor] heartbeat&quot;)
        time.sleep(1)

# Must set daemon BEFORE start()
monitor = threading.Thread(target=background_monitor, daemon=True)
monitor.start()

print(&quot;Main: doing work&quot;)
time.sleep(2.5)
print(&quot;Main: exiting — monitor will be killed automatically&quot;)
# → [Monitor] heartbeat
# → Main: doing work
# → [Monitor] heartbeat
# → [Monitor] heartbeat
# → Main: exiting — monitor will be killed automatically
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;7) &lt;code&gt;Thread.ident&lt;/code&gt; / &lt;code&gt;Thread.native_id&lt;/code&gt; — Thread identifiers (线程标识符)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

def show_ids():
    t = threading.current_thread()
    print(f&quot;ident={t.ident}, native_id={t.native_id}&quot;)

t = threading.Thread(target=show_ids)
t.start()
t.join()
# → ident=140234567890, native_id=12345

print(f&quot;Main ident: {threading.main_thread().ident}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;8) Subclass Pattern — Override &lt;code&gt;run()&lt;/code&gt; (子类模式)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading, time

class DownloadThread(threading.Thread):
    &quot;&quot;&quot;Custom thread that downloads a resource.&quot;&quot;&quot;

    def __init__(self, url: str):
        super().__init__(name=f&quot;Download-{url}&quot;)
        self.url    = url
        self.result = None

    def run(self):
        # Simulate download
        time.sleep(0.5)
        self.result = f&quot;&amp;lt;html from {self.url}&amp;gt;&quot;
        print(f&quot;Downloaded: {self.url}&quot;)

threads = [DownloadThread(f&quot;http://example.com/page{i}&quot;) for i in range(3)]

for t in threads:
    t.start()

for t in threads:
    t.join()
    print(f&quot;Result: {t.result}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Lock — Mutual Exclusion (互斥锁)&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; A &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Lock (互斥锁)&amp;lt;/span&amp;gt; ensures only ONE thread accesses a critical section (临界区) at a time. It has two states: &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;locked&amp;lt;/span&amp;gt; and &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;unlocked&amp;lt;/span&amp;gt;. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;1) &lt;code&gt;Lock.acquire(blocking=True, timeout=-1)&lt;/code&gt; / &lt;code&gt;Lock.release()&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

counter = 0
lock    = threading.Lock()

def increment(n):
    global counter
    for _ in range(n):
        lock.acquire()     # ← blocks until lock is free
        counter += 1       # critical section (临界区)
        lock.release()     # ← always release!

threads = [threading.Thread(target=increment, args=(100_000,)) for _ in range(5)]
for t in threads: t.start()
for t in threads: t.join()

print(f&quot;Counter: {counter}&quot;)   # → Counter: 500000  (always correct)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Never call &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;release()&amp;lt;/code&amp;gt; without a matching &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;acquire()&amp;lt;/code&amp;gt; — raises &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;RuntimeError&amp;lt;/code&amp;gt;. Always prefer the &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;with&amp;lt;/code&amp;gt; context manager to guarantee release on exceptions.&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;2) Context Manager — &lt;code&gt;with lock&lt;/code&gt; (上下文管理器)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

shared_list = []
lock = threading.Lock()

def safe_append(value):
    with lock:                     # ← acquire on entry, release on exit (even on exception)
        shared_list.append(value)

threads = [threading.Thread(target=safe_append, args=(i,)) for i in range(10)]
for t in threads: t.start()
for t in threads: t.join()

print(sorted(shared_list))   # → [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) &lt;code&gt;Lock.acquire(blocking=False)&lt;/code&gt; — Non-blocking try (非阻塞尝试)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading, time

lock = threading.Lock()

def try_lock(name):
    acquired = lock.acquire(blocking=False)
    if acquired:
        print(f&quot;[{name}] acquired the lock&quot;)
        time.sleep(2)
        lock.release()
    else:
        print(f&quot;[{name}] could not acquire — skipping&quot;)

t1 = threading.Thread(target=try_lock, args=(&quot;T1&quot;,))
t2 = threading.Thread(target=try_lock, args=(&quot;T2&quot;,))
t1.start(); t2.start()
t1.join();  t2.join()
# → [T1] acquired the lock
# → [T2] could not acquire — skipping
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4) &lt;code&gt;Lock.acquire(timeout=N)&lt;/code&gt; — Timed wait (超时等待)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading, time

lock = threading.Lock()
lock.acquire()   # pre-lock it

def worker():
    result = lock.acquire(timeout=1.5)   # wait max 1.5s
    if result:
        print(&quot;Got the lock&quot;)
        lock.release()
    else:
        print(&quot;Timed out waiting for lock&quot;)

t = threading.Thread(target=worker)
t.start()
t.join()
# → Timed out waiting for lock   (lock was never released)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5) &lt;code&gt;Lock.locked()&lt;/code&gt; — Query state (查询状态)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

lock = threading.Lock()
print(lock.locked())   # → False

lock.acquire()
print(lock.locked())   # → True

lock.release()
print(lock.locked())   # → False
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;3. RLock — Reentrant Lock (可重入锁)&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; A &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;RLock (可重入锁)&amp;lt;/span&amp;gt; can be acquired multiple times by the &amp;lt;em&amp;gt;same thread&amp;lt;/em&amp;gt; without deadlocking. It tracks an internal &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;recursion count (递归计数)&amp;lt;/span&amp;gt; — the lock is only released when the count reaches zero. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;1) Basic RLock usage&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

rlock = threading.RLock()

def outer():
    with rlock:                   # recursion count → 1
        print(&quot;outer acquired&quot;)
        inner()                   # same thread acquires again
        print(&quot;outer releasing&quot;)
    # recursion count → 0 (fully released)

def inner():
    with rlock:                   # recursion count → 2
        print(&quot;inner acquired&quot;)
    # recursion count → 1

t = threading.Thread(target=outer)
t.start(); t.join()
# → outer acquired
# → inner acquired
# → outer releasing
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;A plain &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Lock&amp;lt;/code&amp;gt; would DEADLOCK in the above pattern&amp;lt;/span&amp;gt; because the same thread tries to acquire an already-locked lock. Use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;RLock&amp;lt;/code&amp;gt; whenever a method holding the lock may call another method that also needs the lock.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;2) RLock in a class (类中使用RLock)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

class BankAccount:
    def __init__(self, balance: float):
        self.balance = balance
        self._lock   = threading.RLock()

    def deposit(self, amount: float):
        with self._lock:
            self.balance += amount
            print(f&quot;Deposited {amount:.2f} → balance={self.balance:.2f}&quot;)

    def withdraw(self, amount: float):
        with self._lock:
            self.balance -= amount
            print(f&quot;Withdrew  {amount:.2f} → balance={self.balance:.2f}&quot;)

    def transfer_in(self, amount: float):
        with self._lock:            # outer acquire
            self.deposit(amount)   # inner acquire (reentrant!)
            print(f&quot;Transfer complete&quot;)

account = BankAccount(1000.0)
t = threading.Thread(target=account.transfer_in, args=(250.0,))
t.start(); t.join()
# → Deposited 250.00 → balance=1250.00
# → Transfer complete
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;4. Condition — Wait/Notify Pattern (条件变量)&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; A &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Condition (条件变量)&amp;lt;/span&amp;gt; allows threads to &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;wait&amp;lt;/span&amp;gt; for a specific condition to become true and &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;notify&amp;lt;/span&amp;gt; other threads when it does. It wraps an underlying lock. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;1) &lt;code&gt;Condition.wait()&lt;/code&gt; / &lt;code&gt;notify()&lt;/code&gt; / &lt;code&gt;notify_all()&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading, time, collections

# Classic Producer-Consumer (生产者-消费者) pattern
buffer    = collections.deque()
MAX_SIZE  = 3
condition = threading.Condition()

def producer():
    for i in range(6):
        with condition:
            while len(buffer) &amp;gt;= MAX_SIZE:
                print(f&quot;Producer waiting — buffer full&quot;)
                condition.wait()           # ← releases lock, blocks
            buffer.append(i)
            print(f&quot;Produced {i}  | buffer={list(buffer)}&quot;)
            condition.notify_all()        # ← wake waiting consumers
        time.sleep(0.3)

def consumer(name):
    for _ in range(3):
        with condition:
            while not buffer:
                print(f&quot;[{name}] waiting — buffer empty&quot;)
                condition.wait()           # ← releases lock, blocks
            item = buffer.popleft()
            print(f&quot;[{name}] consumed {item} | buffer={list(buffer)}&quot;)
            condition.notify_all()        # ← wake waiting producer

threads = [
    threading.Thread(target=producer),
    threading.Thread(target=consumer, args=(&quot;C1&quot;,)),
    threading.Thread(target=consumer, args=(&quot;C2&quot;,)),
]
for t in threads: t.start()
for t in threads: t.join()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) &lt;code&gt;Condition.wait(timeout=N)&lt;/code&gt; — Timed wait&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading, time

condition = threading.Condition()
data_ready = False

def waiter():
    with condition:
        result = condition.wait(timeout=2.0)   # wait max 2 seconds
        if result:
            print(&quot;Condition met!&quot;)
        else:
            print(&quot;Timed out — condition never triggered&quot;)

def notifier():
    time.sleep(5)   # too slow
    with condition:
        condition.notify()

t1 = threading.Thread(target=waiter)
t2 = threading.Thread(target=notifier)
t1.start(); t2.start()
t1.join();  t2.join()
# → Timed out — condition never triggered
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) &lt;code&gt;Condition.wait_for(predicate, timeout=None)&lt;/code&gt; — Predicate wait&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading, time

items  = []
cond   = threading.Condition()

def consumer():
    with cond:
        # Block until at least 3 items are available
        cond.wait_for(lambda: len(items) &amp;gt;= 3)
        print(f&quot;Got items: {items}&quot;)

def producer():
    for i in range(5):
        time.sleep(0.5)
        with cond:
            items.append(i)
            print(f&quot;Added item {i}&quot;)
            cond.notify_all()

t1 = threading.Thread(target=consumer)
t2 = threading.Thread(target=producer)
t1.start(); t2.start()
t1.join();  t2.join()
# → Added item 0
# → Added item 1
# → Added item 2
# → Got items: [0, 1, 2]
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;5. Semaphore &amp;amp; BoundedSemaphore (信号量)&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; A &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Semaphore (信号量)&amp;lt;/span&amp;gt; maintains an internal counter. &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;acquire()&amp;lt;/code&amp;gt; decrements it (blocks at zero); &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;release()&amp;lt;/code&amp;gt; increments it. Perfect for limiting concurrent access to a resource pool. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;1) &lt;code&gt;Semaphore(value=1)&lt;/code&gt; — Connection pool simulation (连接池模拟)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading, time, random

# Allow max 3 simultaneous DB connections
db_semaphore = threading.Semaphore(3)

def use_db_connection(thread_id):
    print(f&quot;Thread {thread_id}: waiting for DB connection&quot;)
    with db_semaphore:                      # acquire (count -1)
        print(f&quot;Thread {thread_id}: got connection&quot;)
        time.sleep(random.uniform(0.5, 1.5))
        print(f&quot;Thread {thread_id}: released connection&quot;)
                                            # release (count +1) on exit

threads = [threading.Thread(target=use_db_connection, args=(i,)) for i in range(7)]
for t in threads: t.start()
for t in threads: t.join()
# At most 3 &quot;got connection&quot; lines active at any time
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) &lt;code&gt;BoundedSemaphore&lt;/code&gt; — Prevent over-release (防止超额释放)&lt;/h3&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Warning: a plain &lt;code&gt;Semaphore&lt;/code&gt; allows &lt;code&gt;release()&lt;/code&gt; beyond the initial value — this is usually a bug. &lt;code&gt;BoundedSemaphore&lt;/code&gt; raises &lt;code&gt;ValueError&lt;/code&gt; if the count would exceed the initial value.&amp;lt;/span&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import threading

sem   = threading.Semaphore(2)
bsem  = threading.BoundedSemaphore(2)

# Plain Semaphore — silently over-releases
sem.release()   # count goes to 3 — no error (潜在bug)
print(f&quot;Semaphore value after over-release: OK (silent)&quot;)

# BoundedSemaphore — raises ValueError
try:
    bsem.release()   # count would exceed 2
except ValueError as e:
    print(f&quot;BoundedSemaphore caught: {e}&quot;)
# → BoundedSemaphore caught: Semaphore released too many times
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) Rate limiter pattern (限速器模式)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading, time

# Limit to 2 concurrent API calls
api_semaphore = threading.BoundedSemaphore(2)

def call_api(endpoint):
    with api_semaphore:
        print(f&quot;Calling {endpoint}&quot;)
        time.sleep(1)   # simulate API latency
        print(f&quot;Done    {endpoint}&quot;)

endpoints = [f&quot;/api/resource/{i}&quot; for i in range(6)]
threads   = [threading.Thread(target=call_api, args=(ep,)) for ep in endpoints]

for t in threads: t.start()
for t in threads: t.join()
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;6. Event — Simple Flag Signaling (事件信号)&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; An &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Event (事件)&amp;lt;/span&amp;gt; is a simple boolean flag. Threads can &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;wait()&amp;lt;/code&amp;gt; until the flag is set, and any thread can &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;set()&amp;lt;/code&amp;gt; or &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;clear()&amp;lt;/code&amp;gt; it. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;1) &lt;code&gt;Event.set()&lt;/code&gt; / &lt;code&gt;Event.clear()&lt;/code&gt; / &lt;code&gt;Event.wait()&lt;/code&gt; / &lt;code&gt;Event.is_set()&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading, time

start_event = threading.Event()

def worker(name):
    print(f&quot;[{name}] waiting for start signal...&quot;)
    start_event.wait()               # blocks until event is set
    print(f&quot;[{name}] GO! Starting work&quot;)

workers = [threading.Thread(target=worker, args=(f&quot;W{i}&quot;,)) for i in range(4)]
for w in workers: w.start()

print(&quot;Main: preparing...&quot;)
time.sleep(2)
print(&quot;Main: firing start signal!&quot;)
start_event.set()                    # wake ALL waiting threads at once

for w in workers: w.join()
# → [W0] waiting for start signal...
# → [W1] waiting for start signal...
# → [W2] waiting for start signal...
# → [W3] waiting for start signal...
# (2s pause)
# → Main: firing start signal!
# → [W0] GO! Starting work    (all 4 unblock simultaneously)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) &lt;code&gt;Event.wait(timeout=N)&lt;/code&gt; — Timed wait&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading, time

ready = threading.Event()

def service():
    print(&quot;Service: initializing (takes 3s)...&quot;)
    time.sleep(3)
    ready.set()
    print(&quot;Service: ready!&quot;)

def client():
    if ready.wait(timeout=1.5):    # only wait 1.5s
        print(&quot;Client: connected!&quot;)
    else:
        print(&quot;Client: service not ready in time, aborting&quot;)

t1 = threading.Thread(target=service)
t2 = threading.Thread(target=client)
t1.start(); t2.start()
t1.join();  t2.join()
# → Service: initializing (takes 3s)...
# → Client: service not ready in time, aborting
# → Service: ready!
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) Stop signal pattern (停止信号模式)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading, time

stop_event = threading.Event()

def background_worker():
    count = 0
    while not stop_event.is_set():    # check flag each iteration
        print(f&quot;Working... iteration {count}&quot;)
        count += 1
        time.sleep(0.5)
    print(&quot;Worker: received stop signal, exiting cleanly&quot;)

t = threading.Thread(target=background_worker)
t.start()

time.sleep(2)
print(&quot;Main: sending stop signal&quot;)
stop_event.set()
t.join()
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;7. Timer — Delayed Execution (延迟执行)&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;threading.Timer&amp;lt;/code&amp;gt; is a subclass of &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Thread&amp;lt;/code&amp;gt; that executes a function after a specified delay. It can be &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;cancelled&amp;lt;/span&amp;gt; before firing. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;1) Basic Timer&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

def reminder(message):
    print(f&quot;⏰ Reminder: {message}&quot;)

# Fire after 3 seconds
t = threading.Timer(3.0, reminder, args=(&quot;Meeting at 3pm!&quot;,))
t.start()

print(&quot;Timer set. Waiting...&quot;)
t.join()
# → Timer set. Waiting...
# (3s pause)
# → ⏰ Reminder: Meeting at 3pm!
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) &lt;code&gt;Timer.cancel()&lt;/code&gt; — Cancel before firing&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading, time

fired = False

def action():
    global fired
    fired = True
    print(&quot;Action fired!&quot;)

t = threading.Timer(5.0, action)
t.start()

time.sleep(1)
t.cancel()    # ← cancel within the window
t.join()

print(f&quot;Action fired: {fired}&quot;)   # → Action fired: False
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) Repeating timer pattern (重复定时器模式)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

class RepeatingTimer:
    &quot;&quot;&quot;Fires a function every `interval` seconds.&quot;&quot;&quot;

    def __init__(self, interval: float, func, *args):
        self.interval = interval
        self.func     = func
        self.args     = args
        self._timer   = None
        self._running = False

    def _run(self):
        self.func(*self.args)
        if self._running:
            self._schedule()

    def _schedule(self):
        self._timer = threading.Timer(self.interval, self._run)
        self._timer.daemon = True
        self._timer.start()

    def start(self):
        self._running = True
        self._schedule()

    def stop(self):
        self._running = False
        if self._timer:
            self._timer.cancel()

import time

counter = [0]
def tick():
    counter[0] += 1
    print(f&quot;Tick #{counter[0]}&quot;)

rt = RepeatingTimer(0.5, tick)
rt.start()
time.sleep(2.5)
rt.stop()
print(f&quot;Total ticks: {counter[0]}&quot;)   # → Total ticks: 5
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;8. Barrier — Thread Synchronization Point (屏障同步点)&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; A &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Barrier (屏障)&amp;lt;/span&amp;gt; makes a fixed number of threads wait at a rendezvous point until ALL of them arrive — then releases all of them simultaneously. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;1) &lt;code&gt;Barrier(parties, action=None, timeout=None)&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading, time, random

NUM_WORKERS = 4
barrier = threading.Barrier(NUM_WORKERS)

def phase_worker(name):
    # Phase 1
    duration = random.uniform(0.5, 2.0)
    print(f&quot;[{name}] phase 1 working for {duration:.1f}s&quot;)
    time.sleep(duration)
    print(f&quot;[{name}] phase 1 done — waiting at barrier&quot;)

    barrier.wait()     # ← all threads block here until all 4 arrive

    print(f&quot;[{name}] phase 2 starting (all threads released together)&quot;)

threads = [threading.Thread(target=phase_worker, args=(f&quot;W{i}&quot;,))
           for i in range(NUM_WORKERS)]
for t in threads: t.start()
for t in threads: t.join()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) &lt;code&gt;Barrier&lt;/code&gt; with &lt;code&gt;action&lt;/code&gt; callback&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading, time

def setup_phase():
    &quot;&quot;&quot;Runs ONCE when all threads reach the barrier, before release.&quot;&quot;&quot;
    print(&quot;&amp;gt;&amp;gt;&amp;gt; All threads ready — running barrier action &amp;lt;&amp;lt;&amp;lt;&quot;)

barrier = threading.Barrier(3, action=setup_phase)

def worker(name):
    time.sleep(0.1)
    print(f&quot;[{name}] arrived at barrier&quot;)
    barrier.wait()
    print(f&quot;[{name}] past barrier&quot;)

threads = [threading.Thread(target=worker, args=(f&quot;T{i}&quot;,)) for i in range(3)]
for t in threads: t.start()
for t in threads: t.join()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) &lt;code&gt;Barrier.abort()&lt;/code&gt; / &lt;code&gt;BrokenBarrierError&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading, time

barrier = threading.Barrier(3)

def risky_worker(name, should_abort):
    try:
        if should_abort:
            time.sleep(0.2)
            print(f&quot;[{name}] aborting barrier!&quot;)
            barrier.abort()          # breaks the barrier for everyone
        else:
            print(f&quot;[{name}] waiting at barrier...&quot;)
            barrier.wait(timeout=2)
            print(f&quot;[{name}] passed!&quot;)
    except threading.BrokenBarrierError:
        print(f&quot;[{name}] barrier was broken — handling gracefully&quot;)

threads = [
    threading.Thread(target=risky_worker, args=(&quot;T0&quot;, False)),
    threading.Thread(target=risky_worker, args=(&quot;T1&quot;, False)),
    threading.Thread(target=risky_worker, args=(&quot;T2&quot;, True)),   # aborts
]
for t in threads: t.start()
for t in threads: t.join()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4) Barrier properties&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

b = threading.Barrier(5)
print(b.parties)    # → 5   (total threads needed)
print(b.n_waiting)  # → 0   (currently waiting)
print(b.broken)     # → False
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;9. local — Thread-local Storage (线程本地存储)&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;threading.local()&amp;lt;/code&amp;gt; creates an object where each thread has its &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;own independent copy&amp;lt;/span&amp;gt; of every attribute. Ideal for thread-specific state like database connections or request contexts. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;1) Basic thread-local usage&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

local_data = threading.local()

def worker(value):
    local_data.x = value              # each thread sets its own .x
    import time; time.sleep(0.1)      # let other threads run
    print(f&quot;Thread {threading.current_thread().name}: x = {local_data.x}&quot;)

threads = [threading.Thread(target=worker, args=(i*10,), name=f&quot;T{i}&quot;)
           for i in range(4)]
for t in threads: t.start()
for t in threads: t.join()
# → Thread T0: x = 0
# → Thread T1: x = 10
# → Thread T2: x = 20
# → Thread T3: x = 30
# (each thread sees only its own value — no interference)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) Thread-local DB connection pattern&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading
import sqlite3

_local = threading.local()

def get_connection(db_path: str) -&amp;gt; sqlite3.Connection:
    &quot;&quot;&quot;Return a per-thread DB connection (创建线程私有数据库连接).&quot;&quot;&quot;
    if not hasattr(_local, &quot;conn&quot;):
        _local.conn = sqlite3.connect(db_path)
        print(f&quot;[{threading.current_thread().name}] created new connection&quot;)
    return _local.conn

def db_worker(db_path: str):
    conn = get_connection(db_path)
    conn.execute(&quot;CREATE TABLE IF NOT EXISTS t (v INTEGER)&quot;)
    conn.execute(&quot;INSERT INTO t VALUES (?)&quot;, (threading.get_ident(),))
    conn.commit()
    print(f&quot;[{threading.current_thread().name}] inserted row&quot;)

threads = [threading.Thread(target=db_worker, args=(&quot;:memory:&quot;,), name=f&quot;DB-{i}&quot;)
           for i in range(3)]
for t in threads: t.start()
for t in threads: t.join()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) Subclass &lt;code&gt;local&lt;/code&gt; for initialization&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

class RequestContext(threading.local):
    &quot;&quot;&quot;Thread-local request context with defaults.&quot;&quot;&quot;
    def __init__(self):
        super().__init__()
        self.user_id   = None
        self.request_id = None

ctx = RequestContext()

def handle_request(user_id, req_id):
    ctx.user_id    = user_id
    ctx.request_id = req_id
    import time; time.sleep(0.05)
    print(f&quot;Processing request {ctx.request_id} for user {ctx.user_id}&quot;)

threads = [threading.Thread(target=handle_request, args=(f&quot;user{i}&quot;, f&quot;req-{i:03}&quot;))
           for i in range(4)]
for t in threads: t.start()
for t in threads: t.join()
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;10. Module-level Functions (模块级函数)&lt;/h2&gt;
&lt;h3&gt;1) &lt;code&gt;threading.current_thread()&lt;/code&gt; — Get the current thread&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

def show_self():
    t = threading.current_thread()
    print(f&quot;name={t.name}, ident={t.ident}, daemon={t.daemon}&quot;)

main_t = threading.current_thread()
print(f&quot;Main thread: {main_t.name}&quot;)

t = threading.Thread(target=show_self, name=&quot;MyWorker&quot;)
t.start(); t.join()
# → Main thread: MainThread
# → name=MyWorker, ident=140..., daemon=False
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) &lt;code&gt;threading.main_thread()&lt;/code&gt; — Get the main thread&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

def check_main():
    mt = threading.main_thread()
    ct = threading.current_thread()
    print(f&quot;Main thread: {mt.name}&quot;)
    print(f&quot;This thread: {ct.name}&quot;)
    print(f&quot;Am I main?  {ct is mt}&quot;)

t = threading.Thread(target=check_main)
t.start(); t.join()
# → Main thread: MainThread
# → This thread: Thread-1
# → Am I main?  False
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) &lt;code&gt;threading.active_count()&lt;/code&gt; — Count live threads&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading, time

def slow():
    time.sleep(2)

print(threading.active_count())   # → 1  (main only)

threads = [threading.Thread(target=slow) for _ in range(3)]
for t in threads: t.start()

print(threading.active_count())   # → 4  (main + 3 workers)
for t in threads: t.join()
print(threading.active_count())   # → 1
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4) &lt;code&gt;threading.enumerate()&lt;/code&gt; — List all live threads&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading, time

def task(n):
    time.sleep(n)

threads = [threading.Thread(target=task, args=(i,), name=f&quot;T{i}&quot;) for i in range(1,4)]
for t in threads: t.start()

for t in threading.enumerate():
    print(f&quot;  alive: {t.name} | daemon={t.daemon}&quot;)
# → alive: MainThread | daemon=False
# → alive: T1        | daemon=False
# → alive: T2        | daemon=False
# → alive: T3        | daemon=False

for t in threads: t.join()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5) &lt;code&gt;threading.settrace(func)&lt;/code&gt; / &lt;code&gt;threading.setprofile(func)&lt;/code&gt; — Thread hooks&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading, sys

def my_tracer(frame, event, arg):
    if event == &quot;call&quot;:
        print(f&quot;[TRACE] calling {frame.f_code.co_name}&quot;)
    return my_tracer

def task():
    x = 1 + 1
    return x

threading.settrace(my_tracer)    # set trace for ALL future threads
t = threading.Thread(target=task)
t.start(); t.join()
threading.settrace(None)         # remove tracer
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6) &lt;code&gt;threading.stack_size(size=0)&lt;/code&gt; — Set thread stack size&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

# Set stack size to 512 KB for all future threads
threading.stack_size(512 * 1024)
print(f&quot;Stack size: {threading.stack_size()} bytes&quot;)

def task():
    print(f&quot;Running with custom stack size&quot;)

t = threading.Thread(target=task)
t.start(); t.join()

threading.stack_size(0)   # reset to default
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;7) &lt;code&gt;threading.excepthook&lt;/code&gt; — Handle uncaught thread exceptions (未捕获异常处理)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

def custom_excepthook(args):
    print(f&quot;Uncaught exception in thread [{args.thread.name}]:&quot;)
    print(f&quot;  Type:    {args.exc_type.__name__}&quot;)
    print(f&quot;  Message: {args.exc_value}&quot;)

threading.excepthook = custom_excepthook

def buggy_task():
    raise ValueError(&quot;Something went wrong in thread!&quot;)

t = threading.Thread(target=buggy_task, name=&quot;BuggyThread&quot;)
t.start(); t.join()
# → Uncaught exception in thread [BuggyThread]:
# →   Type:    ValueError
# →   Message: Something went wrong in thread!
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;8) &lt;code&gt;threading.get_ident()&lt;/code&gt; / &lt;code&gt;threading.get_native_id()&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

def show_ids():
    print(f&quot;Python ident:    {threading.get_ident()}&quot;)
    print(f&quot;OS native id:    {threading.get_native_id()}&quot;)

t = threading.Thread(target=show_ids)
t.start(); t.join()
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;11. queue Module — Thread-safe Queues (线程安全队列)&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; The &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;queue&amp;lt;/code&amp;gt; module provides three thread-safe queue classes: &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Queue (FIFO)&amp;lt;/span&amp;gt;, &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;LifoQueue (LIFO/stack)&amp;lt;/span&amp;gt;, and &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;PriorityQueue (优先队列)&amp;lt;/span&amp;gt;. All use internal locks, so no external synchronization is needed. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;1) &lt;code&gt;Queue(maxsize=0)&lt;/code&gt; — FIFO Queue&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from queue import Queue
import threading, time

q = Queue(maxsize=3)

def producer():
    for i in range(6):
        q.put(i)          # blocks if queue is full (maxsize reached)
        print(f&quot;Put {i}  | qsize={q.qsize()}&quot;)
        time.sleep(0.2)

def consumer():
    for _ in range(6):
        item = q.get()    # blocks if queue is empty
        print(f&quot;Got {item}&quot;)
        q.task_done()
        time.sleep(0.5)

t1 = threading.Thread(target=producer)
t2 = threading.Thread(target=consumer)
t1.start(); t2.start()
t1.join();  t2.join()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) &lt;code&gt;Queue.put_nowait()&lt;/code&gt; / &lt;code&gt;Queue.get_nowait()&lt;/code&gt; — Non-blocking&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from queue import Queue, Full, Empty

q = Queue(maxsize=2)
q.put(&quot;item1&quot;)
q.put(&quot;item2&quot;)

try:
    q.put_nowait(&quot;item3&quot;)     # queue full!
except Full:
    print(&quot;Queue full — item3 dropped&quot;)

try:
    while True:
        print(q.get_nowait())
except Empty:
    print(&quot;Queue emptied&quot;)
# → Queue full — item3 dropped
# → item1
# → item2
# → Queue emptied
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) &lt;code&gt;Queue.join()&lt;/code&gt; / &lt;code&gt;Queue.task_done()&lt;/code&gt; — Work tracking&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from queue import Queue
import threading

work_queue = Queue()

def worker():
    while True:
        task = work_queue.get()
        if task is None:
            break
        print(f&quot;Processing: {task}&quot;)
        work_queue.task_done()   # signal this task is complete

# Start 3 workers
workers = [threading.Thread(target=worker, daemon=True) for _ in range(3)]
for w in workers: w.start()

# Enqueue tasks
for task in [&quot;task_A&quot;, &quot;task_B&quot;, &quot;task_C&quot;, &quot;task_D&quot;, &quot;task_E&quot;]:
    work_queue.put(task)

work_queue.join()   # blocks until ALL task_done() called
print(&quot;All tasks completed!&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4) &lt;code&gt;LifoQueue&lt;/code&gt; — Stack (栈/后进先出)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from queue import LifoQueue

stack = LifoQueue()
stack.put(&quot;first&quot;)
stack.put(&quot;second&quot;)
stack.put(&quot;third&quot;)

while not stack.empty():
    print(stack.get())
# → third
# → second
# → first
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5) &lt;code&gt;PriorityQueue&lt;/code&gt; — Priority-based processing (优先级队列)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from queue import PriorityQueue
import threading, time

pq = PriorityQueue()

# (priority, task_name) — lower number = higher priority
pq.put((3, &quot;low-priority task&quot;))
pq.put((1, &quot;URGENT task&quot;))
pq.put((2, &quot;medium task&quot;))
pq.put((1, &quot;another URGENT task&quot;))

while not pq.empty():
    priority, task = pq.get()
    print(f&quot;[priority={priority}] Processing: {task}&quot;)
# → [priority=1] Processing: URGENT task
# → [priority=1] Processing: another URGENT task
# → [priority=2] Processing: medium task
# → [priority=3] Processing: low-priority task
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;12. ThreadPoolExecutor — High-level Thread Pool (高级线程池)&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;concurrent.futures.ThreadPoolExecutor&amp;lt;/code&amp;gt; provides a high-level, &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;Future-based (Future对象)&amp;lt;/span&amp;gt; interface for thread pools. It is the &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;recommended way&amp;lt;/span&amp;gt; to run IO-bound tasks in modern Python. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;1) &lt;code&gt;submit()&lt;/code&gt; → Future&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from concurrent.futures import ThreadPoolExecutor
import time

def fetch_data(url: str) -&amp;gt; str:
    time.sleep(1)   # simulate network call
    return f&quot;&amp;lt;data from {url}&amp;gt;&quot;

urls = [f&quot;http://example.com/page{i}&quot; for i in range(5)]

with ThreadPoolExecutor(max_workers=3) as executor:
    futures = [executor.submit(fetch_data, url) for url in urls]

    for future in futures:
        result = future.result()   # blocks until this future completes
        print(result)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) &lt;code&gt;map()&lt;/code&gt; — Parallel map (并行映射)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from concurrent.futures import ThreadPoolExecutor
import time

def square(n):
    time.sleep(0.2)
    return n * n

with ThreadPoolExecutor(max_workers=4) as executor:
    results = list(executor.map(square, range(10)))

print(results)   # → [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) &lt;code&gt;Future&lt;/code&gt; API — &lt;code&gt;done()&lt;/code&gt;, &lt;code&gt;cancel()&lt;/code&gt;, &lt;code&gt;add_done_callback()&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from concurrent.futures import ThreadPoolExecutor
import time

def slow_task(n):
    time.sleep(n)
    return f&quot;result-{n}&quot;

def on_done(future):
    print(f&quot;Callback: task finished → {future.result()}&quot;)

with ThreadPoolExecutor(max_workers=2) as executor:
    f1 = executor.submit(slow_task, 1)
    f2 = executor.submit(slow_task, 2)

    f1.add_done_callback(on_done)    # register callback
    f2.add_done_callback(on_done)

    print(f&quot;f1 done: {f1.done()}&quot;)   # likely False (still running)
    time.sleep(1.5)
    print(f&quot;f1 done: {f1.done()}&quot;)   # → True
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4) &lt;code&gt;as_completed()&lt;/code&gt; — Process in completion order (按完成顺序处理)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from concurrent.futures import ThreadPoolExecutor, as_completed
import time, random

def task(n):
    delay = random.uniform(0.1, 1.0)
    time.sleep(delay)
    return (n, delay)

with ThreadPoolExecutor(max_workers=5) as executor:
    futures = {executor.submit(task, i): i for i in range(8)}

    for future in as_completed(futures):
        task_id = futures[future]
        n, delay = future.result()
        print(f&quot;Task {n} finished in {delay:.2f}s&quot;)
# Tasks print in the order they complete, not submission order
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5) Exception handling in futures (Future异常处理)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from concurrent.futures import ThreadPoolExecutor

def risky(x):
    if x == 3:
        raise ValueError(f&quot;Bad input: {x}&quot;)
    return x * 2

with ThreadPoolExecutor(max_workers=2) as executor:
    futures = [executor.submit(risky, i) for i in range(5)]

for i, f in enumerate(futures):
    try:
        print(f&quot;Result {i}: {f.result()}&quot;)
    except ValueError as e:
        print(f&quot;Result {i}: ERROR — {e}&quot;)
# → Result 0: 0
# → Result 1: 2
# → Result 2: 4
# → Result 3: ERROR — Bad input: 3
# → Result 4: 8
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;13. Common Patterns &amp;amp; Pitfalls (常见模式与陷阱)&lt;/h2&gt;
&lt;h3&gt;1) Race condition example (竞态条件示例)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

counter = 0   # UNSAFE shared state

def unsafe_increment():
    global counter
    for _ in range(100_000):
        counter += 1   # NOT atomic! (read-modify-write)

threads = [threading.Thread(target=unsafe_increment) for _ in range(5)]
for t in threads: t.start()
for t in threads: t.join()

print(f&quot;Expected: 500000&quot;)
print(f&quot;Actual:   {counter}&quot;)   # likely LESS than 500000 — data race!
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) Deadlock example + fix (死锁示例及修复)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

lock_a = threading.Lock()
lock_b = threading.Lock()

# ─── DEADLOCK version ────────────────────────────────
def thread1_deadlock():
    with lock_a:
        import time; time.sleep(0.1)
        with lock_b:                  # waits for lock_b
            print(&quot;T1: got both locks&quot;)

def thread2_deadlock():
    with lock_b:
        import time; time.sleep(0.1)
        with lock_a:                  # waits for lock_a → DEADLOCK
            print(&quot;T2: got both locks&quot;)

# ─── FIXED version: always acquire locks in the same order ──
def thread1_safe():
    with lock_a:                      # acquire A first
        with lock_b:                  # then B
            print(&quot;T1 safe: got both locks&quot;)

def thread2_safe():
    with lock_a:                      # acquire A first (same order!)
        with lock_b:
            print(&quot;T2 safe: got both locks&quot;)

t1 = threading.Thread(target=thread1_safe)
t2 = threading.Thread(target=thread2_safe)
t1.start(); t2.start()
t1.join();  t2.join()
# → T1 safe: got both locks
# → T2 safe: got both locks
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) Thread-safe singleton (线程安全单例)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

class Singleton:
    _instance = None
    _lock     = threading.Lock()

    def __new__(cls):
        if cls._instance is None:              # first check (no lock)
            with cls._lock:
                if cls._instance is None:      # second check (with lock)
                    cls._instance = super().__new__(cls)
                    print(&quot;Singleton created&quot;)
        return cls._instance

def get_instance():
    s = Singleton()
    print(f&quot;Got instance: {id(s)}&quot;)

threads = [threading.Thread(target=get_instance) for _ in range(5)]
for t in threads: t.start()
for t in threads: t.join()
# → Singleton created   (exactly once)
# → Got instance: 140...  (same id for all 5 threads)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;14. Full API Quick Reference (API速查表)&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Class / Function&lt;/th&gt;
&lt;th&gt;Key Methods&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Thread&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;start()&lt;/code&gt; &lt;code&gt;join()&lt;/code&gt; &lt;code&gt;is_alive()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create and manage threads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Lock&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;acquire()&lt;/code&gt; &lt;code&gt;release()&lt;/code&gt; &lt;code&gt;locked()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mutual exclusion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;RLock&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;acquire()&lt;/code&gt; &lt;code&gt;release()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reentrant mutual exclusion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Condition&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;wait()&lt;/code&gt; &lt;code&gt;wait_for()&lt;/code&gt; &lt;code&gt;notify()&lt;/code&gt; &lt;code&gt;notify_all()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Wait/notify synchronization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Semaphore&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;acquire()&lt;/code&gt; &lt;code&gt;release()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Limit concurrent access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;BoundedSemaphore&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;acquire()&lt;/code&gt; &lt;code&gt;release()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Semaphore with over-release guard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Event&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;set()&lt;/code&gt; &lt;code&gt;clear()&lt;/code&gt; &lt;code&gt;wait()&lt;/code&gt; &lt;code&gt;is_set()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Boolean flag signaling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Timer&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;start()&lt;/code&gt; &lt;code&gt;cancel()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delayed / cancellable execution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Barrier&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;wait()&lt;/code&gt; &lt;code&gt;abort()&lt;/code&gt; &lt;code&gt;reset()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;N-thread rendezvous point&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;local&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;attribute access&lt;/td&gt;
&lt;td&gt;Per-thread storage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Queue&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;put()&lt;/code&gt; &lt;code&gt;get()&lt;/code&gt; &lt;code&gt;task_done()&lt;/code&gt; &lt;code&gt;join()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Thread-safe FIFO queue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;LifoQueue&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;put()&lt;/code&gt; &lt;code&gt;get()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Thread-safe stack&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;PriorityQueue&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;put()&lt;/code&gt; &lt;code&gt;get()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Thread-safe priority queue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;ThreadPoolExecutor&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;submit()&lt;/code&gt; &lt;code&gt;map()&lt;/code&gt; &lt;code&gt;shutdown()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;High-level thread pool&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;current_thread()&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Get current Thread object&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;active_count()&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Count live threads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;enumerate()&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;List all live threads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;excepthook&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Handle uncaught thread exceptions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt; Python threading excels at &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;IO-bound concurrency (IO密集型并发)&amp;lt;/span&amp;gt;: use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;ThreadPoolExecutor&amp;lt;/code&amp;gt; for simple task pools, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Queue&amp;lt;/code&amp;gt; for producer-consumer pipelines, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Lock&amp;lt;/code&amp;gt;/&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;RLock&amp;lt;/code&amp;gt; for shared state, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Event&amp;lt;/code&amp;gt; for signaling, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Semaphore&amp;lt;/code&amp;gt; for resource pools, and &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Barrier&amp;lt;/code&amp;gt; for multi-phase synchronization — always protect shared mutable state to avoid &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Race Conditions (竞态条件)&amp;lt;/span&amp;gt; and &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Deadlocks (死锁)&amp;lt;/span&amp;gt;. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;&lt;strong&gt;I. Python Multithreading — Complete API Reference Manual&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Python&apos;s &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;threading&amp;lt;/span&amp;gt; module provides a high-level interface for &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Multithreading (多线程编程)&amp;lt;/span&amp;gt; built on top of the lower-level &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;_thread&amp;lt;/code&amp;gt; module. Because of the &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;GIL (Global Interpreter Lock, 全局解释器锁)&amp;lt;/span&amp;gt;, threads do not achieve true CPU parallelism for pure Python code — but they excel at &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;IO-bound tasks (IO密集型任务)&amp;lt;/span&amp;gt; such as network requests, file operations, and database calls. This manual covers every public API with runnable examples. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1. Thread — Core Thread Object (核心线程对象)&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;threading.Thread&amp;lt;/code&amp;gt; is the fundamental building block. A thread can be created by passing a &amp;lt;strong&amp;gt;callable target&amp;lt;/strong&amp;gt; or by &amp;lt;strong&amp;gt;subclassing&amp;lt;/strong&amp;gt; and overriding &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;run()&amp;lt;/code&amp;gt;. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;1) Constructor (构造函数)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;threading.Thread(
    group=None,      # reserved, always None
    target=None,     # callable to run in thread
    name=None,       # thread name string
    args=(),         # positional args tuple for target
    kwargs=None,     # keyword args dict for target
    daemon=None      # True → daemon thread (守护线程)
)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) &lt;code&gt;Thread.start()&lt;/code&gt; — Launch the thread&lt;/h3&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;Schedules&amp;lt;/span&amp;gt; the thread for execution. Must be called exactly once per Thread object.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import threading
import time

def worker(name, delay):
    time.sleep(delay)
    print(f&quot;[{name}] finished after {delay}s&quot;)

t1 = threading.Thread(target=worker, args=(&quot;Alpha&quot;, 1))
t2 = threading.Thread(target=worker, args=(&quot;Beta&quot;,  2))

t1.start()   # ← launches t1
t2.start()   # ← launches t2 concurrently

print(&quot;Main thread continues immediately&quot;)
# Output order (non-deterministic):
# Main thread continues immediately
# [Alpha] finished after 1s
# [Beta]  finished after 2s
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Calling &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;start()&amp;lt;/code&amp;gt; twice on the same Thread raises &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;RuntimeError&amp;lt;/code&amp;gt;.&amp;lt;/span&amp;gt; If you need to rerun a task, create a new Thread instance.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;3) &lt;code&gt;Thread.join(timeout=None)&lt;/code&gt; — Wait for completion (等待线程结束)&lt;/h3&gt;
&lt;p&gt;Blocks the calling thread until the target thread terminates, or until &lt;code&gt;timeout&lt;/code&gt; seconds elapse.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import threading, time

def slow_task():
    print(&quot;Task started&quot;)
    time.sleep(3)
    print(&quot;Task done&quot;)

t = threading.Thread(target=slow_task)
t.start()

t.join(timeout=5)   # wait up to 5 seconds

if t.is_alive():
    print(&quot;Thread still running after timeout!&quot;)
else:
    print(&quot;Thread completed successfully&quot;)
# → Task started
# → Task done
# → Thread completed successfully
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4) &lt;code&gt;Thread.is_alive()&lt;/code&gt; — Check thread status (检查线程状态)&lt;/h3&gt;
&lt;p&gt;Returns &lt;code&gt;True&lt;/code&gt; between &lt;code&gt;start()&lt;/code&gt; and thread termination.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import threading, time

def task():
    time.sleep(2)

t = threading.Thread(target=task)
print(t.is_alive())   # → False  (not started yet)
t.start()
print(t.is_alive())   # → True   (running)
t.join()
print(t.is_alive())   # → False  (terminated)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5) &lt;code&gt;Thread.name&lt;/code&gt; / &lt;code&gt;Thread.getName()&lt;/code&gt; / &lt;code&gt;Thread.setName()&lt;/code&gt; — Thread name (线程名)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

def task():
    # Access name inside the thread
    print(f&quot;Running as: {threading.current_thread().name}&quot;)

t = threading.Thread(target=task, name=&quot;WorkerThread-1&quot;)
print(t.name)          # → WorkerThread-1
t.setName(&quot;Renamed&quot;)
print(t.getName())     # → Renamed
t.start()
t.join()
# → Running as: Renamed
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6) &lt;code&gt;Thread.daemon&lt;/code&gt; — Daemon threads (守护线程)&lt;/h3&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;A daemon thread is automatically killed when ALL non-daemon threads exit — it does NOT block program shutdown.&amp;lt;/span&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import threading, time

def background_monitor():
    while True:
        print(&quot;[Monitor] heartbeat&quot;)
        time.sleep(1)

# Must set daemon BEFORE start()
monitor = threading.Thread(target=background_monitor, daemon=True)
monitor.start()

print(&quot;Main: doing work&quot;)
time.sleep(2.5)
print(&quot;Main: exiting — monitor will be killed automatically&quot;)
# → [Monitor] heartbeat
# → Main: doing work
# → [Monitor] heartbeat
# → [Monitor] heartbeat
# → Main: exiting — monitor will be killed automatically
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;7) &lt;code&gt;Thread.ident&lt;/code&gt; / &lt;code&gt;Thread.native_id&lt;/code&gt; — Thread identifiers (线程标识符)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

def show_ids():
    t = threading.current_thread()
    print(f&quot;ident={t.ident}, native_id={t.native_id}&quot;)

t = threading.Thread(target=show_ids)
t.start()
t.join()
# → ident=140234567890, native_id=12345

print(f&quot;Main ident: {threading.main_thread().ident}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;8) Subclass Pattern — Override &lt;code&gt;run()&lt;/code&gt; (子类模式)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading, time

class DownloadThread(threading.Thread):
    &quot;&quot;&quot;Custom thread that downloads a resource.&quot;&quot;&quot;

    def __init__(self, url: str):
        super().__init__(name=f&quot;Download-{url}&quot;)
        self.url    = url
        self.result = None

    def run(self):
        # Simulate download
        time.sleep(0.5)
        self.result = f&quot;&amp;lt;html from {self.url}&amp;gt;&quot;
        print(f&quot;Downloaded: {self.url}&quot;)

threads = [DownloadThread(f&quot;http://example.com/page{i}&quot;) for i in range(3)]

for t in threads:
    t.start()

for t in threads:
    t.join()
    print(f&quot;Result: {t.result}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Lock — Mutual Exclusion (互斥锁)&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; A &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Lock (互斥锁)&amp;lt;/span&amp;gt; ensures only ONE thread accesses a critical section (临界区) at a time. It has two states: &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;locked&amp;lt;/span&amp;gt; and &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;unlocked&amp;lt;/span&amp;gt;. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;1) &lt;code&gt;Lock.acquire(blocking=True, timeout=-1)&lt;/code&gt; / &lt;code&gt;Lock.release()&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

counter = 0
lock    = threading.Lock()

def increment(n):
    global counter
    for _ in range(n):
        lock.acquire()     # ← blocks until lock is free
        counter += 1       # critical section (临界区)
        lock.release()     # ← always release!

threads = [threading.Thread(target=increment, args=(100_000,)) for _ in range(5)]
for t in threads: t.start()
for t in threads: t.join()

print(f&quot;Counter: {counter}&quot;)   # → Counter: 500000  (always correct)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Never call &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;release()&amp;lt;/code&amp;gt; without a matching &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;acquire()&amp;lt;/code&amp;gt; — raises &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;RuntimeError&amp;lt;/code&amp;gt;. Always prefer the &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;with&amp;lt;/code&amp;gt; context manager to guarantee release on exceptions.&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;2) Context Manager — &lt;code&gt;with lock&lt;/code&gt; (上下文管理器)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

shared_list = []
lock = threading.Lock()

def safe_append(value):
    with lock:                     # ← acquire on entry, release on exit (even on exception)
        shared_list.append(value)

threads = [threading.Thread(target=safe_append, args=(i,)) for i in range(10)]
for t in threads: t.start()
for t in threads: t.join()

print(sorted(shared_list))   # → [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) &lt;code&gt;Lock.acquire(blocking=False)&lt;/code&gt; — Non-blocking try (非阻塞尝试)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading, time

lock = threading.Lock()

def try_lock(name):
    acquired = lock.acquire(blocking=False)
    if acquired:
        print(f&quot;[{name}] acquired the lock&quot;)
        time.sleep(2)
        lock.release()
    else:
        print(f&quot;[{name}] could not acquire — skipping&quot;)

t1 = threading.Thread(target=try_lock, args=(&quot;T1&quot;,))
t2 = threading.Thread(target=try_lock, args=(&quot;T2&quot;,))
t1.start(); t2.start()
t1.join();  t2.join()
# → [T1] acquired the lock
# → [T2] could not acquire — skipping
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4) &lt;code&gt;Lock.acquire(timeout=N)&lt;/code&gt; — Timed wait (超时等待)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading, time

lock = threading.Lock()
lock.acquire()   # pre-lock it

def worker():
    result = lock.acquire(timeout=1.5)   # wait max 1.5s
    if result:
        print(&quot;Got the lock&quot;)
        lock.release()
    else:
        print(&quot;Timed out waiting for lock&quot;)

t = threading.Thread(target=worker)
t.start()
t.join()
# → Timed out waiting for lock   (lock was never released)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5) &lt;code&gt;Lock.locked()&lt;/code&gt; — Query state (查询状态)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

lock = threading.Lock()
print(lock.locked())   # → False

lock.acquire()
print(lock.locked())   # → True

lock.release()
print(lock.locked())   # → False
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;3. RLock — Reentrant Lock (可重入锁)&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; A &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;RLock (可重入锁)&amp;lt;/span&amp;gt; can be acquired multiple times by the &amp;lt;em&amp;gt;same thread&amp;lt;/em&amp;gt; without deadlocking. It tracks an internal &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;recursion count (递归计数)&amp;lt;/span&amp;gt; — the lock is only released when the count reaches zero. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;1) Basic RLock usage&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

rlock = threading.RLock()

def outer():
    with rlock:                   # recursion count → 1
        print(&quot;outer acquired&quot;)
        inner()                   # same thread acquires again
        print(&quot;outer releasing&quot;)
    # recursion count → 0 (fully released)

def inner():
    with rlock:                   # recursion count → 2
        print(&quot;inner acquired&quot;)
    # recursion count → 1

t = threading.Thread(target=outer)
t.start(); t.join()
# → outer acquired
# → inner acquired
# → outer releasing
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;A plain &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Lock&amp;lt;/code&amp;gt; would DEADLOCK in the above pattern&amp;lt;/span&amp;gt; because the same thread tries to acquire an already-locked lock. Use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;RLock&amp;lt;/code&amp;gt; whenever a method holding the lock may call another method that also needs the lock.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;2) RLock in a class (类中使用RLock)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

class BankAccount:
    def __init__(self, balance: float):
        self.balance = balance
        self._lock   = threading.RLock()

    def deposit(self, amount: float):
        with self._lock:
            self.balance += amount
            print(f&quot;Deposited {amount:.2f} → balance={self.balance:.2f}&quot;)

    def withdraw(self, amount: float):
        with self._lock:
            self.balance -= amount
            print(f&quot;Withdrew  {amount:.2f} → balance={self.balance:.2f}&quot;)

    def transfer_in(self, amount: float):
        with self._lock:            # outer acquire
            self.deposit(amount)   # inner acquire (reentrant!)
            print(f&quot;Transfer complete&quot;)

account = BankAccount(1000.0)
t = threading.Thread(target=account.transfer_in, args=(250.0,))
t.start(); t.join()
# → Deposited 250.00 → balance=1250.00
# → Transfer complete
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;4. Condition — Wait/Notify Pattern (条件变量)&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; A &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Condition (条件变量)&amp;lt;/span&amp;gt; allows threads to &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;wait&amp;lt;/span&amp;gt; for a specific condition to become true and &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;notify&amp;lt;/span&amp;gt; other threads when it does. It wraps an underlying lock. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;1) &lt;code&gt;Condition.wait()&lt;/code&gt; / &lt;code&gt;notify()&lt;/code&gt; / &lt;code&gt;notify_all()&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading, time, collections

# Classic Producer-Consumer (生产者-消费者) pattern
buffer    = collections.deque()
MAX_SIZE  = 3
condition = threading.Condition()

def producer():
    for i in range(6):
        with condition:
            while len(buffer) &amp;gt;= MAX_SIZE:
                print(f&quot;Producer waiting — buffer full&quot;)
                condition.wait()           # ← releases lock, blocks
            buffer.append(i)
            print(f&quot;Produced {i}  | buffer={list(buffer)}&quot;)
            condition.notify_all()        # ← wake waiting consumers
        time.sleep(0.3)

def consumer(name):
    for _ in range(3):
        with condition:
            while not buffer:
                print(f&quot;[{name}] waiting — buffer empty&quot;)
                condition.wait()           # ← releases lock, blocks
            item = buffer.popleft()
            print(f&quot;[{name}] consumed {item} | buffer={list(buffer)}&quot;)
            condition.notify_all()        # ← wake waiting producer

threads = [
    threading.Thread(target=producer),
    threading.Thread(target=consumer, args=(&quot;C1&quot;,)),
    threading.Thread(target=consumer, args=(&quot;C2&quot;,)),
]
for t in threads: t.start()
for t in threads: t.join()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) &lt;code&gt;Condition.wait(timeout=N)&lt;/code&gt; — Timed wait&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading, time

condition = threading.Condition()
data_ready = False

def waiter():
    with condition:
        result = condition.wait(timeout=2.0)   # wait max 2 seconds
        if result:
            print(&quot;Condition met!&quot;)
        else:
            print(&quot;Timed out — condition never triggered&quot;)

def notifier():
    time.sleep(5)   # too slow
    with condition:
        condition.notify()

t1 = threading.Thread(target=waiter)
t2 = threading.Thread(target=notifier)
t1.start(); t2.start()
t1.join();  t2.join()
# → Timed out — condition never triggered
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) &lt;code&gt;Condition.wait_for(predicate, timeout=None)&lt;/code&gt; — Predicate wait&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading, time

items  = []
cond   = threading.Condition()

def consumer():
    with cond:
        # Block until at least 3 items are available
        cond.wait_for(lambda: len(items) &amp;gt;= 3)
        print(f&quot;Got items: {items}&quot;)

def producer():
    for i in range(5):
        time.sleep(0.5)
        with cond:
            items.append(i)
            print(f&quot;Added item {i}&quot;)
            cond.notify_all()

t1 = threading.Thread(target=consumer)
t2 = threading.Thread(target=producer)
t1.start(); t2.start()
t1.join();  t2.join()
# → Added item 0
# → Added item 1
# → Added item 2
# → Got items: [0, 1, 2]
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;5. Semaphore &amp;amp; BoundedSemaphore (信号量)&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; A &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Semaphore (信号量)&amp;lt;/span&amp;gt; maintains an internal counter. &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;acquire()&amp;lt;/code&amp;gt; decrements it (blocks at zero); &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;release()&amp;lt;/code&amp;gt; increments it. Perfect for limiting concurrent access to a resource pool. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;1) &lt;code&gt;Semaphore(value=1)&lt;/code&gt; — Connection pool simulation (连接池模拟)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading, time, random

# Allow max 3 simultaneous DB connections
db_semaphore = threading.Semaphore(3)

def use_db_connection(thread_id):
    print(f&quot;Thread {thread_id}: waiting for DB connection&quot;)
    with db_semaphore:                      # acquire (count -1)
        print(f&quot;Thread {thread_id}: got connection&quot;)
        time.sleep(random.uniform(0.5, 1.5))
        print(f&quot;Thread {thread_id}: released connection&quot;)
                                            # release (count +1) on exit

threads = [threading.Thread(target=use_db_connection, args=(i,)) for i in range(7)]
for t in threads: t.start()
for t in threads: t.join()
# At most 3 &quot;got connection&quot; lines active at any time
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) &lt;code&gt;BoundedSemaphore&lt;/code&gt; — Prevent over-release (防止超额释放)&lt;/h3&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Warning: a plain &lt;code&gt;Semaphore&lt;/code&gt; allows &lt;code&gt;release()&lt;/code&gt; beyond the initial value — this is usually a bug. &lt;code&gt;BoundedSemaphore&lt;/code&gt; raises &lt;code&gt;ValueError&lt;/code&gt; if the count would exceed the initial value.&amp;lt;/span&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import threading

sem   = threading.Semaphore(2)
bsem  = threading.BoundedSemaphore(2)

# Plain Semaphore — silently over-releases
sem.release()   # count goes to 3 — no error (潜在bug)
print(f&quot;Semaphore value after over-release: OK (silent)&quot;)

# BoundedSemaphore — raises ValueError
try:
    bsem.release()   # count would exceed 2
except ValueError as e:
    print(f&quot;BoundedSemaphore caught: {e}&quot;)
# → BoundedSemaphore caught: Semaphore released too many times
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) Rate limiter pattern (限速器模式)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading, time

# Limit to 2 concurrent API calls
api_semaphore = threading.BoundedSemaphore(2)

def call_api(endpoint):
    with api_semaphore:
        print(f&quot;Calling {endpoint}&quot;)
        time.sleep(1)   # simulate API latency
        print(f&quot;Done    {endpoint}&quot;)

endpoints = [f&quot;/api/resource/{i}&quot; for i in range(6)]
threads   = [threading.Thread(target=call_api, args=(ep,)) for ep in endpoints]

for t in threads: t.start()
for t in threads: t.join()
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;6. Event — Simple Flag Signaling (事件信号)&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; An &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Event (事件)&amp;lt;/span&amp;gt; is a simple boolean flag. Threads can &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;wait()&amp;lt;/code&amp;gt; until the flag is set, and any thread can &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;set()&amp;lt;/code&amp;gt; or &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;clear()&amp;lt;/code&amp;gt; it. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;1) &lt;code&gt;Event.set()&lt;/code&gt; / &lt;code&gt;Event.clear()&lt;/code&gt; / &lt;code&gt;Event.wait()&lt;/code&gt; / &lt;code&gt;Event.is_set()&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading, time

start_event = threading.Event()

def worker(name):
    print(f&quot;[{name}] waiting for start signal...&quot;)
    start_event.wait()               # blocks until event is set
    print(f&quot;[{name}] GO! Starting work&quot;)

workers = [threading.Thread(target=worker, args=(f&quot;W{i}&quot;,)) for i in range(4)]
for w in workers: w.start()

print(&quot;Main: preparing...&quot;)
time.sleep(2)
print(&quot;Main: firing start signal!&quot;)
start_event.set()                    # wake ALL waiting threads at once

for w in workers: w.join()
# → [W0] waiting for start signal...
# → [W1] waiting for start signal...
# → [W2] waiting for start signal...
# → [W3] waiting for start signal...
# (2s pause)
# → Main: firing start signal!
# → [W0] GO! Starting work    (all 4 unblock simultaneously)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) &lt;code&gt;Event.wait(timeout=N)&lt;/code&gt; — Timed wait&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading, time

ready = threading.Event()

def service():
    print(&quot;Service: initializing (takes 3s)...&quot;)
    time.sleep(3)
    ready.set()
    print(&quot;Service: ready!&quot;)

def client():
    if ready.wait(timeout=1.5):    # only wait 1.5s
        print(&quot;Client: connected!&quot;)
    else:
        print(&quot;Client: service not ready in time, aborting&quot;)

t1 = threading.Thread(target=service)
t2 = threading.Thread(target=client)
t1.start(); t2.start()
t1.join();  t2.join()
# → Service: initializing (takes 3s)...
# → Client: service not ready in time, aborting
# → Service: ready!
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) Stop signal pattern (停止信号模式)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading, time

stop_event = threading.Event()

def background_worker():
    count = 0
    while not stop_event.is_set():    # check flag each iteration
        print(f&quot;Working... iteration {count}&quot;)
        count += 1
        time.sleep(0.5)
    print(&quot;Worker: received stop signal, exiting cleanly&quot;)

t = threading.Thread(target=background_worker)
t.start()

time.sleep(2)
print(&quot;Main: sending stop signal&quot;)
stop_event.set()
t.join()
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;7. Timer — Delayed Execution (延迟执行)&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;threading.Timer&amp;lt;/code&amp;gt; is a subclass of &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Thread&amp;lt;/code&amp;gt; that executes a function after a specified delay. It can be &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;cancelled&amp;lt;/span&amp;gt; before firing. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;1) Basic Timer&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

def reminder(message):
    print(f&quot;⏰ Reminder: {message}&quot;)

# Fire after 3 seconds
t = threading.Timer(3.0, reminder, args=(&quot;Meeting at 3pm!&quot;,))
t.start()

print(&quot;Timer set. Waiting...&quot;)
t.join()
# → Timer set. Waiting...
# (3s pause)
# → ⏰ Reminder: Meeting at 3pm!
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) &lt;code&gt;Timer.cancel()&lt;/code&gt; — Cancel before firing&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading, time

fired = False

def action():
    global fired
    fired = True
    print(&quot;Action fired!&quot;)

t = threading.Timer(5.0, action)
t.start()

time.sleep(1)
t.cancel()    # ← cancel within the window
t.join()

print(f&quot;Action fired: {fired}&quot;)   # → Action fired: False
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) Repeating timer pattern (重复定时器模式)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

class RepeatingTimer:
    &quot;&quot;&quot;Fires a function every `interval` seconds.&quot;&quot;&quot;

    def __init__(self, interval: float, func, *args):
        self.interval = interval
        self.func     = func
        self.args     = args
        self._timer   = None
        self._running = False

    def _run(self):
        self.func(*self.args)
        if self._running:
            self._schedule()

    def _schedule(self):
        self._timer = threading.Timer(self.interval, self._run)
        self._timer.daemon = True
        self._timer.start()

    def start(self):
        self._running = True
        self._schedule()

    def stop(self):
        self._running = False
        if self._timer:
            self._timer.cancel()

import time

counter = [0]
def tick():
    counter[0] += 1
    print(f&quot;Tick #{counter[0]}&quot;)

rt = RepeatingTimer(0.5, tick)
rt.start()
time.sleep(2.5)
rt.stop()
print(f&quot;Total ticks: {counter[0]}&quot;)   # → Total ticks: 5
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;8. Barrier — Thread Synchronization Point (屏障同步点)&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; A &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Barrier (屏障)&amp;lt;/span&amp;gt; makes a fixed number of threads wait at a rendezvous point until ALL of them arrive — then releases all of them simultaneously. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;1) &lt;code&gt;Barrier(parties, action=None, timeout=None)&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading, time, random

NUM_WORKERS = 4
barrier = threading.Barrier(NUM_WORKERS)

def phase_worker(name):
    # Phase 1
    duration = random.uniform(0.5, 2.0)
    print(f&quot;[{name}] phase 1 working for {duration:.1f}s&quot;)
    time.sleep(duration)
    print(f&quot;[{name}] phase 1 done — waiting at barrier&quot;)

    barrier.wait()     # ← all threads block here until all 4 arrive

    print(f&quot;[{name}] phase 2 starting (all threads released together)&quot;)

threads = [threading.Thread(target=phase_worker, args=(f&quot;W{i}&quot;,))
           for i in range(NUM_WORKERS)]
for t in threads: t.start()
for t in threads: t.join()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) &lt;code&gt;Barrier&lt;/code&gt; with &lt;code&gt;action&lt;/code&gt; callback&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading, time

def setup_phase():
    &quot;&quot;&quot;Runs ONCE when all threads reach the barrier, before release.&quot;&quot;&quot;
    print(&quot;&amp;gt;&amp;gt;&amp;gt; All threads ready — running barrier action &amp;lt;&amp;lt;&amp;lt;&quot;)

barrier = threading.Barrier(3, action=setup_phase)

def worker(name):
    time.sleep(0.1)
    print(f&quot;[{name}] arrived at barrier&quot;)
    barrier.wait()
    print(f&quot;[{name}] past barrier&quot;)

threads = [threading.Thread(target=worker, args=(f&quot;T{i}&quot;,)) for i in range(3)]
for t in threads: t.start()
for t in threads: t.join()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) &lt;code&gt;Barrier.abort()&lt;/code&gt; / &lt;code&gt;BrokenBarrierError&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading, time

barrier = threading.Barrier(3)

def risky_worker(name, should_abort):
    try:
        if should_abort:
            time.sleep(0.2)
            print(f&quot;[{name}] aborting barrier!&quot;)
            barrier.abort()          # breaks the barrier for everyone
        else:
            print(f&quot;[{name}] waiting at barrier...&quot;)
            barrier.wait(timeout=2)
            print(f&quot;[{name}] passed!&quot;)
    except threading.BrokenBarrierError:
        print(f&quot;[{name}] barrier was broken — handling gracefully&quot;)

threads = [
    threading.Thread(target=risky_worker, args=(&quot;T0&quot;, False)),
    threading.Thread(target=risky_worker, args=(&quot;T1&quot;, False)),
    threading.Thread(target=risky_worker, args=(&quot;T2&quot;, True)),   # aborts
]
for t in threads: t.start()
for t in threads: t.join()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4) Barrier properties&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

b = threading.Barrier(5)
print(b.parties)    # → 5   (total threads needed)
print(b.n_waiting)  # → 0   (currently waiting)
print(b.broken)     # → False
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;9. local — Thread-local Storage (线程本地存储)&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;threading.local()&amp;lt;/code&amp;gt; creates an object where each thread has its &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;own independent copy&amp;lt;/span&amp;gt; of every attribute. Ideal for thread-specific state like database connections or request contexts. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;1) Basic thread-local usage&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

local_data = threading.local()

def worker(value):
    local_data.x = value              # each thread sets its own .x
    import time; time.sleep(0.1)      # let other threads run
    print(f&quot;Thread {threading.current_thread().name}: x = {local_data.x}&quot;)

threads = [threading.Thread(target=worker, args=(i*10,), name=f&quot;T{i}&quot;)
           for i in range(4)]
for t in threads: t.start()
for t in threads: t.join()
# → Thread T0: x = 0
# → Thread T1: x = 10
# → Thread T2: x = 20
# → Thread T3: x = 30
# (each thread sees only its own value — no interference)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) Thread-local DB connection pattern&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading
import sqlite3

_local = threading.local()

def get_connection(db_path: str) -&amp;gt; sqlite3.Connection:
    &quot;&quot;&quot;Return a per-thread DB connection (创建线程私有数据库连接).&quot;&quot;&quot;
    if not hasattr(_local, &quot;conn&quot;):
        _local.conn = sqlite3.connect(db_path)
        print(f&quot;[{threading.current_thread().name}] created new connection&quot;)
    return _local.conn

def db_worker(db_path: str):
    conn = get_connection(db_path)
    conn.execute(&quot;CREATE TABLE IF NOT EXISTS t (v INTEGER)&quot;)
    conn.execute(&quot;INSERT INTO t VALUES (?)&quot;, (threading.get_ident(),))
    conn.commit()
    print(f&quot;[{threading.current_thread().name}] inserted row&quot;)

threads = [threading.Thread(target=db_worker, args=(&quot;:memory:&quot;,), name=f&quot;DB-{i}&quot;)
           for i in range(3)]
for t in threads: t.start()
for t in threads: t.join()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) Subclass &lt;code&gt;local&lt;/code&gt; for initialization&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

class RequestContext(threading.local):
    &quot;&quot;&quot;Thread-local request context with defaults.&quot;&quot;&quot;
    def __init__(self):
        super().__init__()
        self.user_id   = None
        self.request_id = None

ctx = RequestContext()

def handle_request(user_id, req_id):
    ctx.user_id    = user_id
    ctx.request_id = req_id
    import time; time.sleep(0.05)
    print(f&quot;Processing request {ctx.request_id} for user {ctx.user_id}&quot;)

threads = [threading.Thread(target=handle_request, args=(f&quot;user{i}&quot;, f&quot;req-{i:03}&quot;))
           for i in range(4)]
for t in threads: t.start()
for t in threads: t.join()
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;10. Module-level Functions (模块级函数)&lt;/h2&gt;
&lt;h3&gt;1) &lt;code&gt;threading.current_thread()&lt;/code&gt; — Get the current thread&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

def show_self():
    t = threading.current_thread()
    print(f&quot;name={t.name}, ident={t.ident}, daemon={t.daemon}&quot;)

main_t = threading.current_thread()
print(f&quot;Main thread: {main_t.name}&quot;)

t = threading.Thread(target=show_self, name=&quot;MyWorker&quot;)
t.start(); t.join()
# → Main thread: MainThread
# → name=MyWorker, ident=140..., daemon=False
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) &lt;code&gt;threading.main_thread()&lt;/code&gt; — Get the main thread&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

def check_main():
    mt = threading.main_thread()
    ct = threading.current_thread()
    print(f&quot;Main thread: {mt.name}&quot;)
    print(f&quot;This thread: {ct.name}&quot;)
    print(f&quot;Am I main?  {ct is mt}&quot;)

t = threading.Thread(target=check_main)
t.start(); t.join()
# → Main thread: MainThread
# → This thread: Thread-1
# → Am I main?  False
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) &lt;code&gt;threading.active_count()&lt;/code&gt; — Count live threads&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading, time

def slow():
    time.sleep(2)

print(threading.active_count())   # → 1  (main only)

threads = [threading.Thread(target=slow) for _ in range(3)]
for t in threads: t.start()

print(threading.active_count())   # → 4  (main + 3 workers)
for t in threads: t.join()
print(threading.active_count())   # → 1
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4) &lt;code&gt;threading.enumerate()&lt;/code&gt; — List all live threads&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading, time

def task(n):
    time.sleep(n)

threads = [threading.Thread(target=task, args=(i,), name=f&quot;T{i}&quot;) for i in range(1,4)]
for t in threads: t.start()

for t in threading.enumerate():
    print(f&quot;  alive: {t.name} | daemon={t.daemon}&quot;)
# → alive: MainThread | daemon=False
# → alive: T1        | daemon=False
# → alive: T2        | daemon=False
# → alive: T3        | daemon=False

for t in threads: t.join()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5) &lt;code&gt;threading.settrace(func)&lt;/code&gt; / &lt;code&gt;threading.setprofile(func)&lt;/code&gt; — Thread hooks&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading, sys

def my_tracer(frame, event, arg):
    if event == &quot;call&quot;:
        print(f&quot;[TRACE] calling {frame.f_code.co_name}&quot;)
    return my_tracer

def task():
    x = 1 + 1
    return x

threading.settrace(my_tracer)    # set trace for ALL future threads
t = threading.Thread(target=task)
t.start(); t.join()
threading.settrace(None)         # remove tracer
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6) &lt;code&gt;threading.stack_size(size=0)&lt;/code&gt; — Set thread stack size&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

# Set stack size to 512 KB for all future threads
threading.stack_size(512 * 1024)
print(f&quot;Stack size: {threading.stack_size()} bytes&quot;)

def task():
    print(f&quot;Running with custom stack size&quot;)

t = threading.Thread(target=task)
t.start(); t.join()

threading.stack_size(0)   # reset to default
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;7) &lt;code&gt;threading.excepthook&lt;/code&gt; — Handle uncaught thread exceptions (未捕获异常处理)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

def custom_excepthook(args):
    print(f&quot;Uncaught exception in thread [{args.thread.name}]:&quot;)
    print(f&quot;  Type:    {args.exc_type.__name__}&quot;)
    print(f&quot;  Message: {args.exc_value}&quot;)

threading.excepthook = custom_excepthook

def buggy_task():
    raise ValueError(&quot;Something went wrong in thread!&quot;)

t = threading.Thread(target=buggy_task, name=&quot;BuggyThread&quot;)
t.start(); t.join()
# → Uncaught exception in thread [BuggyThread]:
# →   Type:    ValueError
# →   Message: Something went wrong in thread!
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;8) &lt;code&gt;threading.get_ident()&lt;/code&gt; / &lt;code&gt;threading.get_native_id()&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

def show_ids():
    print(f&quot;Python ident:    {threading.get_ident()}&quot;)
    print(f&quot;OS native id:    {threading.get_native_id()}&quot;)

t = threading.Thread(target=show_ids)
t.start(); t.join()
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;11. queue Module — Thread-safe Queues (线程安全队列)&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; The &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;queue&amp;lt;/code&amp;gt; module provides three thread-safe queue classes: &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Queue (FIFO)&amp;lt;/span&amp;gt;, &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;LifoQueue (LIFO/stack)&amp;lt;/span&amp;gt;, and &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;PriorityQueue (优先队列)&amp;lt;/span&amp;gt;. All use internal locks, so no external synchronization is needed. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;1) &lt;code&gt;Queue(maxsize=0)&lt;/code&gt; — FIFO Queue&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from queue import Queue
import threading, time

q = Queue(maxsize=3)

def producer():
    for i in range(6):
        q.put(i)          # blocks if queue is full (maxsize reached)
        print(f&quot;Put {i}  | qsize={q.qsize()}&quot;)
        time.sleep(0.2)

def consumer():
    for _ in range(6):
        item = q.get()    # blocks if queue is empty
        print(f&quot;Got {item}&quot;)
        q.task_done()
        time.sleep(0.5)

t1 = threading.Thread(target=producer)
t2 = threading.Thread(target=consumer)
t1.start(); t2.start()
t1.join();  t2.join()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) &lt;code&gt;Queue.put_nowait()&lt;/code&gt; / &lt;code&gt;Queue.get_nowait()&lt;/code&gt; — Non-blocking&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from queue import Queue, Full, Empty

q = Queue(maxsize=2)
q.put(&quot;item1&quot;)
q.put(&quot;item2&quot;)

try:
    q.put_nowait(&quot;item3&quot;)     # queue full!
except Full:
    print(&quot;Queue full — item3 dropped&quot;)

try:
    while True:
        print(q.get_nowait())
except Empty:
    print(&quot;Queue emptied&quot;)
# → Queue full — item3 dropped
# → item1
# → item2
# → Queue emptied
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) &lt;code&gt;Queue.join()&lt;/code&gt; / &lt;code&gt;Queue.task_done()&lt;/code&gt; — Work tracking&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from queue import Queue
import threading

work_queue = Queue()

def worker():
    while True:
        task = work_queue.get()
        if task is None:
            break
        print(f&quot;Processing: {task}&quot;)
        work_queue.task_done()   # signal this task is complete

# Start 3 workers
workers = [threading.Thread(target=worker, daemon=True) for _ in range(3)]
for w in workers: w.start()

# Enqueue tasks
for task in [&quot;task_A&quot;, &quot;task_B&quot;, &quot;task_C&quot;, &quot;task_D&quot;, &quot;task_E&quot;]:
    work_queue.put(task)

work_queue.join()   # blocks until ALL task_done() called
print(&quot;All tasks completed!&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4) &lt;code&gt;LifoQueue&lt;/code&gt; — Stack (栈/后进先出)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from queue import LifoQueue

stack = LifoQueue()
stack.put(&quot;first&quot;)
stack.put(&quot;second&quot;)
stack.put(&quot;third&quot;)

while not stack.empty():
    print(stack.get())
# → third
# → second
# → first
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5) &lt;code&gt;PriorityQueue&lt;/code&gt; — Priority-based processing (优先级队列)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from queue import PriorityQueue
import threading, time

pq = PriorityQueue()

# (priority, task_name) — lower number = higher priority
pq.put((3, &quot;low-priority task&quot;))
pq.put((1, &quot;URGENT task&quot;))
pq.put((2, &quot;medium task&quot;))
pq.put((1, &quot;another URGENT task&quot;))

while not pq.empty():
    priority, task = pq.get()
    print(f&quot;[priority={priority}] Processing: {task}&quot;)
# → [priority=1] Processing: URGENT task
# → [priority=1] Processing: another URGENT task
# → [priority=2] Processing: medium task
# → [priority=3] Processing: low-priority task
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;12. ThreadPoolExecutor — High-level Thread Pool (高级线程池)&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;concurrent.futures.ThreadPoolExecutor&amp;lt;/code&amp;gt; provides a high-level, &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;Future-based (Future对象)&amp;lt;/span&amp;gt; interface for thread pools. It is the &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;recommended way&amp;lt;/span&amp;gt; to run IO-bound tasks in modern Python. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;1) &lt;code&gt;submit()&lt;/code&gt; → Future&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from concurrent.futures import ThreadPoolExecutor
import time

def fetch_data(url: str) -&amp;gt; str:
    time.sleep(1)   # simulate network call
    return f&quot;&amp;lt;data from {url}&amp;gt;&quot;

urls = [f&quot;http://example.com/page{i}&quot; for i in range(5)]

with ThreadPoolExecutor(max_workers=3) as executor:
    futures = [executor.submit(fetch_data, url) for url in urls]

    for future in futures:
        result = future.result()   # blocks until this future completes
        print(result)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) &lt;code&gt;map()&lt;/code&gt; — Parallel map (并行映射)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from concurrent.futures import ThreadPoolExecutor
import time

def square(n):
    time.sleep(0.2)
    return n * n

with ThreadPoolExecutor(max_workers=4) as executor:
    results = list(executor.map(square, range(10)))

print(results)   # → [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) &lt;code&gt;Future&lt;/code&gt; API — &lt;code&gt;done()&lt;/code&gt;, &lt;code&gt;cancel()&lt;/code&gt;, &lt;code&gt;add_done_callback()&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from concurrent.futures import ThreadPoolExecutor
import time

def slow_task(n):
    time.sleep(n)
    return f&quot;result-{n}&quot;

def on_done(future):
    print(f&quot;Callback: task finished → {future.result()}&quot;)

with ThreadPoolExecutor(max_workers=2) as executor:
    f1 = executor.submit(slow_task, 1)
    f2 = executor.submit(slow_task, 2)

    f1.add_done_callback(on_done)    # register callback
    f2.add_done_callback(on_done)

    print(f&quot;f1 done: {f1.done()}&quot;)   # likely False (still running)
    time.sleep(1.5)
    print(f&quot;f1 done: {f1.done()}&quot;)   # → True
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4) &lt;code&gt;as_completed()&lt;/code&gt; — Process in completion order (按完成顺序处理)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from concurrent.futures import ThreadPoolExecutor, as_completed
import time, random

def task(n):
    delay = random.uniform(0.1, 1.0)
    time.sleep(delay)
    return (n, delay)

with ThreadPoolExecutor(max_workers=5) as executor:
    futures = {executor.submit(task, i): i for i in range(8)}

    for future in as_completed(futures):
        task_id = futures[future]
        n, delay = future.result()
        print(f&quot;Task {n} finished in {delay:.2f}s&quot;)
# Tasks print in the order they complete, not submission order
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5) Exception handling in futures (Future异常处理)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from concurrent.futures import ThreadPoolExecutor

def risky(x):
    if x == 3:
        raise ValueError(f&quot;Bad input: {x}&quot;)
    return x * 2

with ThreadPoolExecutor(max_workers=2) as executor:
    futures = [executor.submit(risky, i) for i in range(5)]

for i, f in enumerate(futures):
    try:
        print(f&quot;Result {i}: {f.result()}&quot;)
    except ValueError as e:
        print(f&quot;Result {i}: ERROR — {e}&quot;)
# → Result 0: 0
# → Result 1: 2
# → Result 2: 4
# → Result 3: ERROR — Bad input: 3
# → Result 4: 8
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;13. Common Patterns &amp;amp; Pitfalls (常见模式与陷阱)&lt;/h2&gt;
&lt;h3&gt;1) Race condition example (竞态条件示例)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

counter = 0   # UNSAFE shared state

def unsafe_increment():
    global counter
    for _ in range(100_000):
        counter += 1   # NOT atomic! (read-modify-write)

threads = [threading.Thread(target=unsafe_increment) for _ in range(5)]
for t in threads: t.start()
for t in threads: t.join()

print(f&quot;Expected: 500000&quot;)
print(f&quot;Actual:   {counter}&quot;)   # likely LESS than 500000 — data race!
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) Deadlock example + fix (死锁示例及修复)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

lock_a = threading.Lock()
lock_b = threading.Lock()

# ─── DEADLOCK version ────────────────────────────────
def thread1_deadlock():
    with lock_a:
        import time; time.sleep(0.1)
        with lock_b:                  # waits for lock_b
            print(&quot;T1: got both locks&quot;)

def thread2_deadlock():
    with lock_b:
        import time; time.sleep(0.1)
        with lock_a:                  # waits for lock_a → DEADLOCK
            print(&quot;T2: got both locks&quot;)

# ─── FIXED version: always acquire locks in the same order ──
def thread1_safe():
    with lock_a:                      # acquire A first
        with lock_b:                  # then B
            print(&quot;T1 safe: got both locks&quot;)

def thread2_safe():
    with lock_a:                      # acquire A first (same order!)
        with lock_b:
            print(&quot;T2 safe: got both locks&quot;)

t1 = threading.Thread(target=thread1_safe)
t2 = threading.Thread(target=thread2_safe)
t1.start(); t2.start()
t1.join();  t2.join()
# → T1 safe: got both locks
# → T2 safe: got both locks
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) Thread-safe singleton (线程安全单例)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import threading

class Singleton:
    _instance = None
    _lock     = threading.Lock()

    def __new__(cls):
        if cls._instance is None:              # first check (no lock)
            with cls._lock:
                if cls._instance is None:      # second check (with lock)
                    cls._instance = super().__new__(cls)
                    print(&quot;Singleton created&quot;)
        return cls._instance

def get_instance():
    s = Singleton()
    print(f&quot;Got instance: {id(s)}&quot;)

threads = [threading.Thread(target=get_instance) for _ in range(5)]
for t in threads: t.start()
for t in threads: t.join()
# → Singleton created   (exactly once)
# → Got instance: 140...  (same id for all 5 threads)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;14. Full API Quick Reference (API速查表)&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Class / Function&lt;/th&gt;
&lt;th&gt;Key Methods&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Thread&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;start()&lt;/code&gt; &lt;code&gt;join()&lt;/code&gt; &lt;code&gt;is_alive()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create and manage threads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Lock&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;acquire()&lt;/code&gt; &lt;code&gt;release()&lt;/code&gt; &lt;code&gt;locked()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mutual exclusion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;RLock&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;acquire()&lt;/code&gt; &lt;code&gt;release()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reentrant mutual exclusion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Condition&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;wait()&lt;/code&gt; &lt;code&gt;wait_for()&lt;/code&gt; &lt;code&gt;notify()&lt;/code&gt; &lt;code&gt;notify_all()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Wait/notify synchronization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Semaphore&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;acquire()&lt;/code&gt; &lt;code&gt;release()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Limit concurrent access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;BoundedSemaphore&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;acquire()&lt;/code&gt; &lt;code&gt;release()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Semaphore with over-release guard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Event&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;set()&lt;/code&gt; &lt;code&gt;clear()&lt;/code&gt; &lt;code&gt;wait()&lt;/code&gt; &lt;code&gt;is_set()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Boolean flag signaling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Timer&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;start()&lt;/code&gt; &lt;code&gt;cancel()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delayed / cancellable execution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Barrier&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;wait()&lt;/code&gt; &lt;code&gt;abort()&lt;/code&gt; &lt;code&gt;reset()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;N-thread rendezvous point&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;local&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;attribute access&lt;/td&gt;
&lt;td&gt;Per-thread storage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Queue&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;put()&lt;/code&gt; &lt;code&gt;get()&lt;/code&gt; &lt;code&gt;task_done()&lt;/code&gt; &lt;code&gt;join()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Thread-safe FIFO queue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;LifoQueue&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;put()&lt;/code&gt; &lt;code&gt;get()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Thread-safe stack&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;PriorityQueue&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;put()&lt;/code&gt; &lt;code&gt;get()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Thread-safe priority queue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;ThreadPoolExecutor&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;submit()&lt;/code&gt; &lt;code&gt;map()&lt;/code&gt; &lt;code&gt;shutdown()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;High-level thread pool&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;current_thread()&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Get current Thread object&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;active_count()&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Count live threads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;enumerate()&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;List all live threads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;excepthook&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Handle uncaught thread exceptions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt; Python threading excels at &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;IO-bound concurrency (IO密集型并发)&amp;lt;/span&amp;gt;: use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;ThreadPoolExecutor&amp;lt;/code&amp;gt; for simple task pools, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Queue&amp;lt;/code&amp;gt; for producer-consumer pipelines, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Lock&amp;lt;/code&amp;gt;/&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;RLock&amp;lt;/code&amp;gt; for shared state, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Event&amp;lt;/code&amp;gt; for signaling, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Semaphore&amp;lt;/code&amp;gt; for resource pools, and &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Barrier&amp;lt;/code&amp;gt; for multi-phase synchronization — always protect shared mutable state to avoid &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Race Conditions (竞态条件)&amp;lt;/span&amp;gt; and &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Deadlocks (死锁)&amp;lt;/span&amp;gt;. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;&lt;strong&gt;II. When to Use Each API — Scenario Decision Guide (使用场景决策指南)&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; Choosing the wrong synchronization primitive is a common source of bugs, deadlocks, and poor performance. This chapter maps every threading API to its &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;concrete real-world scenarios&amp;lt;/span&amp;gt;, explains the &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;decision logic&amp;lt;/span&amp;gt; behind each choice, and provides a final &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Decision Flowchart (决策流程图)&amp;lt;/span&amp;gt; for quick lookup. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1. Thread — When to create raw threads (何时创建原始线程)&lt;/h2&gt;
&lt;h3&gt;1) ✅ Use &lt;code&gt;Thread&lt;/code&gt; directly when&lt;/h3&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;1.&amp;lt;/span&amp;gt; You need &lt;strong&gt;full lifecycle control&lt;/strong&gt; — start, monitor, join at precise moments. &amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;2.&amp;lt;/span&amp;gt; The thread has &lt;strong&gt;long-running, stateful logic&lt;/strong&gt; best expressed as a class with &lt;code&gt;run()&lt;/code&gt;. &amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;3.&amp;lt;/span&amp;gt; You need to store a &lt;strong&gt;result on the thread object&lt;/strong&gt; itself (&lt;code&gt;self.result = ...&lt;/code&gt;). &amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;4.&amp;lt;/span&amp;gt; You&apos;re building a &lt;strong&gt;daemon background service&lt;/strong&gt; (heartbeat, log flusher, monitor).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# ✅ Scenario: long-lived stateful background service
import threading, time

class HeartbeatThread(threading.Thread):
    &quot;&quot;&quot;Sends periodic heartbeats to a server.&quot;&quot;&quot;
    def __init__(self, server_url, interval=5):
        super().__init__(daemon=True, name=&quot;Heartbeat&quot;)
        self.server_url = server_url
        self.interval   = interval
        self._stop      = threading.Event()

    def run(self):
        while not self._stop.is_set():
            print(f&quot;[Heartbeat] ping → {self.server_url}&quot;)
            time.sleep(self.interval)

    def stop(self):
        self._stop.set()

hb = HeartbeatThread(&quot;http://api.example.com/health&quot;)
hb.start()
time.sleep(12)
hb.stop()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) ❌ Do NOT use raw &lt;code&gt;Thread&lt;/code&gt; when&lt;/h3&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;× You just need to run many short tasks in parallel → use &lt;code&gt;ThreadPoolExecutor&lt;/code&gt; instead.&amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;× You need return values from many tasks → &lt;code&gt;Future.result()&lt;/code&gt; is cleaner than &lt;code&gt;t.result&lt;/code&gt;.&amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;× You need CPU parallelism → use &lt;code&gt;multiprocessing&lt;/code&gt; (GIL blocks true parallelism).&amp;lt;/span&amp;gt;&lt;/p&gt;
&lt;h3&gt;3) &lt;code&gt;daemon=True&lt;/code&gt; — Specifically when&lt;/h3&gt;
&lt;p&gt;Use daemon threads for tasks that should &lt;strong&gt;not keep the program alive&lt;/strong&gt; if the main thread exits:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;&lt;code&gt;daemon=True&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;daemon=False&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Background log flusher&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Health monitor / watchdog&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Worker that must finish&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DB write that must commit&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre&gt;&lt;code&gt;# ✅ Scenario: log flusher that should die with the app
import threading, time

log_buffer = []

def flush_logs():
    while True:
        if log_buffer:
            print(f&quot;[Flush] writing {len(log_buffer)} log entries&quot;)
            log_buffer.clear()
        time.sleep(1)

flusher = threading.Thread(target=flush_logs, daemon=True)
flusher.start()

# Main thread does work, flusher auto-dies when main exits
for i in range(5):
    log_buffer.append(f&quot;event-{i}&quot;)
    time.sleep(0.5)
print(&quot;Main done — flusher daemon killed automatically&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Lock — When to use mutual exclusion (何时使用互斥锁)&lt;/h2&gt;
&lt;h3&gt;1) ✅ Use &lt;code&gt;Lock&lt;/code&gt; when&lt;/h3&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;1.&amp;lt;/span&amp;gt; Multiple threads &lt;strong&gt;read AND write&lt;/strong&gt; the same variable / data structure. &amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;2.&amp;lt;/span&amp;gt; An operation that looks atomic is actually &lt;strong&gt;read-modify-write&lt;/strong&gt; (e.g. &lt;code&gt;counter += 1&lt;/code&gt;). &amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;3.&amp;lt;/span&amp;gt; You&apos;re updating a &lt;strong&gt;shared list, dict, or custom object&lt;/strong&gt;. &amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;4.&amp;lt;/span&amp;gt; You need to protect a &lt;strong&gt;file write&lt;/strong&gt; or &lt;strong&gt;database update&lt;/strong&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# ✅ Scenario: shared bank account balance — MUST use Lock
import threading

class Account:
    def __init__(self, balance):
        self.balance = balance
        self._lock   = threading.Lock()

    def transfer(self, amount):
        with self._lock:                  # critical section
            if self.balance &amp;gt;= amount:
                time.sleep(0.001)         # simulate DB latency
                self.balance -= amount
                return True
            return False

import time
acc     = Account(1000)
results = []

def try_withdraw():
    results.append(acc.transfer(100))

threads = [threading.Thread(target=try_withdraw) for _ in range(20)]
for t in threads: t.start()
for t in threads: t.join()

print(f&quot;Balance: {acc.balance}&quot;)         # always ≥ 0
print(f&quot;Successful: {results.count(True)}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) ❌ Do NOT use &lt;code&gt;Lock&lt;/code&gt; when&lt;/h3&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;× The same thread needs to acquire the lock twice → use &lt;code&gt;RLock&lt;/code&gt; instead (plain Lock deadlocks).&amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;× You need to wait for a condition, not just exclusive access → use &lt;code&gt;Condition&lt;/code&gt;.&amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;× You only need to limit concurrency to N &amp;gt; 1 → use &lt;code&gt;Semaphore&lt;/code&gt;.&amp;lt;/span&amp;gt;&lt;/p&gt;
&lt;h3&gt;3) Scenario matrix (场景矩阵)&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;Correct primitive&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1 thread at a time, non-reentrant&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Lock&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1 thread at a time, same thread re-acquires&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;RLock&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;N threads at a time&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Semaphore(N)&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wait until data is ready&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Condition&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One-time go signal&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Event&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;3. RLock — When re-entrancy is needed (何时需要可重入锁)&lt;/h2&gt;
&lt;h3&gt;1) ✅ Use &lt;code&gt;RLock&lt;/code&gt; when&lt;/h3&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;1.&amp;lt;/span&amp;gt; A method holding the lock &lt;strong&gt;calls another method that also acquires the same lock&lt;/strong&gt;. &amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;2.&amp;lt;/span&amp;gt; You&apos;re building a &lt;strong&gt;class with multiple synchronized methods&lt;/strong&gt; that call each other. &amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;3.&amp;lt;/span&amp;gt; You have &lt;strong&gt;recursive algorithms&lt;/strong&gt; that need locking at each level.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# ✅ Scenario: tree traversal where each node uses the same lock
import threading

class SafeTree:
    def __init__(self, value, children=None):
        self.value    = value
        self.children = children or []
        self._lock    = threading.RLock()

    def sum_values(self):
        with self._lock:                        # acquire (depth 1)
            total = self.value
            for child in self.children:
                total += child.sum_values()     # same lock, deeper (depth 2+)
            return total

tree = SafeTree(1, [SafeTree(2), SafeTree(3, [SafeTree(4)])])
t = threading.Thread(target=lambda: print(f&quot;Sum: {tree.sum_values()}&quot;))
t.start(); t.join()
# → Sum: 10
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) ❌ Do NOT use &lt;code&gt;RLock&lt;/code&gt; when&lt;/h3&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;× Methods don&apos;t call each other — a plain &lt;code&gt;Lock&lt;/code&gt; has slightly lower overhead.&amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;× You want to detect accidental re-entry as a bug — &lt;code&gt;Lock&lt;/code&gt; will surface it as a deadlock.&amp;lt;/span&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4. Condition — When threads must wait for state changes (何时等待状态变化)&lt;/h2&gt;
&lt;h3&gt;1) ✅ Use &lt;code&gt;Condition&lt;/code&gt; when&lt;/h3&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;1.&amp;lt;/span&amp;gt; One thread must &lt;strong&gt;wait until another thread changes some data&lt;/strong&gt; (not just unlocks). &amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;2.&amp;lt;/span&amp;gt; Implementing &lt;strong&gt;producer-consumer&lt;/strong&gt; patterns with a bounded buffer. &amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;3.&amp;lt;/span&amp;gt; Threads need to &lt;strong&gt;coordinate in phases&lt;/strong&gt; — e.g., &quot;wait until queue has ≥ 3 items&quot;. &amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;4.&amp;lt;/span&amp;gt; You need &lt;strong&gt;selective wakeup&lt;/strong&gt; — notify only one waiter vs. all waiters.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# ✅ Scenario: order fulfillment system
# Orders must wait until inventory is restocked
import threading, time, collections

inventory = collections.defaultdict(int)
cond      = threading.Condition()

def restock_worker():
    items = [(&quot;apple&quot;, 50), (&quot;banana&quot;, 30), (&quot;cherry&quot;, 20)]
    for item, qty in items:
        time.sleep(1)
        with cond:
            inventory[item] += qty
            print(f&quot;[Restock] {item} +{qty} → total={inventory[item]}&quot;)
            cond.notify_all()   # wake all waiting orders

def process_order(order_id, item, qty):
    with cond:
        cond.wait_for(lambda: inventory[item] &amp;gt;= qty)  # wait for stock
        inventory[item] -= qty
        print(f&quot;[Order {order_id}] filled {qty}x {item} → remaining={inventory[item]}&quot;)

threads = [
    threading.Thread(target=restock_worker),
    threading.Thread(target=process_order, args=(1, &quot;apple&quot;,  20)),
    threading.Thread(target=process_order, args=(2, &quot;banana&quot;, 15)),
    threading.Thread(target=process_order, args=(3, &quot;apple&quot;,  40)),
]
for t in threads: t.start()
for t in threads: t.join()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) &lt;code&gt;notify()&lt;/code&gt; vs &lt;code&gt;notify_all()&lt;/code&gt; — When to use which&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Only &lt;strong&gt;one&lt;/strong&gt; consumer can act (e.g. one slot freed)&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;notify()&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;All&lt;/strong&gt; consumers might now be able to proceed&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;notify_all()&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;You added multiple items to the buffer at once&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;notify_all()&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Only one thread is waiting (guaranteed)&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;notify()&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;3) ❌ Do NOT use &lt;code&gt;Condition&lt;/code&gt; when&lt;/h3&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;× You just need a one-time signal → use &lt;code&gt;Event&lt;/code&gt; (simpler API).&amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;× The data flowing between threads is the signal → use &lt;code&gt;Queue&lt;/code&gt; (built-in blocking).&amp;lt;/span&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;5. Semaphore — When limiting concurrent access (何时限制并发访问数量)&lt;/h2&gt;
&lt;h3&gt;1) ✅ Use &lt;code&gt;Semaphore&lt;/code&gt; when&lt;/h3&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;1.&amp;lt;/span&amp;gt; You have a &lt;strong&gt;resource pool&lt;/strong&gt; with a fixed capacity: DB connections, HTTP clients, file handles. &amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;2.&amp;lt;/span&amp;gt; You need &lt;strong&gt;rate limiting&lt;/strong&gt; — at most N concurrent API calls. &amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;3.&amp;lt;/span&amp;gt; Implementing a &lt;strong&gt;thread pool&lt;/strong&gt; from scratch (though &lt;code&gt;ThreadPoolExecutor&lt;/code&gt; is preferred). &amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;4.&amp;lt;/span&amp;gt; A resource requires &lt;strong&gt;N permits&lt;/strong&gt; to use (e.g. a GPU with N memory slots).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# ✅ Scenario: limit concurrent external API calls to avoid 429 Too Many Requests
import threading, time, random

MAX_CONCURRENT = 3
api_semaphore  = threading.BoundedSemaphore(MAX_CONCURRENT)

def call_external_api(request_id):
    print(f&quot;[Req {request_id}] queued&quot;)
    with api_semaphore:
        print(f&quot;[Req {request_id}] calling API...&quot;)
        time.sleep(random.uniform(0.5, 1.5))   # simulate API latency
        print(f&quot;[Req {request_id}] done&quot;)

# Simulate 10 concurrent requests — only 3 run at once
threads = [threading.Thread(target=call_external_api, args=(i,)) for i in range(10)]
for t in threads: t.start()
for t in threads: t.join()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) &lt;code&gt;Semaphore&lt;/code&gt; vs &lt;code&gt;BoundedSemaphore&lt;/code&gt; — When to use which&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Resource pool (connection pool, thread pool)&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;BoundedSemaphore&amp;lt;/code&amp;gt; — prevents logic bugs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Signaling between threads (producer increments)&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Semaphore&amp;lt;/code&amp;gt; — counter can exceed initial&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;You want a runtime error on over-release&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;BoundedSemaphore&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;3) ❌ Do NOT use &lt;code&gt;Semaphore&lt;/code&gt; when&lt;/h3&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;× You only need to allow 1 thread at a time → use &lt;code&gt;Lock&lt;/code&gt; (clearer intent).&amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;× You need workers to process tasks from a queue → use &lt;code&gt;ThreadPoolExecutor&lt;/code&gt;.&amp;lt;/span&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;6. Event — When broadcasting a one-time signal (何时广播一次性信号)&lt;/h2&gt;
&lt;h3&gt;1) ✅ Use &lt;code&gt;Event&lt;/code&gt; when&lt;/h3&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;1.&amp;lt;/span&amp;gt; One thread needs to &lt;strong&gt;signal multiple waiting threads simultaneously&lt;/strong&gt; (broadcast). &amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;2.&amp;lt;/span&amp;gt; Implementing a &lt;strong&gt;start gun&lt;/strong&gt; — all workers blocked until a &quot;ready&quot; signal fires. &amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;3.&amp;lt;/span&amp;gt; A &lt;strong&gt;graceful shutdown&lt;/strong&gt; flag — workers poll &lt;code&gt;stop_event.is_set()&lt;/code&gt; each iteration. &amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;4.&amp;lt;/span&amp;gt; A &lt;strong&gt;service readiness probe&lt;/strong&gt; — clients wait until the server is initialized. &amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;5.&amp;lt;/span&amp;gt; One-shot notifications where the flag &lt;strong&gt;stays set&lt;/strong&gt; permanently after firing.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# ✅ Scenario: web server workers wait for config to load before serving
import threading, time

config_loaded = threading.Event()
config        = {}

def load_config():
    print(&quot;[Config] loading from database...&quot;)
    time.sleep(2)
    config.update({&quot;host&quot;: &quot;0.0.0.0&quot;, &quot;port&quot;: 8080, &quot;debug&quot;: False})
    print(&quot;[Config] loaded!&quot;)
    config_loaded.set()           # broadcast to ALL waiting workers

def request_handler(worker_id):
    config_loaded.wait()          # block until config ready
    print(f&quot;[Worker {worker_id}] serving on {config[&apos;host&apos;]}:{config[&apos;port&apos;]}&quot;)

threads = (
    [threading.Thread(target=load_config)] +
    [threading.Thread(target=request_handler, args=(i,)) for i in range(5)]
)
for t in threads: t.start()
for t in threads: t.join()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) &lt;code&gt;Event&lt;/code&gt; vs &lt;code&gt;Condition&lt;/code&gt; — Decision rule&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Question&lt;/th&gt;
&lt;th&gt;Answer → Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Signal multiple threads with a permanent flag?&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Event&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wait for a data condition that can change repeatedly?&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Condition&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Need to reset and re-arm the signal?&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Event.clear()&amp;lt;/code&amp;gt; or &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Condition&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One producer, many consumers woken at once?&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Event&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;3) ❌ Do NOT use &lt;code&gt;Event&lt;/code&gt; when&lt;/h3&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;× The condition can be true/false multiple times (e.g. buffer empty↔full) → use &lt;code&gt;Condition&lt;/code&gt;.&amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;× You&apos;re passing data along with the signal → use &lt;code&gt;Queue&lt;/code&gt;.&amp;lt;/span&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;7. Timer — When delaying or scheduling execution (何时延迟或定时执行)&lt;/h2&gt;
&lt;h3&gt;1) ✅ Use &lt;code&gt;Timer&lt;/code&gt; when&lt;/h3&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;1.&amp;lt;/span&amp;gt; You need to &lt;strong&gt;run a function once after a delay&lt;/strong&gt;, without blocking the main thread. &amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;2.&amp;lt;/span&amp;gt; The action might need to be &lt;strong&gt;cancelled before it fires&lt;/strong&gt; (e.g. debouncing). &amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;3.&amp;lt;/span&amp;gt; Implementing &lt;strong&gt;timeouts&lt;/strong&gt; for external operations. &amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;4.&amp;lt;/span&amp;gt; &lt;strong&gt;Session expiry&lt;/strong&gt;, cache invalidation, or auto-logout after inactivity.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# ✅ Scenario: debounce user input — only save after 500ms of inactivity
import threading

_save_timer = None

def debounced_save(content):
    global _save_timer
    if _save_timer:
        _save_timer.cancel()     # cancel previous pending save
    _save_timer = threading.Timer(0.5, do_save, args=(content,))
    _save_timer.start()

def do_save(content):
    print(f&quot;[Save] writing: &apos;{content}&apos;&quot;)

# Rapid keystrokes — only the last one saves
import time
debounced_save(&quot;H&quot;)
debounced_save(&quot;He&quot;)
debounced_save(&quot;Hel&quot;)
debounced_save(&quot;Hell&quot;)
time.sleep(0.1)
debounced_save(&quot;Hello&quot;)
time.sleep(0.8)
# → [Save] writing: &apos;Hello&apos;   (only once, after 500ms of quiet)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) ❌ Do NOT use &lt;code&gt;Timer&lt;/code&gt; when&lt;/h3&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;× You need recurring execution → build a RepeatingTimer (see §7.3 in Part I) or use &lt;code&gt;sched&lt;/code&gt;.&amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;× You need sub-millisecond precision — &lt;code&gt;Timer&lt;/code&gt; uses &lt;code&gt;time.sleep()&lt;/code&gt; which is OS-dependent.&amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;× Complex scheduling (cron-like) → use &lt;code&gt;APScheduler&lt;/code&gt; or &lt;code&gt;Celery&lt;/code&gt;.&amp;lt;/span&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;8. Barrier — When threads must synchronize at a checkpoint (何时需要检查点同步)&lt;/h2&gt;
&lt;h3&gt;1) ✅ Use &lt;code&gt;Barrier&lt;/code&gt; when&lt;/h3&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;1.&amp;lt;/span&amp;gt; A computation has &lt;strong&gt;multiple phases&lt;/strong&gt; and ALL threads must finish phase N before ANY starts phase N+1. &amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;2.&amp;lt;/span&amp;gt; &lt;strong&gt;Parallel simulation&lt;/strong&gt; — each timestep must complete across all worker threads before advancing. &amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;3.&amp;lt;/span&amp;gt; &lt;strong&gt;Test synchronization&lt;/strong&gt; — ensure all threads reach a certain point before asserting results. &amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;4.&amp;lt;/span&amp;gt; &lt;strong&gt;Coordinated startup&lt;/strong&gt; — all services initialized before traffic is allowed.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# ✅ Scenario: parallel matrix computation with two phases
import threading, time, random

NUM_WORKERS = 4
barrier     = threading.Barrier(NUM_WORKERS)
partial_results = [0] * NUM_WORKERS
final_results   = [0] * NUM_WORKERS

def compute_worker(worker_id):
    # ── Phase 1: independent computation ──────────────
    time.sleep(random.uniform(0.3, 1.2))
    partial_results[worker_id] = random.randint(10, 100)
    print(f&quot;[W{worker_id}] Phase 1 done: partial={partial_results[worker_id]}&quot;)

    barrier.wait()   # ← ALL workers must finish phase 1 before phase 2

    # ── Phase 2: needs ALL phase-1 results ────────────
    # e.g., normalize by global sum
    total = sum(partial_results)
    final_results[worker_id] = partial_results[worker_id] / total
    print(f&quot;[W{worker_id}] Phase 2 done: final={final_results[worker_id]:.3f}&quot;)

threads = [threading.Thread(target=compute_worker, args=(i,)) for i in range(NUM_WORKERS)]
for t in threads: t.start()
for t in threads: t.join()

print(f&quot;\nFinal results: {[f&apos;{r:.3f}&apos; for r in final_results]}&quot;)
print(f&quot;Sum check: {sum(final_results):.6f}&quot;)   # → ~1.0
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) ❌ Do NOT use &lt;code&gt;Barrier&lt;/code&gt; when&lt;/h3&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;× Thread count is dynamic (unknown at creation time) — Barrier requires a fixed &lt;code&gt;parties&lt;/code&gt; count.&amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;× Only one thread needs to wait for others → use &lt;code&gt;Thread.join()&lt;/code&gt; or &lt;code&gt;Event&lt;/code&gt;.&amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;× Threads have different roles (not symmetric) → use &lt;code&gt;Condition&lt;/code&gt; or &lt;code&gt;Queue&lt;/code&gt;.&amp;lt;/span&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;9. threading.local — When isolating per-thread state (何时隔离线程私有状态)&lt;/h2&gt;
&lt;h3&gt;1) ✅ Use &lt;code&gt;threading.local&lt;/code&gt; when&lt;/h3&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;1.&amp;lt;/span&amp;gt; Each thread needs its &lt;strong&gt;own copy of a connection&lt;/strong&gt; (DB, HTTP session, file handle). &amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;2.&amp;lt;/span&amp;gt; You&apos;re building &lt;strong&gt;middleware or frameworks&lt;/strong&gt; that attach request context per thread. &amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;3.&amp;lt;/span&amp;gt; A global-looking variable must actually be &lt;strong&gt;thread-specific&lt;/strong&gt; (e.g., current user, transaction ID). &amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;4.&amp;lt;/span&amp;gt; Avoiding lock contention by giving each thread &lt;strong&gt;its own cache&lt;/strong&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# ✅ Scenario: per-thread HTTP session (connection pooling per thread)
import threading
import urllib.request

_local = threading.local()

def get_session():
    &quot;&quot;&quot;Return a thread-local opener — no lock needed, no sharing.&quot;&quot;&quot;
    if not hasattr(_local, &quot;opener&quot;):
        _local.opener = urllib.request.build_opener()
        print(f&quot;[{threading.current_thread().name}] created new HTTP session&quot;)
    return _local.opener

def fetch(url):
    session = get_session()     # each thread gets its own
    # session.open(url) ...
    print(f&quot;[{threading.current_thread().name}] fetching {url}&quot;)

threads = [threading.Thread(target=fetch, args=(f&quot;http://example.com/{i}&quot;,),
                            name=f&quot;Fetcher-{i}&quot;) for i in range(4)]
for t in threads: t.start()
for t in threads: t.join()
# Each thread creates exactly one session — no contention, no sharing
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) ❌ Do NOT use &lt;code&gt;threading.local&lt;/code&gt; when&lt;/h3&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;× Threads need to share and pass data to each other → use &lt;code&gt;Queue&lt;/code&gt; or shared objects with &lt;code&gt;Lock&lt;/code&gt;.&amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;× Using &lt;code&gt;ThreadPoolExecutor&lt;/code&gt; — threads are reused, old local state may persist unexpectedly.&amp;lt;/span&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;In a thread pool, worker threads are reused across tasks. If you use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;threading.local&amp;lt;/code&amp;gt; inside a pool, &amp;lt;strong&amp;gt;always initialize the local value at the start of each task&amp;lt;/strong&amp;gt;, not just on first access — otherwise task 2 on the same thread will see task 1&apos;s leftover state.&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;10. Queue / LifoQueue / PriorityQueue — When passing data between threads (何时在线程间传递数据)&lt;/h2&gt;
&lt;h3&gt;1) ✅ Use &lt;code&gt;Queue&lt;/code&gt; when&lt;/h3&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;1.&amp;lt;/span&amp;gt; Implementing &lt;strong&gt;producer-consumer&lt;/strong&gt; patterns — the queue IS the synchronization. &amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;2.&amp;lt;/span&amp;gt; Work items need to be &lt;strong&gt;processed in order&lt;/strong&gt; (FIFO). &amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;3.&amp;lt;/span&amp;gt; You want &lt;strong&gt;backpressure&lt;/strong&gt; — producers block when the buffer is full (&lt;code&gt;maxsize&lt;/code&gt;). &amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;4.&amp;lt;/span&amp;gt; You need &lt;strong&gt;work completion tracking&lt;/strong&gt; via &lt;code&gt;task_done()&lt;/code&gt; + &lt;code&gt;join()&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# ✅ Scenario: image processing pipeline
# Loader threads → Queue → Processor threads → Queue → Writer threads
import threading, time, queue

raw_queue       = queue.Queue(maxsize=10)
processed_queue = queue.Queue(maxsize=10)

def loader(n_images):
    for i in range(n_images):
        time.sleep(0.1)
        raw_queue.put(f&quot;image_{i:03}.jpg&quot;)
        print(f&quot;[Loader] queued image_{i:03}.jpg&quot;)
    raw_queue.put(None)   # sentinel (哨兵值)

def processor():
    while True:
        item = raw_queue.get()
        if item is None:
            processed_queue.put(None)
            raw_queue.task_done()
            break
        result = f&quot;processed_{item}&quot;
        time.sleep(0.2)   # simulate processing
        processed_queue.put(result)
        raw_queue.task_done()

def writer():
    while True:
        item = processed_queue.get()
        if item is None:
            processed_queue.task_done()
            break
        print(f&quot;[Writer] saved {item}&quot;)
        processed_queue.task_done()

threads = [
    threading.Thread(target=loader,    args=(5,)),
    threading.Thread(target=processor),
    threading.Thread(target=writer),
]
for t in threads: t.start()
for t in threads: t.join()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) ✅ Use &lt;code&gt;LifoQueue&lt;/code&gt; when&lt;/h3&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;1.&amp;lt;/span&amp;gt; Most-recently-added tasks are more &lt;strong&gt;cache-warm&lt;/strong&gt; or likely to be &lt;strong&gt;more relevant&lt;/strong&gt;. &amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;2.&amp;lt;/span&amp;gt; Implementing &lt;strong&gt;depth-first search&lt;/strong&gt; with worker threads. &amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;3.&amp;lt;/span&amp;gt; Worker threads processing &lt;strong&gt;undo stacks&lt;/strong&gt; or &lt;strong&gt;rollback operations&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;3) ✅ Use &lt;code&gt;PriorityQueue&lt;/code&gt; when&lt;/h3&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;1.&amp;lt;/span&amp;gt; Tasks have different &lt;strong&gt;urgency levels&lt;/strong&gt; — critical tasks skip the queue. &amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;2.&amp;lt;/span&amp;gt; Implementing a &lt;strong&gt;task scheduler&lt;/strong&gt; with priority (e.g., real-time vs. batch jobs). &amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;3.&amp;lt;/span&amp;gt; &lt;strong&gt;Retry logic&lt;/strong&gt; — failed tasks re-enqueued with higher priority.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# ✅ Scenario: multi-tier job scheduler
import threading, queue, time

job_queue = queue.PriorityQueue()

# Priority levels (优先级级别)
CRITICAL = 1
HIGH     = 2
NORMAL   = 3
BATCH    = 4

def scheduler():
    while True:
        try:
            priority, job_id, task = job_queue.get(timeout=2)
            print(f&quot;[Scheduler] running [{[&apos;&apos;,&apos;CRITICAL&apos;,&apos;HIGH&apos;,&apos;NORMAL&apos;,&apos;BATCH&apos;][priority]}] {job_id}&quot;)
            task()
            job_queue.task_done()
        except queue.Empty:
            print(&quot;[Scheduler] no more jobs&quot;)
            break

# Submit jobs in arbitrary order
job_queue.put((NORMAL,   &quot;job-001&quot;, lambda: time.sleep(0.1)))
job_queue.put((BATCH,    &quot;job-002&quot;, lambda: time.sleep(0.1)))
job_queue.put((CRITICAL, &quot;job-003&quot;, lambda: time.sleep(0.1)))
job_queue.put((HIGH,     &quot;job-004&quot;, lambda: time.sleep(0.1)))
job_queue.put((NORMAL,   &quot;job-005&quot;, lambda: time.sleep(0.1)))

t = threading.Thread(target=scheduler)
t.start(); t.join()
# Always runs: CRITICAL → HIGH → NORMAL → NORMAL → BATCH
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;11. ThreadPoolExecutor — When managing a pool of workers (何时使用线程池)&lt;/h2&gt;
&lt;h3&gt;1) ✅ Use &lt;code&gt;ThreadPoolExecutor&lt;/code&gt; when&lt;/h3&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;1.&amp;lt;/span&amp;gt; Running &lt;strong&gt;many short-to-medium IO-bound tasks&lt;/strong&gt; concurrently (HTTP, DB, file IO). &amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;2.&amp;lt;/span&amp;gt; You need &lt;strong&gt;return values&lt;/strong&gt; from concurrent tasks without manual thread management. &amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;3.&amp;lt;/span&amp;gt; Applying the &lt;strong&gt;same function to many inputs&lt;/strong&gt; in parallel (&lt;code&gt;executor.map&lt;/code&gt;). &amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;4.&amp;lt;/span&amp;gt; You want &lt;strong&gt;automatic thread lifecycle management&lt;/strong&gt; (creation, recycling, shutdown).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# ✅ Scenario: fetch multiple URLs concurrently, collect all results
from concurrent.futures import ThreadPoolExecutor, as_completed
import time, random

def fetch_url(url):
    &quot;&quot;&quot;Simulate network fetch with random latency.&quot;&quot;&quot;
    latency = random.uniform(0.2, 1.5)
    time.sleep(latency)
    if &quot;broken&quot; in url:
        raise ConnectionError(f&quot;Failed to connect to {url}&quot;)
    return {&quot;url&quot;: url, &quot;status&quot;: 200, &quot;latency&quot;: round(latency, 3)}

urls = [
    &quot;https://api.example.com/users&quot;,
    &quot;https://api.example.com/products&quot;,
    &quot;https://api.example.com/broken-endpoint&quot;,
    &quot;https://api.example.com/orders&quot;,
    &quot;https://api.example.com/inventory&quot;,
]

print(&quot;Starting concurrent fetches...\n&quot;)
with ThreadPoolExecutor(max_workers=4) as executor:
    future_to_url = {executor.submit(fetch_url, url): url for url in urls}

    for future in as_completed(future_to_url):
        url = future_to_url[future]
        try:
            result = future.result()
            print(f&quot;✅ {result[&apos;url&apos;]:&amp;lt;40} latency={result[&apos;latency&apos;]}s&quot;)
        except ConnectionError as e:
            print(f&quot;❌ {url:&amp;lt;40} ERROR: {e}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) &lt;code&gt;submit()&lt;/code&gt; vs &lt;code&gt;map()&lt;/code&gt; — When to use which&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Need individual &lt;code&gt;Future&lt;/code&gt; objects for callbacks/cancellation&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;submit()&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Simple parallel map, results in submission order&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;map()&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Process results as they complete (not submission order)&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;as_completed()&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mixed inputs with different argument structures&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;submit()&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;3) ❌ Do NOT use &lt;code&gt;ThreadPoolExecutor&lt;/code&gt; when&lt;/h3&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;× CPU-bound tasks (image processing, ML inference) → use &lt;code&gt;ProcessPoolExecutor&lt;/code&gt; instead.&amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;× Tasks need complex inter-thread communication → combine with &lt;code&gt;Queue&lt;/code&gt;.&amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;× Thousands of very short tasks (&amp;lt; 1ms) → thread overhead dominates; use &lt;code&gt;asyncio&lt;/code&gt;.&amp;lt;/span&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;12. Master Decision Flowchart (总决策流程图)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;START: I need concurrent execution
│
├─ CPU-bound (数学计算、压缩、ML)?
│   └─ YES → use multiprocessing.Process or ProcessPoolExecutor
│
└─ IO-bound (网络、文件、数据库)?
    │
    ├─ Simple: run N tasks, collect results
    │   └─ use ThreadPoolExecutor.submit() / map()
    │
    ├─ Complex: need fine-grained control
    │   │
    │   ├─ Tasks need to exchange data?
    │   │   └─ use Queue (FIFO) / LifoQueue / PriorityQueue
    │   │
    │   ├─ Need to protect shared state?
    │   │   ├─ One thread at a time, non-reentrant → Lock
    │   │   ├─ One thread at a time, reentrant    → RLock
    │   │   └─ N threads at a time                → Semaphore(N)
    │   │
    │   ├─ Need to wait for a condition?
    │   │   ├─ One-time broadcast signal → Event
    │   │   └─ Repeated condition change → Condition
    │   │
    │   ├─ Need all threads to reach a point?
    │   │   └─ Barrier(N)
    │   │
    │   ├─ Need per-thread private state?
    │   │   └─ threading.local()
    │   │
    │   ├─ Need delayed / cancellable execution?
    │   │   └─ Timer
    │   │
    │   └─ Long-lived background service?
    │       └─ Thread(daemon=True) + Event (stop signal)
    │
    └─ Very high concurrency (1000s of tasks)?
        └─ use asyncio + aiohttp (not threading)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;13. Real-world Scenario → API Mapping (真实场景 → API 映射)&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Real-world scenario (真实场景)&lt;/th&gt;
&lt;th&gt;API to use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Fetch 100 URLs in parallel&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;ThreadPoolExecutor&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Download pipeline: fetch → parse → store&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Queue&amp;lt;/code&amp;gt; (3-stage pipeline)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shared counter incremented by many threads&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Lock&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Class method calls another synchronized method&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;RLock&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Workers wait for DB to be populated&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Condition.wait_for()&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5 workers start simultaneously (race simulation)&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Barrier&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max 3 concurrent DB connections&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;BoundedSemaphore(3)&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Server &quot;ready&quot; signal to all request handlers&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Event&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Graceful shutdown of background worker&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Event&amp;lt;/code&amp;gt; (stop flag)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auto-logout after 30min inactivity&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Timer&amp;lt;/code&amp;gt; + &lt;code&gt;cancel()&lt;/code&gt; on activity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Debounce save-to-disk on rapid edits&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Timer&amp;lt;/code&amp;gt; + &lt;code&gt;cancel()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Per-thread DB connection (no sharing)&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;threading.local()&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Critical tasks skip the line&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;PriorityQueue&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Undo stack processed by worker thread&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;LifoQueue&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Parallel phases: all workers finish step 1 first&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Barrier&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Background heartbeat / health monitor&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Thread(daemon=True)&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LRU cache with thread-safe eviction&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Lock&amp;lt;/code&amp;gt; + &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;OrderedDict&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rate-limit outgoing API requests&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;BoundedSemaphore&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway for Part II&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt; The decision rule is simple: &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;&amp;lt;strong&amp;gt;data flows between threads&amp;lt;/strong&amp;gt;&amp;lt;/span&amp;gt; → &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Queue&amp;lt;/code&amp;gt;  |  &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;&amp;lt;strong&amp;gt;shared state needs protection&amp;lt;/strong&amp;gt;&amp;lt;/span&amp;gt; → &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Lock&amp;lt;/code&amp;gt;/&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;RLock&amp;lt;/code&amp;gt;  |  &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;&amp;lt;strong&amp;gt;wait for a condition&amp;lt;/strong&amp;gt;&amp;lt;/span&amp;gt; → &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Event&amp;lt;/code&amp;gt; or &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Condition&amp;lt;/code&amp;gt;  |  &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;&amp;lt;strong&amp;gt;limit concurrency&amp;lt;/strong&amp;gt;&amp;lt;/span&amp;gt; → &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Semaphore&amp;lt;/code&amp;gt;  |  &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;&amp;lt;strong&amp;gt;just run N tasks&amp;lt;/strong&amp;gt;&amp;lt;/span&amp;gt; → &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;ThreadPoolExecutor&amp;lt;/code&amp;gt;. &amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Transformer Code</title><link>https://lxy-alexander.github.io/blog/posts/llm/transformer-code/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/llm/transformer-code/</guid><description>Transformer Code</description><pubDate>Sun, 08 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;&lt;strong&gt;I. Transformer — Complete Learning Handbook&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; &amp;lt;strong&amp;gt;Overview:&amp;lt;/strong&amp;gt; The &amp;lt;strong&amp;gt;Transformer (变换器)&amp;lt;/strong&amp;gt; is the foundational architecture behind virtually all modern large language models — GPT, BERT, T5, LLaMA, and beyond. Introduced in &amp;lt;em&amp;gt;&quot;Attention Is All You Need&quot;&amp;lt;/em&amp;gt; (Vaswani et al., 2017), it replaces recurrence with &amp;lt;strong&amp;gt;Self-Attention (自注意力机制)&amp;lt;/strong&amp;gt;, enabling fully parallel training and capturing long-range dependencies without vanishing gradients. This handbook covers every component from first principles, and ends with complete, runnable training and inference code. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1. Architecture Overview (架构总览)&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-c69d652d2a0747fab9aad1fab48ff742.r2.dev/images/image-20260206003119904&quot; alt=&quot;image-20260206003119904&quot; /&gt;&lt;/p&gt;
&lt;p&gt;A standard &lt;strong&gt;Encoder-Decoder Transformer (编码器-解码器变换器)&lt;/strong&gt; consists of:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input Tokens
     ↓
[Token Embedding + Positional Encoding]
     ↓
┌─────────────────────────────────┐
│  Encoder (编码器)  × N layers    │
│  ┌──────────────────────────┐   │
│  │ Multi-Head Self-Attention│   │
│  │ Add &amp;amp; Norm               │   │
│  │ Feed-Forward Network     │   │
│  │ Add &amp;amp; Norm               │   │
│  └──────────────────────────┘   │
└─────────────────────────────────┘
     ↓  (encoder output = memory)
┌─────────────────────────────────┐
│  Decoder (解码器)  × N layers    │
│  ┌──────────────────────────┐   │
│  │ Masked Self-Attention    │   │
│  │ Add &amp;amp; Norm               │   │
│  │ Cross-Attention          │   │
│  │ Add &amp;amp; Norm               │   │
│  │ Feed-Forward Network     │   │
│  │ Add &amp;amp; Norm               │   │
│  └──────────────────────────┘   │
└─────────────────────────────────┘
     ↓
Linear + Softmax → Output Probabilities
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Scaled Dot-Product Attention (缩放点积注意力)&lt;/h2&gt;
&lt;h3&gt;1) The Formula&lt;/h3&gt;
&lt;p&gt;The core attention operation:&lt;/p&gt;
&lt;p&gt;$$\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V$$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Q (Query, 查询)&amp;lt;/span&amp;gt;: What is each token looking for?&lt;/li&gt;
&lt;li&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;K (Key, 键)&amp;lt;/span&amp;gt;: How can this token be found by others?&lt;/li&gt;
&lt;li&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;V (Value, 值)&amp;lt;/span&amp;gt;: What does this token actually offer?&lt;/li&gt;
&lt;li&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;$\sqrt{d_k}$ (scaling factor, 缩放因子)&amp;lt;/span&amp;gt;: Prevents softmax saturation when $d_k$ is large&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2) Implementation&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import torch
import torch.nn as nn
import torch.nn.functional as F
import math

def scaled_dot_product_attention(
    Q: torch.Tensor,   # (batch, heads, seq_q, d_k)
    K: torch.Tensor,   # (batch, heads, seq_k, d_k)
    V: torch.Tensor,   # (batch, heads, seq_k, d_v)
    mask: torch.Tensor = None,
) -&amp;gt; tuple[torch.Tensor, torch.Tensor]:
    d_k = Q.size(-1)

    # Step 1: Compute attention scores
    scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(d_k)
    # scores shape: (batch, heads, seq_q, seq_k)

    # Step 2: Apply mask (set -inf so softmax → 0)
    if mask is not None:
        scores = scores.masked_fill(mask == 0, float(&apos;-inf&apos;))

    # Step 3: Softmax over key dimension
    attn_weights = F.softmax(scores, dim=-1)   # (batch, heads, seq_q, seq_k)

    # Step 4: Weighted sum of values
    output = torch.matmul(attn_weights, V)     # (batch, heads, seq_q, d_v)

    return output, attn_weights
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; The scaling by &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;√d_k&amp;lt;/code&amp;gt; is critical. Without it, dot products grow large as &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;d_k&amp;lt;/code&amp;gt; increases, pushing softmax into regions with extremely small gradients — causing the &amp;lt;strong&amp;gt;vanishing gradient problem (梯度消失)&amp;lt;/strong&amp;gt; during training.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. Multi-Head Attention (多头注意力)&lt;/h2&gt;
&lt;h3&gt;1) Motivation&lt;/h3&gt;
&lt;p&gt;A single attention head can only attend to one &quot;subspace&quot; at a time. &lt;strong&gt;Multi-Head Attention (多头注意力)&lt;/strong&gt; runs $h$ attention heads in parallel, each learning to focus on different aspects (syntax, semantics, coreference, etc.), then concatenates and projects the results.&lt;/p&gt;
&lt;p&gt;$$\text{MultiHead}(Q,K,V) = \text{Concat}(\text{head}_1, \ldots, \text{head}_h) W^O$$&lt;/p&gt;
&lt;p&gt;$$\text{head}_i = \text{Attention}(QW_i^Q,\ KW_i^K,\ VW_i^V)$$&lt;/p&gt;
&lt;h3&gt;2) Implementation&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;class MultiHeadAttention(nn.Module):
    def __init__(self, d_model: int, num_heads: int, dropout: float = 0.1):
        super().__init__()
        assert d_model % num_heads == 0, &quot;d_model must be divisible by num_heads&quot;

        self.d_model = d_model
        self.num_heads = num_heads
        self.d_k = d_model // num_heads   # Dimension per head

        # Linear projections for Q, K, V, and output
        self.W_q = nn.Linear(d_model, d_model, bias=False)
        self.W_k = nn.Linear(d_model, d_model, bias=False)
        self.W_v = nn.Linear(d_model, d_model, bias=False)
        self.W_o = nn.Linear(d_model, d_model, bias=False)

        self.dropout = nn.Dropout(dropout)

    def split_heads(self, x: torch.Tensor) -&amp;gt; torch.Tensor:
        &quot;&quot;&quot;(batch, seq, d_model) → (batch, heads, seq, d_k)&quot;&quot;&quot;
        batch, seq, _ = x.size()
        x = x.view(batch, seq, self.num_heads, self.d_k)
        return x.transpose(1, 2)   # (batch, heads, seq, d_k)

    def forward(
        self,
        query: torch.Tensor,    # (batch, seq_q, d_model)
        key: torch.Tensor,      # (batch, seq_k, d_model)
        value: torch.Tensor,    # (batch, seq_k, d_model)
        mask: torch.Tensor = None,
    ) -&amp;gt; torch.Tensor:
        # Project inputs to Q, K, V
        Q = self.split_heads(self.W_q(query))   # (batch, heads, seq_q, d_k)
        K = self.split_heads(self.W_k(key))     # (batch, heads, seq_k, d_k)
        V = self.split_heads(self.W_v(value))   # (batch, heads, seq_k, d_k)

        # Scaled dot-product attention
        attn_output, _ = scaled_dot_product_attention(Q, K, V, mask)
        # attn_output: (batch, heads, seq_q, d_k)

        # Concatenate heads
        batch, _, seq_q, _ = attn_output.size()
        attn_output = attn_output.transpose(1, 2).contiguous()
        attn_output = attn_output.view(batch, seq_q, self.d_model)
        # attn_output: (batch, seq_q, d_model)

        # Final linear projection
        return self.W_o(attn_output)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;4. Position-wise Feed-Forward Network (逐位置前馈网络)&lt;/h2&gt;
&lt;p&gt;Applied independently to each position — acts as a two-layer MLP (多层感知机) with an inner expansion:&lt;/p&gt;
&lt;p&gt;$$\text{FFN}(x) = \max(0,\ xW_1 + b_1) W_2 + b_2$$&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class FeedForward(nn.Module):
    def __init__(self, d_model: int, d_ff: int, dropout: float = 0.1):
        super().__init__()
        # Standard expansion: d_ff = 4 * d_model
        self.linear1 = nn.Linear(d_model, d_ff)
        self.linear2 = nn.Linear(d_ff, d_model)
        self.dropout = nn.Dropout(dropout)

    def forward(self, x: torch.Tensor) -&amp;gt; torch.Tensor:
        # x: (batch, seq, d_model)
        x = self.linear1(x)       # (batch, seq, d_ff)
        x = F.relu(x)             # ReLU activation (or GELU in modern variants)
        x = self.dropout(x)
        x = self.linear2(x)       # (batch, seq, d_model)
        return x
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;5. Positional Encoding (位置编码)&lt;/h2&gt;
&lt;p&gt;Since Transformers have no recurrence, positional information must be injected explicitly. The original paper uses &lt;strong&gt;sinusoidal encoding (正弦编码)&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;$$PE_{(pos, 2i)} = \sin\left(\frac{pos}{10000^{2i/d_{model}}}\right)$$ $$PE_{(pos, 2i+1)} = \cos\left(\frac{pos}{10000^{2i/d_{model}}}\right)$$&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class PositionalEncoding(nn.Module):
    def __init__(self, d_model: int, max_seq_len: int = 5000, dropout: float = 0.1):
        super().__init__()
        self.dropout = nn.Dropout(dropout)

        # Build the positional encoding table once
        pe = torch.zeros(max_seq_len, d_model)                    # (max_len, d_model)
        position = torch.arange(0, max_seq_len).unsqueeze(1)      # (max_len, 1)
        div_term = torch.exp(
            torch.arange(0, d_model, 2) * (-math.log(10000.0) / d_model)
        )

        pe[:, 0::2] = torch.sin(position * div_term)   # Even indices
        pe[:, 1::2] = torch.cos(position * div_term)   # Odd indices
        pe = pe.unsqueeze(0)                            # (1, max_len, d_model)

        # Register as buffer (not a parameter — not updated during training)
        self.register_buffer(&apos;pe&apos;, pe)

    def forward(self, x: torch.Tensor) -&amp;gt; torch.Tensor:
        # x: (batch, seq, d_model)
        x = x + self.pe[:, :x.size(1)]   # Add positional encoding
        return self.dropout(x)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Modern models (BERT, RoBERTa, GPT) use &amp;lt;strong&amp;gt;learned positional embeddings (可学习位置嵌入)&amp;lt;/strong&amp;gt; instead of fixed sinusoids. Even more recent models (LLaMA, Mistral) use &amp;lt;strong&amp;gt;RoPE (Rotary Position Embedding, 旋转位置编码)&amp;lt;/strong&amp;gt; which encodes relative positions directly into the attention computation.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;6. Add &amp;amp; Norm — Residual Connection + Layer Normalization&lt;/h2&gt;
&lt;p&gt;Each sub-layer is wrapped with a &lt;strong&gt;residual connection (残差连接)&lt;/strong&gt; and &lt;strong&gt;Layer Normalization (层归一化)&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;$$\text{LayerNorm}(x + \text{Sublayer}(x))$$&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class AddNorm(nn.Module):
    def __init__(self, d_model: int, dropout: float = 0.1):
        super().__init__()
        self.norm = nn.LayerNorm(d_model)
        self.dropout = nn.Dropout(dropout)

    def forward(self, x: torch.Tensor, sublayer_output: torch.Tensor) -&amp;gt; torch.Tensor:
        # Pre-norm variant: norm(x) → sublayer → + x  (used in modern GPT-style)
        # Post-norm variant (original paper): x + sublayer(x) → norm
        return self.norm(x + self.dropout(sublayer_output))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; The original paper uses &amp;lt;strong&amp;gt;Post-LN (后归一化)&amp;lt;/strong&amp;gt; — normalize after adding the residual. Modern models (GPT-2, LLaMA) use &amp;lt;strong&amp;gt;Pre-LN (前归一化)&amp;lt;/strong&amp;gt; — normalize before the sublayer. Pre-LN is more training-stable and is now the dominant choice.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;7. Encoder Layer (编码器层)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class EncoderLayer(nn.Module):
    def __init__(self, d_model: int, num_heads: int, d_ff: int, dropout: float = 0.1):
        super().__init__()
        self.self_attn = MultiHeadAttention(d_model, num_heads, dropout)
        self.ff        = FeedForward(d_model, d_ff, dropout)
        self.norm1     = nn.LayerNorm(d_model)
        self.norm2     = nn.LayerNorm(d_model)
        self.dropout   = nn.Dropout(dropout)

    def forward(self, x: torch.Tensor, src_mask: torch.Tensor = None) -&amp;gt; torch.Tensor:
        # Self-attention + residual + norm
        attn_out = self.self_attn(x, x, x, src_mask)
        x = self.norm1(x + self.dropout(attn_out))

        # Feed-forward + residual + norm
        ff_out = self.ff(x)
        x = self.norm2(x + self.dropout(ff_out))

        return x


class Encoder(nn.Module):
    def __init__(self, num_layers: int, d_model: int, num_heads: int, d_ff: int, dropout: float):
        super().__init__()
        self.layers = nn.ModuleList([
            EncoderLayer(d_model, num_heads, d_ff, dropout)
            for _ in range(num_layers)
        ])
        self.norm = nn.LayerNorm(d_model)

    def forward(self, x: torch.Tensor, src_mask: torch.Tensor = None) -&amp;gt; torch.Tensor:
        for layer in self.layers:
            x = layer(x, src_mask)
        return self.norm(x)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;8. Decoder Layer (解码器层)&lt;/h2&gt;
&lt;p&gt;The decoder has &lt;strong&gt;three sub-layers&lt;/strong&gt;: masked self-attention, cross-attention over encoder output, and feed-forward.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class DecoderLayer(nn.Module):
    def __init__(self, d_model: int, num_heads: int, d_ff: int, dropout: float = 0.1):
        super().__init__()
        self.self_attn  = MultiHeadAttention(d_model, num_heads, dropout)  # Masked
        self.cross_attn = MultiHeadAttention(d_model, num_heads, dropout)  # Cross
        self.ff         = FeedForward(d_model, d_ff, dropout)
        self.norm1      = nn.LayerNorm(d_model)
        self.norm2      = nn.LayerNorm(d_model)
        self.norm3      = nn.LayerNorm(d_model)
        self.dropout    = nn.Dropout(dropout)

    def forward(
        self,
        x: torch.Tensor,           # Decoder input  (batch, tgt_seq, d_model)
        memory: torch.Tensor,      # Encoder output (batch, src_seq, d_model)
        tgt_mask: torch.Tensor = None,   # Causal mask for decoder self-attention
        src_mask: torch.Tensor = None,   # Padding mask for cross-attention
    ) -&amp;gt; torch.Tensor:
        # 1. Masked self-attention (prevents attending to future tokens)
        attn1 = self.self_attn(x, x, x, tgt_mask)
        x = self.norm1(x + self.dropout(attn1))

        # 2. Cross-attention over encoder memory
        attn2 = self.cross_attn(x, memory, memory, src_mask)
        x = self.norm2(x + self.dropout(attn2))

        # 3. Feed-forward
        ff_out = self.ff(x)
        x = self.norm3(x + self.dropout(ff_out))

        return x


class Decoder(nn.Module):
    def __init__(self, num_layers: int, d_model: int, num_heads: int, d_ff: int, dropout: float):
        super().__init__()
        self.layers = nn.ModuleList([
            DecoderLayer(d_model, num_heads, d_ff, dropout)
            for _ in range(num_layers)
        ])
        self.norm = nn.LayerNorm(d_model)

    def forward(self, x, memory, tgt_mask=None, src_mask=None):
        for layer in self.layers:
            x = layer(x, memory, tgt_mask, src_mask)
        return self.norm(x)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;9. Masks (掩码)&lt;/h2&gt;
&lt;h3&gt;1) Padding Mask (填充掩码)&lt;/h3&gt;
&lt;p&gt;Prevents attention over &lt;code&gt;&amp;lt;PAD&amp;gt;&lt;/code&gt; tokens:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def make_pad_mask(seq: torch.Tensor, pad_idx: int = 0) -&amp;gt; torch.Tensor:
    &quot;&quot;&quot;
    seq: (batch, seq_len) — integer token IDs
    Returns: (batch, 1, 1, seq_len) — True where NOT padding
    &quot;&quot;&quot;
    return (seq != pad_idx).unsqueeze(1).unsqueeze(2)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) Causal Mask / Look-ahead Mask (因果掩码)&lt;/h3&gt;
&lt;p&gt;Prevents decoder positions from attending to future positions:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def make_causal_mask(seq_len: int, device: torch.device) -&amp;gt; torch.Tensor:
    &quot;&quot;&quot;
    Returns lower-triangular mask of shape (1, 1, seq_len, seq_len)
    Position i can attend to positions 0..i only.
    &quot;&quot;&quot;
    mask = torch.tril(torch.ones(seq_len, seq_len, device=device))
    return mask.unsqueeze(0).unsqueeze(0)   # (1, 1, seq_len, seq_len)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;10. Complete Transformer Model (完整模型)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Transformer(nn.Module):
    def __init__(
        self,
        src_vocab_size: int,
        tgt_vocab_size: int,
        d_model: int      = 512,
        num_heads: int    = 8,
        num_layers: int   = 6,
        d_ff: int         = 2048,
        max_seq_len: int  = 512,
        dropout: float    = 0.1,
        pad_idx: int      = 0,
    ):
        super().__init__()
        self.pad_idx = pad_idx
        self.d_model = d_model

        # Embeddings
        self.src_embedding = nn.Embedding(src_vocab_size, d_model, padding_idx=pad_idx)
        self.tgt_embedding = nn.Embedding(tgt_vocab_size, d_model, padding_idx=pad_idx)
        self.pos_encoding  = PositionalEncoding(d_model, max_seq_len, dropout)

        # Encoder &amp;amp; Decoder
        self.encoder = Encoder(num_layers, d_model, num_heads, d_ff, dropout)
        self.decoder = Decoder(num_layers, d_model, num_heads, d_ff, dropout)

        # Output projection
        self.fc_out = nn.Linear(d_model, tgt_vocab_size)

        # Weight initialization
        self._init_weights()

    def _init_weights(self):
        for p in self.parameters():
            if p.dim() &amp;gt; 1:
                nn.init.xavier_uniform_(p)

    def encode(self, src: torch.Tensor, src_mask: torch.Tensor = None) -&amp;gt; torch.Tensor:
        x = self.pos_encoding(self.src_embedding(src) * math.sqrt(self.d_model))
        return self.encoder(x, src_mask)

    def decode(
        self,
        tgt: torch.Tensor,
        memory: torch.Tensor,
        tgt_mask: torch.Tensor = None,
        src_mask: torch.Tensor = None,
    ) -&amp;gt; torch.Tensor:
        x = self.pos_encoding(self.tgt_embedding(tgt) * math.sqrt(self.d_model))
        return self.decoder(x, memory, tgt_mask, src_mask)

    def forward(
        self,
        src: torch.Tensor,   # (batch, src_len)
        tgt: torch.Tensor,   # (batch, tgt_len)
    ) -&amp;gt; torch.Tensor:
        # Build masks
        src_mask = make_pad_mask(src, self.pad_idx)
        tgt_pad_mask = make_pad_mask(tgt, self.pad_idx)
        tgt_causal   = make_causal_mask(tgt.size(1), tgt.device)
        tgt_mask     = tgt_pad_mask &amp;amp; tgt_causal   # Combine both

        # Forward pass
        memory = self.encode(src, src_mask)
        output = self.decode(tgt, memory, tgt_mask, src_mask)

        # Project to vocabulary
        return self.fc_out(output)   # (batch, tgt_len, tgt_vocab_size)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;11. Training (训练)&lt;/h2&gt;
&lt;h3&gt;1) Hyperparameters &amp;amp; Setup&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import torch
import torch.optim as optim
from torch.utils.data import DataLoader, Dataset

# ---- Hyperparameters ----
SRC_VOCAB  = 8000
TGT_VOCAB  = 8000
D_MODEL    = 256
NUM_HEADS  = 8
NUM_LAYERS = 4
D_FF       = 1024
MAX_LEN    = 128
DROPOUT    = 0.1
PAD_IDX    = 0
BOS_IDX    = 1
EOS_IDX    = 2
BATCH_SIZE = 32
EPOCHS     = 20
LR         = 1e-4
DEVICE     = torch.device(&quot;cuda&quot; if torch.cuda.is_available() else &quot;cpu&quot;)

# ---- Model ----
model = Transformer(
    src_vocab_size=SRC_VOCAB,
    tgt_vocab_size=TGT_VOCAB,
    d_model=D_MODEL,
    num_heads=NUM_HEADS,
    num_layers=NUM_LAYERS,
    d_ff=D_FF,
    max_seq_len=MAX_LEN,
    dropout=DROPOUT,
    pad_idx=PAD_IDX,
).to(DEVICE)

print(f&quot;Parameters: {sum(p.numel() for p in model.parameters()):,}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;2) Learning Rate Scheduler — Warmup (学习率预热)&lt;/h3&gt;
&lt;p&gt;The original paper uses a custom schedule: $$lr = d_{model}^{-0.5} \cdot \min(\text{step}^{-0.5},\ \text{step} \cdot \text{warmup}^{-1.5})$$&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class WarmupScheduler:
    def __init__(self, optimizer, d_model: int, warmup_steps: int = 4000):
        self.optimizer = optimizer
        self.d_model = d_model
        self.warmup_steps = warmup_steps
        self.step_num = 0

    def step(self):
        self.step_num += 1
        lr = self.d_model ** (-0.5) * min(
            self.step_num ** (-0.5),
            self.step_num * self.warmup_steps ** (-1.5)
        )
        for param_group in self.optimizer.param_groups:
            param_group[&apos;lr&apos;] = lr

optimizer  = optim.Adam(model.parameters(), lr=0, betas=(0.9, 0.98), eps=1e-9)
scheduler  = WarmupScheduler(optimizer, d_model=D_MODEL, warmup_steps=4000)
criterion  = nn.CrossEntropyLoss(ignore_index=PAD_IDX, label_smoothing=0.1)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;strong&amp;gt;Label Smoothing (标签平滑)&amp;lt;/strong&amp;gt; with &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;label_smoothing=0.1&amp;lt;/code&amp;gt; distributes 10% of the probability mass uniformly across all tokens instead of concentrating it on the correct token. This regularizes the model and prevents overconfidence.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;3) Dummy Dataset for Demonstration&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;class Seq2SeqDataset(Dataset):
    &quot;&quot;&quot;
    Minimal demo dataset — replace with real tokenized data.
    Each sample is (src_ids, tgt_ids).
    &quot;&quot;&quot;
    def __init__(self, size=1000, src_vocab=8000, tgt_vocab=8000,
                 src_len=20, tgt_len=22):
        self.data = [
            (
                torch.randint(3, src_vocab, (src_len,)),
                torch.randint(3, tgt_vocab, (tgt_len,)),
            )
            for _ in range(size)
        ]

    def __len__(self):
        return len(self.data)

    def __getitem__(self, idx):
        return self.data[idx]


def collate_fn(batch):
    &quot;&quot;&quot;Pad sequences in a batch to the same length.&quot;&quot;&quot;
    src_batch, tgt_batch = zip(*batch)
    src_padded = torch.nn.utils.rnn.pad_sequence(src_batch, batch_first=True, padding_value=PAD_IDX)
    tgt_padded = torch.nn.utils.rnn.pad_sequence(tgt_batch, batch_first=True, padding_value=PAD_IDX)
    return src_padded, tgt_padded


train_dataset = Seq2SeqDataset(size=2000)
train_loader  = DataLoader(train_dataset, batch_size=BATCH_SIZE,
                           shuffle=True, collate_fn=collate_fn)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;4) Training Loop (训练循环)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;def train_epoch(model, loader, optimizer, scheduler, criterion, device):
    model.train()
    total_loss = 0.0
    total_tokens = 0

    for batch_idx, (src, tgt) in enumerate(loader):
        src = src.to(device)         # (batch, src_len)
        tgt = tgt.to(device)         # (batch, tgt_len)

        # Teacher forcing (教师强制):
        #   Input  to decoder: tgt[:, :-1]  (all but last token)
        #   Target from model: tgt[:, 1:]   (all but first token = BOS)
        tgt_input  = tgt[:, :-1]
        tgt_target = tgt[:, 1:]

        # Forward pass
        logits = model(src, tgt_input)
        # logits: (batch, tgt_len-1, tgt_vocab_size)

        # Reshape for cross-entropy
        logits_flat  = logits.reshape(-1, logits.size(-1))  # (batch*(tgt-1), vocab)
        targets_flat = tgt_target.reshape(-1)               # (batch*(tgt-1),)

        loss = criterion(logits_flat, targets_flat)

        # Backward pass
        optimizer.zero_grad()
        loss.backward()
        torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)  # Gradient clipping
        optimizer.step()
        scheduler.step()

        # Track metrics
        non_pad = (tgt_target != PAD_IDX).sum().item()
        total_loss   += loss.item() * non_pad
        total_tokens += non_pad

        if batch_idx % 50 == 0:
            print(f&quot;  Batch {batch_idx}/{len(loader)}  &quot;
                  f&quot;Loss: {loss.item():.4f}  &quot;
                  f&quot;LR: {optimizer.param_groups[0][&apos;lr&apos;]:.6f}&quot;)

    return total_loss / total_tokens


def evaluate(model, loader, criterion, device):
    model.eval()
    total_loss = 0.0
    total_tokens = 0

    with torch.no_grad():
        for src, tgt in loader:
            src = src.to(device)
            tgt = tgt.to(device)
            tgt_input  = tgt[:, :-1]
            tgt_target = tgt[:, 1:]

            logits = model(src, tgt_input)
            loss   = criterion(logits.reshape(-1, logits.size(-1)), tgt_target.reshape(-1))

            non_pad = (tgt_target != PAD_IDX).sum().item()
            total_loss   += loss.item() * non_pad
            total_tokens += non_pad

    return total_loss / total_tokens


# ---- Main Training Loop ----
best_val_loss = float(&apos;inf&apos;)

for epoch in range(1, EPOCHS + 1):
    train_loss = train_epoch(model, train_loader, optimizer, scheduler, criterion, DEVICE)
    # val_loss = evaluate(model, val_loader, criterion, DEVICE)

    print(f&quot;\nEpoch {epoch}/{EPOCHS}  Train Loss: {train_loss:.4f}  &quot;
          f&quot;Perplexity: {math.exp(train_loss):.2f}&quot;)

    # Save best checkpoint
    torch.save({
        &apos;epoch&apos;: epoch,
        &apos;model_state_dict&apos;: model.state_dict(),
        &apos;optimizer_state_dict&apos;: optimizer.state_dict(),
        &apos;loss&apos;: train_loss,
    }, &apos;transformer_best.pt&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;12. Inference — Greedy Decoding (贪婪解码)&lt;/h2&gt;
&lt;p&gt;The simplest decoding strategy: at each step, pick the token with the highest probability.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def greedy_decode(
    model: Transformer,
    src: torch.Tensor,         # (1, src_len) — single example
    max_len: int = 50,
    bos_idx: int = BOS_IDX,
    eos_idx: int = EOS_IDX,
    device: torch.device = DEVICE,
) -&amp;gt; list[int]:
    model.eval()
    src = src.to(device)

    with torch.no_grad():
        # Step 1: Encode source sequence once
        src_mask = make_pad_mask(src, PAD_IDX)
        memory = model.encode(src, src_mask)   # (1, src_len, d_model)

        # Step 2: Initialize decoder input with BOS token
        tgt = torch.tensor([[bos_idx]], device=device)   # (1, 1)
        output_tokens = []

        for _ in range(max_len):
            # Build causal mask for current target length
            tgt_mask = make_causal_mask(tgt.size(1), device)

            # Decode one step
            dec_out = model.decode(tgt, memory, tgt_mask, src_mask)
            # dec_out: (1, tgt_len, d_model)

            # Project and take argmax of last position
            logits     = model.fc_out(dec_out[:, -1, :])   # (1, vocab)
            next_token = logits.argmax(dim=-1).item()

            output_tokens.append(next_token)

            if next_token == eos_idx:
                break

            # Append predicted token and continue
            tgt = torch.cat([tgt, torch.tensor([[next_token]], device=device)], dim=1)

    return output_tokens


# Example usage
src_example = torch.randint(3, SRC_VOCAB, (1, 15))
predicted = greedy_decode(model, src_example, max_len=50)
print(&quot;Predicted token IDs:&quot;, predicted)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;13. Inference — Beam Search (束搜索)&lt;/h2&gt;
&lt;p&gt;Maintains the top-&lt;code&gt;k&lt;/code&gt; candidate sequences at each step — much better output quality than greedy.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from dataclasses import dataclass, field

@dataclass(order=True)
class BeamHypothesis:
    score: float
    tokens: list[int] = field(compare=False)


def beam_search_decode(
    model: Transformer,
    src: torch.Tensor,
    beam_size: int   = 4,
    max_len: int     = 50,
    bos_idx: int     = BOS_IDX,
    eos_idx: int     = EOS_IDX,
    device: torch.device = DEVICE,
    length_penalty: float = 0.6,
) -&amp;gt; list[int]:
    model.eval()
    src = src.to(device)

    with torch.no_grad():
        src_mask = make_pad_mask(src, PAD_IDX)
        memory   = model.encode(src, src_mask)

        # Initialize beam with BOS token
        beams     = [BeamHypothesis(score=0.0, tokens=[bos_idx])]
        completed = []

        for step in range(max_len):
            all_candidates = []

            for beam in beams:
                if beam.tokens[-1] == eos_idx:
                    completed.append(beam)
                    continue

                tgt = torch.tensor([beam.tokens], device=device)
                tgt_mask = make_causal_mask(tgt.size(1), device)

                dec_out = model.decode(tgt, memory, tgt_mask, src_mask)
                logits  = model.fc_out(dec_out[:, -1, :])           # (1, vocab)
                log_probs = F.log_softmax(logits, dim=-1).squeeze(0) # (vocab,)

                # Expand top-k tokens
                topk_log_probs, topk_ids = log_probs.topk(beam_size)

                for log_prob, token_id in zip(topk_log_probs.tolist(),
                                               topk_ids.tolist()):
                    new_score  = beam.score + log_prob
                    new_tokens = beam.tokens + [token_id]
                    all_candidates.append(
                        BeamHypothesis(score=new_score, tokens=new_tokens)
                    )

            if not all_candidates:
                break

            # Keep top beam_size candidates
            all_candidates.sort(key=lambda h: h.score / (len(h.tokens) ** length_penalty),
                                 reverse=True)
            beams = all_candidates[:beam_size]

        # Return best completed hypothesis (or best incomplete beam)
        all_hyps = completed + beams
        best = max(all_hyps, key=lambda h: h.score / (len(h.tokens) ** length_penalty))
        return best.tokens[1:]   # Strip BOS


predicted_beam = beam_search_decode(model, src_example, beam_size=4)
print(&quot;Beam search tokens:&quot;, predicted_beam)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;14. Saving &amp;amp; Loading Checkpoints (保存与加载)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;# ---- Save ----
torch.save({
    &apos;model_state_dict&apos;    : model.state_dict(),
    &apos;optimizer_state_dict&apos;: optimizer.state_dict(),
    &apos;epoch&apos;               : epoch,
    &apos;loss&apos;                : train_loss,
    &apos;config&apos;: {
        &apos;src_vocab&apos;: SRC_VOCAB, &apos;tgt_vocab&apos;: TGT_VOCAB,
        &apos;d_model&apos;: D_MODEL, &apos;num_heads&apos;: NUM_HEADS,
        &apos;num_layers&apos;: NUM_LAYERS, &apos;d_ff&apos;: D_FF,
    }
}, &apos;transformer_checkpoint.pt&apos;)

# ---- Load ----
checkpoint = torch.load(&apos;transformer_checkpoint.pt&apos;, map_location=DEVICE)
cfg = checkpoint[&apos;config&apos;]

model = Transformer(
    src_vocab_size=cfg[&apos;src_vocab&apos;],
    tgt_vocab_size=cfg[&apos;tgt_vocab&apos;],
    d_model=cfg[&apos;d_model&apos;],
    num_heads=cfg[&apos;num_heads&apos;],
    num_layers=cfg[&apos;num_layers&apos;],
    d_ff=cfg[&apos;d_ff&apos;],
).to(DEVICE)

model.load_state_dict(checkpoint[&apos;model_state_dict&apos;])
optimizer.load_state_dict(checkpoint[&apos;optimizer_state_dict&apos;])
model.eval()
print(f&quot;Loaded checkpoint from epoch {checkpoint[&apos;epoch&apos;]}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;15. Key Design Decisions &amp;amp; Modern Variants (关键设计决策与现代变体)&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Original Paper&lt;/th&gt;
&lt;th&gt;Modern Practice&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Positional Encoding&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Sinusoidal (fixed)&lt;/td&gt;
&lt;td&gt;Learned embeddings (BERT) / RoPE (LLaMA)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Normalization&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Post-LN (后归一化)&lt;/td&gt;
&lt;td&gt;Pre-LN (前归一化) — more stable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Activation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ReLU&lt;/td&gt;
&lt;td&gt;GELU / SwiGLU (GPT, LLaMA)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Attention&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Full self-attention&lt;/td&gt;
&lt;td&gt;GQA / MQA (grouped/multi-query) — faster inference&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Vocab size&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~37,000&lt;/td&gt;
&lt;td&gt;32k–128k+ with BPE/SentencePiece&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Weight tying&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Tie input &amp;amp; output embeddings (GPT-2)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;KV Cache&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;KV Cache (KV 缓存) for autoregressive inference&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Context length&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;512&lt;/td&gt;
&lt;td&gt;4k–128k+ with sliding window or ALiBi&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt; A Transformer is a stack of &amp;lt;strong&amp;gt;Multi-Head Attention (多头注意力)&amp;lt;/strong&amp;gt; + &amp;lt;strong&amp;gt;Feed-Forward (前馈网络)&amp;lt;/strong&amp;gt; blocks tied together by &amp;lt;strong&amp;gt;Residual Connections (残差连接)&amp;lt;/strong&amp;gt; + &amp;lt;strong&amp;gt;LayerNorm (层归一化)&amp;lt;/strong&amp;gt; — master &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;scaled_dot_product_attention&amp;lt;/code&amp;gt;, understand causal masking, use warmup scheduling, and switch from greedy to beam search for better output quality.&amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Python asyncio</title><link>https://lxy-alexander.github.io/blog/posts/python/python-asyncio/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/python/python-asyncio/</guid><description>Python asyncio</description><pubDate>Sun, 08 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;&lt;strong&gt;I. Python &lt;code&gt;asyncio&lt;/code&gt; — Principles &amp;amp; Core Mechanics&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt;
&amp;lt;strong&amp;gt;Overview:&amp;lt;/strong&amp;gt; &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;asyncio&amp;lt;/code&amp;gt; is Python&apos;s &amp;lt;strong&amp;gt;asynchronous concurrency framework (异步并发框架)&amp;lt;/strong&amp;gt; that uses an &amp;lt;strong&amp;gt;Event Loop (事件循环)&amp;lt;/strong&amp;gt;, &amp;lt;strong&amp;gt;Coroutines (协程)&amp;lt;/strong&amp;gt;, and &amp;lt;strong&amp;gt;non-blocking I/O (非阻塞 I/O)&amp;lt;/strong&amp;gt; to efficiently handle many I/O-bound tasks within a single thread. The core principle: when a coroutine reaches an &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;await&amp;lt;/code&amp;gt;, it &amp;lt;em&amp;gt;yields control&amp;lt;/em&amp;gt; back to the event loop, which uses OS-level I/O multiplexing to resume the coroutine once the I/O operation is ready.
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1. How &lt;code&gt;asyncio&lt;/code&gt; Works — The Big Picture&lt;/h2&gt;
&lt;p&gt;The three pillars of asyncio:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Event Loop (事件循环)&amp;lt;/span&amp;gt; — schedules and runs coroutines, monitors I/O events, resumes tasks when they are ready&lt;/li&gt;
&lt;li&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Coroutines (协程)&amp;lt;/span&amp;gt; — define asynchronous tasks using &lt;code&gt;async/await&lt;/code&gt;; can pause and resume execution&lt;/li&gt;
&lt;li&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Non-blocking I/O (非阻塞 I/O)&amp;lt;/span&amp;gt; — allows the program to perform other work while waiting for I/O operations to complete&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; The principle of asyncio is: use an event loop to schedule coroutines (使用事件循环调度协程). When a coroutine reaches &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;await&amp;lt;/code&amp;gt;, it &amp;lt;strong&amp;gt;yields control (让出执行权)&amp;lt;/strong&amp;gt; back to the event loop, which then uses non-blocking I/O and OS I/O multiplexing (I/O 多路复用) to &amp;lt;strong&amp;gt;resume the coroutine (恢复协程执行)&amp;lt;/strong&amp;gt; once the I/O operation is ready.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Event Loop (事件循环)&lt;/h2&gt;
&lt;h3&gt;1) What the Event Loop Does&lt;/h3&gt;
&lt;p&gt;The &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Event Loop&amp;lt;/span&amp;gt; is the &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;core scheduler (核心调度器)&amp;lt;/span&amp;gt; of asyncio. It is responsible for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;Running&amp;lt;/span&amp;gt; coroutines and tasks&lt;/li&gt;
&lt;li&gt;&amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;Monitoring&amp;lt;/span&amp;gt; I/O events (sockets, file descriptors, timers)&lt;/li&gt;
&lt;li&gt;&amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;Resuming&amp;lt;/span&amp;gt; coroutines when their awaited operation completes&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2) Starting the Event Loop&lt;/h3&gt;
&lt;p&gt;The standard way is via &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;asyncio.run()&amp;lt;/code&amp;gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import asyncio

async def main():
    print(&quot;hello&quot;)

asyncio.run(main())
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What happens here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;asyncio.run()&amp;lt;/code&amp;gt; &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;creates and starts&amp;lt;/span&amp;gt; the event loop&lt;/li&gt;
&lt;li&gt;The event loop &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;executes&amp;lt;/span&amp;gt; the &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;main()&amp;lt;/code&amp;gt; coroutine to completion&lt;/li&gt;
&lt;li&gt;The event loop is &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;closed&amp;lt;/span&amp;gt; when the coroutine returns&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;3. Coroutines (协程)&lt;/h2&gt;
&lt;h3&gt;1) What Is a Coroutine?&lt;/h3&gt;
&lt;p&gt;A &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Coroutine&amp;lt;/span&amp;gt; is a special function that can &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;pause and resume execution&amp;lt;/span&amp;gt;. When it encounters an I/O wait, it pauses and returns control to the event loop, allowing other tasks to run in the meantime.&lt;/p&gt;
&lt;h3&gt;2) How to Define and Use Coroutines&lt;/h3&gt;
&lt;p&gt;Define with &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;async def&amp;lt;/code&amp;gt;, await with &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;await&amp;lt;/code&amp;gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import asyncio

async def task():
    await asyncio.sleep(1)
    print(&quot;done&quot;)

asyncio.run(task())
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What happens here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;async def&amp;lt;/code&amp;gt; &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;defines&amp;lt;/span&amp;gt; the coroutine&lt;/li&gt;
&lt;li&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;await&amp;lt;/code&amp;gt; &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;pauses&amp;lt;/span&amp;gt; the coroutine until the operation completes, yielding control back to the event loop&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Calling an &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;async def&amp;lt;/code&amp;gt; function does &amp;lt;strong&amp;gt;not&amp;lt;/strong&amp;gt; execute it — it returns a &amp;lt;strong&amp;gt;Coroutine Object (协程对象)&amp;lt;/strong&amp;gt;. The coroutine only runs when it is &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;await&amp;lt;/code&amp;gt;-ed or wrapped in a Task via &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;asyncio.create_task()&amp;lt;/code&amp;gt;.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt; asyncio achieves concurrency on a &amp;lt;strong&amp;gt;single thread&amp;lt;/strong&amp;gt; by having the &amp;lt;strong&amp;gt;Event Loop (事件循环)&amp;lt;/strong&amp;gt; continuously schedule &amp;lt;strong&amp;gt;Coroutines (协程)&amp;lt;/strong&amp;gt; — each coroutine runs until it hits an &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;await&amp;lt;/code&amp;gt;, yields control, and is resumed by the event loop once its I/O is ready.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h1&gt;&lt;strong&gt;II. Python &lt;code&gt;asyncio&lt;/code&gt; — Complete API Reference &amp;amp; Usage Scenarios&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; &amp;lt;strong&amp;gt;Overview:&amp;lt;/strong&amp;gt; This note is a complete API reference for Python&apos;s &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;asyncio&amp;lt;/code&amp;gt; library, organized by category. Every interface is paired with a real-world usage scenario so you can immediately see &amp;lt;em&amp;gt;when&amp;lt;/em&amp;gt; and &amp;lt;em&amp;gt;why&amp;lt;/em&amp;gt; to use it. The library is built on a single-threaded &amp;lt;strong&amp;gt;Event Loop (事件循环)&amp;lt;/strong&amp;gt; that schedules &amp;lt;strong&amp;gt;Coroutines (协程)&amp;lt;/strong&amp;gt; cooperatively, making it ideal for &amp;lt;strong&amp;gt;I/O-bound (I/O 密集型)&amp;lt;/strong&amp;gt; workloads. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1. Entry Points — Running Coroutines&lt;/h2&gt;
&lt;h3&gt;1) &lt;code&gt;asyncio.run(coro)&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;The top-level entry point&amp;lt;/span&amp;gt; for running an async program. Creates a new Event Loop (事件循环), runs the coroutine to completion, then closes the loop.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import asyncio

async def main():
    print(&quot;Hello from asyncio!&quot;)

asyncio.run(main())
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Never call &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;asyncio.run()&amp;lt;/code&amp;gt; inside an already-running event loop&amp;lt;/span&amp;gt; (e.g., inside Jupyter Notebook). Use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;await coro&amp;lt;/code&amp;gt; directly instead, or apply &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;nest_asyncio&amp;lt;/code&amp;gt;.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Entry point of any standalone async application — CLI tools, scripts, servers.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;2) &lt;code&gt;asyncio.get_event_loop()&lt;/code&gt; / &lt;code&gt;asyncio.get_running_loop()&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;async def main():
    loop = asyncio.get_running_loop()   # Preferred inside async context
    print(loop)

loop = asyncio.get_event_loop()         # Can be used outside async context
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;API&lt;/th&gt;
&lt;th&gt;When to Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;get_running_loop()&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Inside a coroutine — raises &lt;code&gt;RuntimeError&lt;/code&gt; if no loop is running&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;get_event_loop()&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Outside a coroutine — may create a new loop if none exists&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Coroutines &amp;amp; Tasks (协程与任务)&lt;/h2&gt;
&lt;h3&gt;1) &lt;code&gt;async def&lt;/code&gt; / &lt;code&gt;await&lt;/code&gt; — Defining and Awaiting Coroutines&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;async def fetch(url: str) -&amp;gt; str:
    await asyncio.sleep(1)      # Yield control to event loop
    return f&quot;data from {url}&quot;

async def main():
    result = await fetch(&quot;https://api.example.com&quot;)
    print(result)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Any function that performs I/O — HTTP requests, DB queries, file reads.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;2) &lt;code&gt;asyncio.create_task(coro)&lt;/code&gt; — Schedule Concurrently&lt;/h3&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Wraps a coroutine into a Task (任务)&amp;lt;/span&amp;gt; and schedules it to run on the current event loop immediately — without blocking the caller.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async def worker(name: str, delay: float):
    await asyncio.sleep(delay)
    print(f&quot;{name} done&quot;)

async def main():
    t1 = asyncio.create_task(worker(&quot;A&quot;, 2.0))
    t2 = asyncio.create_task(worker(&quot;B&quot;, 1.0))
    await t1
    await t2
    # Total time ≈ 2s, not 3s
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Fire multiple independent I/O operations simultaneously (parallel API calls, parallel DB queries).&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;A task that is created but never awaited will still run, but any exception it raises will be silently discarded.&amp;lt;/span&amp;gt; Always await your tasks or attach a &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;add_done_callback&amp;lt;/code&amp;gt;.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;3) &lt;code&gt;asyncio.Task&lt;/code&gt; Methods&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;async def main():
    task = asyncio.create_task(worker(&quot;A&quot;, 5.0))

    task.cancel()                    # Request cancellation
    print(task.done())               # True if finished/cancelled/errored
    print(task.cancelled())          # True if cancelled
    print(task.result())             # Returns result (raises if not done)
    task.add_done_callback(lambda t: print(&quot;finished:&quot;, t))
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;cancel()&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Request cancellation — injects &lt;code&gt;CancelledError&lt;/code&gt; at next &lt;code&gt;await&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;done()&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;True if completed, cancelled, or raised an exception&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;result()&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Returns the return value, or re-raises the exception&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;exception()&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Returns the exception if one was raised, else &lt;code&gt;None&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;add_done_callback(fn)&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Register a callback to run when the task finishes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;3. Concurrency Helpers (并发工具)&lt;/h2&gt;
&lt;h3&gt;1) &lt;code&gt;asyncio.gather(*coros, return_exceptions=False)&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Runs multiple awaitables (可等待对象) &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;concurrently&amp;lt;/span&amp;gt;, returns a list of results in the &lt;strong&gt;same order&lt;/strong&gt; as input.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async def main():
    results = await asyncio.gather(
        fetch(&quot;url1&quot;),
        fetch(&quot;url2&quot;),
        fetch(&quot;url3&quot;),
    )
    print(results)   # [&quot;data from url1&quot;, &quot;data from url2&quot;, &quot;data from url3&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With exception handling:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;results = await asyncio.gather(
    fetch(&quot;url1&quot;),
    failing_fetch(),
    return_exceptions=True   # Exceptions returned as values, not raised
)
for r in results:
    if isinstance(r, Exception):
        print(f&quot;Error: {r}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Batch HTTP requests, parallel DB lookups, loading multiple config files simultaneously.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;2) &lt;code&gt;asyncio.wait(tasks, return_when=...)&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Returns two sets: &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;done&amp;lt;/code&amp;gt; and &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;pending&amp;lt;/code&amp;gt;. Gives fine-grained control over when to stop waiting.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async def main():
    tasks = {asyncio.create_task(fetch(url)) for url in urls}

    done, pending = await asyncio.wait(
        tasks,
        return_when=asyncio.FIRST_COMPLETED
    )
    for task in pending:
        task.cancel()
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;return_when&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;Behavior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;ALL_COMPLETED&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Wait for all tasks (default)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;FIRST_COMPLETED&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Return as soon as any task finishes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;FIRST_EXCEPTION&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Return as soon as any task raises&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Race condition (竞态) — take the first successful result and cancel the rest (e.g., querying multiple replicas, use whichever responds first).&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;3) &lt;code&gt;asyncio.as_completed(coros)&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Yields tasks &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;in completion order&amp;lt;/span&amp;gt; (not submission order).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async def main():
    coros = [fetch(url) for url in urls]
    for future in asyncio.as_completed(coros):
        result = await future
        print(f&quot;Got: {result}&quot;)   # Processed as each one finishes
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Show results to the user as they arrive, without waiting for the slowest request.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;4) &lt;code&gt;asyncio.TaskGroup&lt;/code&gt; (Python 3.11+) — Structured Concurrency (结构化并发)&lt;/h3&gt;
&lt;p&gt;If &lt;strong&gt;any&lt;/strong&gt; task raises, all remaining tasks are &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;automatically cancelled&amp;lt;/span&amp;gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async def main():
    async with asyncio.TaskGroup() as tg:
        t1 = tg.create_task(fetch(&quot;url1&quot;))
        t2 = tg.create_task(fetch(&quot;url2&quot;))
    # All done here — or all cancelled if one failed
    print(t1.result(), t2.result())
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Any workflow where subtasks are all required — if one fails, the whole group should abort (e.g., a multi-step pipeline).&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4. Timeouts &amp;amp; Cancellation (超时与取消)&lt;/h2&gt;
&lt;h3&gt;1) &lt;code&gt;asyncio.wait_for(coro, timeout)&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;async def main():
    try:
        result = await asyncio.wait_for(fetch(&quot;url&quot;), timeout=3.0)
    except asyncio.TimeoutError:
        print(&quot;Request timed out after 3s&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Any network call that must complete within a deadline (SLA enforcement, user-facing APIs).&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;2) &lt;code&gt;asyncio.timeout(seconds)&lt;/code&gt; (Python 3.11+)&lt;/h3&gt;
&lt;p&gt;A context-manager (上下文管理器) version of timeout — more composable than &lt;code&gt;wait_for&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async def main():
    try:
        async with asyncio.timeout(5.0):
            result = await fetch(&quot;url&quot;)
            await process(result)
    except TimeoutError:
        print(&quot;Entire block timed out&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Apply a single deadline across multiple awaits inside a block.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;3) &lt;code&gt;asyncio.shield(coro)&lt;/code&gt; — Protect from Cancellation&lt;/h3&gt;
&lt;p&gt;Prevents the inner coroutine from being cancelled when the outer task is cancelled.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async def important_cleanup():
    await asyncio.sleep(1)
    print(&quot;Cleanup done&quot;)

async def main():
    task = asyncio.create_task(important_cleanup())
    try:
        await asyncio.shield(task)
    except asyncio.CancelledError:
        print(&quot;Outer cancelled, but cleanup still runs!&quot;)
        await task   # Wait for it to actually finish
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Protect a critical cleanup/commit operation from being interrupted by a cancellation signal.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;4) Handling &lt;code&gt;CancelledError&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;async def worker():
    try:
        while True:
            await asyncio.sleep(1)
    except asyncio.CancelledError:
        print(&quot;Cleaning up before cancel...&quot;)
        await do_cleanup()
        raise   # Always re-raise!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Always re-raise &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;CancelledError&amp;lt;/code&amp;gt; after cleanup.&amp;lt;/span&amp;gt; Swallowing it breaks the cancellation chain. In Python 3.8+, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;CancelledError&amp;lt;/code&amp;gt; is a subclass of &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;BaseException&amp;lt;/code&amp;gt;, not &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Exception&amp;lt;/code&amp;gt;.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;5. Synchronization Primitives (同步原语)&lt;/h2&gt;
&lt;h3&gt;1) &lt;code&gt;asyncio.Lock&lt;/code&gt; — Mutual Exclusion (互斥锁)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;lock = asyncio.Lock()

async def safe_write(db, data):
    async with lock:
        await db.write(data)   # Only one coroutine at a time
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Protecting shared in-memory state (counters, caches, connection pools) from concurrent modification.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;2) &lt;code&gt;asyncio.Semaphore&lt;/code&gt; — Concurrency Limiter (并发限制)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;sem = asyncio.Semaphore(10)   # Max 10 concurrent requests

async def rate_limited_fetch(session, url):
    async with sem:
        return await session.get(url)

async def main():
    async with aiohttp.ClientSession() as session:
        tasks = [rate_limited_fetch(session, url) for url in urls]
        results = await asyncio.gather(*tasks)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Rate-limiting API calls, capping DB connection count, controlling concurrent file handles.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;3) &lt;code&gt;asyncio.BoundedSemaphore&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Same as &lt;code&gt;Semaphore&lt;/code&gt; but raises &lt;code&gt;ValueError&lt;/code&gt; if &lt;code&gt;release()&lt;/code&gt; is called more times than &lt;code&gt;acquire()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Safety-critical resource pools where over-releasing would be a bug.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;4) &lt;code&gt;asyncio.Event&lt;/code&gt; — Signal Between Coroutines (协程间信号)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;event = asyncio.Event()

async def producer():
    await asyncio.sleep(2)
    print(&quot;Data ready&quot;)
    event.set()            # Signal the consumer

async def consumer():
    await event.wait()     # Block until set
    print(&quot;Processing data&quot;)

async def main():
    await asyncio.gather(producer(), consumer())
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; One-shot notification — signal consumers when data/resource becomes available.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;5) &lt;code&gt;asyncio.Condition&lt;/code&gt; — Wait + Notify Pattern&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;condition = asyncio.Condition()
buffer = []

async def producer():
    async with condition:
        buffer.append(&quot;item&quot;)
        condition.notify_all()   # Wake all waiting consumers

async def consumer():
    async with condition:
        await condition.wait_for(lambda: len(buffer) &amp;gt; 0)
        item = buffer.pop()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Multiple consumers waiting on a shared resource to reach a specific state.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;6) &lt;code&gt;asyncio.Queue&lt;/code&gt; — Producer-Consumer (生产者-消费者)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;async def producer(q: asyncio.Queue):
    for i in range(10):
        await q.put(i)
    await q.put(None)   # Sentinel

async def consumer(q: asyncio.Queue):
    while True:
        item = await q.get()
        if item is None:
            break
        await process(item)
        q.task_done()

async def main():
    q = asyncio.Queue(maxsize=5)
    await asyncio.gather(producer(q), consumer(q))
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Queue Type&lt;/th&gt;
&lt;th&gt;Behavior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Queue&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;FIFO (先进先出)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;LifoQueue&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;LIFO / stack (后进先出)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;PriorityQueue&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Smallest item dequeued first (优先队列)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Pipeline architectures — web crawlers, log processors, streaming data ingestion.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;6. Async Context Managers &amp;amp; Iterators (异步上下文管理器与迭代器)&lt;/h2&gt;
&lt;h3&gt;1) &lt;code&gt;async with&lt;/code&gt; — Async Context Manager (异步上下文管理器)&lt;/h3&gt;
&lt;p&gt;Implement &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;&lt;strong&gt;aenter&lt;/strong&gt;&amp;lt;/code&amp;gt; and &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;&lt;strong&gt;aexit&lt;/strong&gt;&amp;lt;/code&amp;gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class AsyncDB:
    async def __aenter__(self):
        self.conn = await connect_db()
        return self.conn

    async def __aexit__(self, *args):
        await self.conn.close()

async def main():
    async with AsyncDB() as conn:
        result = await conn.query(&quot;SELECT 1&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Any resource requiring async setup/teardown — DB connections, HTTP sessions, file handles, locks.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;2) &lt;code&gt;@asynccontextmanager&lt;/code&gt; — Decorator Shortcut&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from contextlib import asynccontextmanager

@asynccontextmanager
async def managed_connection():
    conn = await connect_db()
    try:
        yield conn
    finally:
        await conn.close()

async def main():
    async with managed_connection() as conn:
        await conn.query(&quot;SELECT 1&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Simpler alternative to writing a full class when you need a one-off async context manager.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;3) &lt;code&gt;async for&lt;/code&gt; — Async Iterator (异步迭代器)&lt;/h3&gt;
&lt;p&gt;Implement &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;&lt;strong&gt;aiter&lt;/strong&gt;&amp;lt;/code&amp;gt; and &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;&lt;strong&gt;anext&lt;/strong&gt;&amp;lt;/code&amp;gt;, or use an async generator:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async def paginated_api(base_url: str):
    page = 1
    while True:
        data = await fetch(f&quot;{base_url}?page={page}&quot;)
        if not data:
            break
        yield data
        page += 1

async def main():
    async for page in paginated_api(&quot;https://api.example.com/items&quot;):
        await process(page)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Paginated APIs, streaming database cursors, real-time event streams (WebSocket, SSE).&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;7. Running Blocking Code (在异步中运行阻塞代码)&lt;/h2&gt;
&lt;h3&gt;1) &lt;code&gt;asyncio.to_thread(func, *args)&lt;/code&gt; (Python 3.9+)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import time

async def main():
    # Run blocking I/O in a thread without freezing the event loop
    result = await asyncio.to_thread(time.sleep, 2)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Legacy blocking libraries (e.g., &lt;code&gt;requests&lt;/code&gt;, &lt;code&gt;psycopg2&lt;/code&gt;, &lt;code&gt;time.sleep&lt;/code&gt;), file system operations.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;2) &lt;code&gt;loop.run_in_executor(executor, func, *args)&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor

async def main():
    loop = asyncio.get_running_loop()

    # Thread pool — for blocking I/O
    result = await loop.run_in_executor(None, blocking_io_func, arg)

    # Process pool — for CPU-bound work
    with ProcessPoolExecutor() as pool:
        result = await loop.run_in_executor(pool, cpu_bound_func, arg)
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Executor&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;None&amp;lt;/code&amp;gt; (default ThreadPool)&lt;/td&gt;
&lt;td&gt;Blocking I/O, legacy libraries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;ThreadPoolExecutor&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Explicit thread pool sizing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;ProcessPoolExecutor&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;CPU-bound tasks (bypasses GIL)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Image processing, ML inference on CPU, compression, encryption — any heavy computation alongside async I/O.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;8. Streams — High-Level Network I/O (高层网络 I/O)&lt;/h2&gt;
&lt;h3&gt;1) &lt;code&gt;asyncio.open_connection(host, port)&lt;/code&gt; — TCP Client&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;async def tcp_client():
    reader, writer = await asyncio.open_connection(&quot;127.0.0.1&quot;, 8888)

    writer.write(b&quot;Hello\n&quot;)
    await writer.drain()

    data = await reader.readline()
    print(f&quot;Received: {data.decode()}&quot;)

    writer.close()
    await writer.wait_closed()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Custom TCP clients — talking to Redis, custom protocols, game servers.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;2) &lt;code&gt;asyncio.start_server(handler, host, port)&lt;/code&gt; — TCP Server&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;async def handle_client(reader, writer):
    data = await reader.read(1024)
    writer.write(data)         # Echo back
    await writer.drain()
    writer.close()

async def main():
    server = await asyncio.start_server(handle_client, &quot;127.0.0.1&quot;, 8888)
    async with server:
        await server.serve_forever()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Building lightweight TCP/protocol servers (chat, telnet, custom RPC).&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;9. Subprocesses (异步子进程)&lt;/h2&gt;
&lt;h3&gt;1) &lt;code&gt;asyncio.create_subprocess_exec()&lt;/code&gt; / &lt;code&gt;asyncio.create_subprocess_shell()&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;async def run_command():
    proc = await asyncio.create_subprocess_exec(
        &quot;ls&quot;, &quot;-la&quot;,
        stdout=asyncio.subprocess.PIPE,
        stderr=asyncio.subprocess.PIPE,
    )
    stdout, stderr = await proc.communicate()
    print(stdout.decode())

async def run_shell():
    proc = await asyncio.create_subprocess_shell(
        &quot;echo hello &amp;amp;&amp;amp; sleep 1 &amp;amp;&amp;amp; echo world&quot;,
        stdout=asyncio.subprocess.PIPE,
    )
    stdout, _ = await proc.communicate()
    print(stdout.decode())
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;API&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;create_subprocess_exec&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Safe — no shell injection, explicit args&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;create_subprocess_shell&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Convenient — supports pipes/redirects, shell expansion&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Running external tools (ffmpeg, git, compilers) without blocking the event loop.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;10. Utilities &amp;amp; Introspection (工具与内省)&lt;/h2&gt;
&lt;h3&gt;1) &lt;code&gt;asyncio.sleep(delay, result=None)&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;async def main():
    await asyncio.sleep(0)    # Yield control without waiting (common pattern)
    await asyncio.sleep(1.5)  # Wait 1.5 seconds
    val = await asyncio.sleep(2, result=&quot;done&quot;)  # Returns result after delay
    print(val)   # &quot;done&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; &lt;code&gt;sleep(0)&lt;/code&gt; is used to yield control voluntarily in tight loops, preventing event loop starvation (事件循环饥饿).&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;2) &lt;code&gt;asyncio.current_task()&lt;/code&gt; / &lt;code&gt;asyncio.all_tasks()&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;async def main():
    me = asyncio.current_task()
    me.set_name(&quot;main-task&quot;)

    all_running = asyncio.all_tasks()
    print(f&quot;Running tasks: {len(all_running)}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Debugging, logging task names, graceful shutdown (cancel all tasks on SIGINT).&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;3) &lt;code&gt;asyncio.ensure_future(coro_or_future)&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Schedules a coroutine or wraps a Future (期约) into a Task. Largely superseded by &lt;code&gt;create_task()&lt;/code&gt; in modern code.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;task = asyncio.ensure_future(my_coro())   # Legacy — prefer create_task()
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;4) &lt;code&gt;asyncio.wrap_future(future)&lt;/code&gt; — Bridge with &lt;code&gt;concurrent.futures&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Wraps a &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;concurrent.futures.Future&amp;lt;/code&amp;gt; into an asyncio-compatible &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;asyncio.Future&amp;lt;/code&amp;gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import concurrent.futures

def blocking():
    return 42

async def main():
    loop = asyncio.get_running_loop()
    with concurrent.futures.ThreadPoolExecutor() as pool:
        future = pool.submit(blocking)
        result = await asyncio.wrap_future(future)
        print(result)   # 42
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Integrating existing &lt;code&gt;concurrent.futures&lt;/code&gt;-based code into an asyncio application.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;5) &lt;code&gt;asyncio.isfuture()&lt;/code&gt; / &lt;code&gt;asyncio.iscoroutine()&lt;/code&gt; / &lt;code&gt;asyncio.iscoroutinefunction()&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import asyncio

async def my_coro(): pass

print(asyncio.iscoroutinefunction(my_coro))   # True
print(asyncio.iscoroutine(my_coro()))         # True
print(asyncio.isfuture(asyncio.Future()))     # True
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Writing framework code or decorators that need to handle both sync and async callables.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;11. Low-level Event Loop APIs (底层事件循环接口)&lt;/h2&gt;
&lt;h3&gt;1) &lt;code&gt;loop.call_soon(callback, *args)&lt;/code&gt; / &lt;code&gt;loop.call_later(delay, callback)&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Schedule a plain (non-coroutine) callback:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;loop = asyncio.get_event_loop()
loop.call_soon(print, &quot;scheduled immediately&quot;)
loop.call_later(2.0, print, &quot;scheduled in 2s&quot;)
loop.call_at(loop.time() + 5.0, print, &quot;scheduled at absolute time&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Integrating callback-based legacy code into an asyncio event loop.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;2) &lt;code&gt;loop.add_reader(fd, callback)&lt;/code&gt; / &lt;code&gt;loop.add_writer(fd, callback)&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Register a callback to fire when a file descriptor (文件描述符) becomes readable/writable.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;loop.add_reader(sock.fileno(), on_data_received)
loop.remove_reader(sock.fileno())
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Building low-level protocol handlers — custom socket management, raw I/O multiplexing.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;3) &lt;code&gt;asyncio.Protocol&lt;/code&gt; / &lt;code&gt;asyncio.DatagramProtocol&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;The low-level &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;callback-based protocol interface&amp;lt;/span&amp;gt;, underlying &lt;code&gt;StreamReader&lt;/code&gt;/&lt;code&gt;StreamWriter&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class EchoProtocol(asyncio.Protocol):
    def connection_made(self, transport):
        self.transport = transport

    def data_received(self, data: bytes):
        self.transport.write(data)   # Echo

    def connection_lost(self, exc):
        print(&quot;Connection closed&quot;)

async def main():
    loop = asyncio.get_running_loop()
    server = await loop.create_server(EchoProtocol, &quot;127.0.0.1&quot;, 8888)
    async with server:
        await server.serve_forever()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; High-performance servers where the overhead of &lt;code&gt;StreamReader&lt;/code&gt;/&lt;code&gt;StreamWriter&lt;/code&gt; is unacceptable, or when implementing a custom protocol (e.g., custom binary framing).&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;12. Graceful Shutdown Pattern (优雅关闭模式)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import asyncio
import signal

async def main():
    loop = asyncio.get_running_loop()

    stop = loop.create_future()
    loop.add_signal_handler(signal.SIGINT, stop.set_result, None)
    loop.add_signal_handler(signal.SIGTERM, stop.set_result, None)

    tasks = [asyncio.create_task(worker(i)) for i in range(5)]

    await stop   # Block until SIGINT/SIGTERM

    print(&quot;Shutting down...&quot;)
    for t in tasks:
        t.cancel()

    await asyncio.gather(*tasks, return_exceptions=True)
    print(&quot;All tasks cancelled. Bye.&quot;)

asyncio.run(main())
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Any long-running async service (web server, bot, background processor) that must clean up gracefully on &lt;code&gt;Ctrl+C&lt;/code&gt; or a system signal.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;13. Quick API Comparison Table&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;API&lt;/th&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Key Trait&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;asyncio.run()&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Entry point&lt;/td&gt;
&lt;td&gt;Creates + closes loop; top-level only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;create_task()&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Scheduling&lt;/td&gt;
&lt;td&gt;Non-blocking schedule; returns Task&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;gather()&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Concurrency&lt;/td&gt;
&lt;td&gt;All results in order; short-circuits on exception&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;wait()&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Concurrency&lt;/td&gt;
&lt;td&gt;Returns done/pending sets; fine control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;as_completed()&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Concurrency&lt;/td&gt;
&lt;td&gt;Yields in completion order&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;TaskGroup&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Structured&lt;/td&gt;
&lt;td&gt;Auto-cancel on failure (3.11+)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;wait_for()&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Timeout&lt;/td&gt;
&lt;td&gt;Cancels task on timeout&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;timeout()&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Timeout&lt;/td&gt;
&lt;td&gt;Context manager; covers multiple awaits (3.11+)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;shield()&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Cancellation&lt;/td&gt;
&lt;td&gt;Protects inner task from outer cancel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Lock&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Sync&lt;/td&gt;
&lt;td&gt;Mutex; one at a time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Semaphore&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Sync&lt;/td&gt;
&lt;td&gt;N at a time; rate limiting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Event&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Sync&lt;/td&gt;
&lt;td&gt;One-shot signal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Condition&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Sync&lt;/td&gt;
&lt;td&gt;Wait-for-state with notify&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Queue&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Sync&lt;/td&gt;
&lt;td&gt;FIFO producer-consumer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;to_thread()&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Blocking&lt;/td&gt;
&lt;td&gt;Offload to thread pool (3.9+)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;run_in_executor()&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Blocking&lt;/td&gt;
&lt;td&gt;Thread or process pool&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;open_connection()&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Streams&lt;/td&gt;
&lt;td&gt;High-level TCP client&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;start_server()&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Streams&lt;/td&gt;
&lt;td&gt;High-level TCP server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Protocol&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Low-level&lt;/td&gt;
&lt;td&gt;Callback-based; max performance&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt; Master the &amp;lt;strong&amp;gt;three tiers&amp;lt;/strong&amp;gt;: use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;gather&amp;lt;/code&amp;gt; / &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;TaskGroup&amp;lt;/code&amp;gt; for everyday concurrency, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Lock&amp;lt;/code&amp;gt; / &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Semaphore&amp;lt;/code&amp;gt; / &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Queue&amp;lt;/code&amp;gt; for coordination, and &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;to_thread&amp;lt;/code&amp;gt; / &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;run_in_executor&amp;lt;/code&amp;gt; to escape blocking code — everything else (streams, protocols, signals) builds on these foundations.&amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>python dataclass</title><link>https://lxy-alexander.github.io/blog/posts/python/python-dataclass/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/python/python-dataclass/</guid><description>python dataclass</description><pubDate>Sun, 08 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;&lt;strong&gt;I. Python &lt;code&gt;dataclasses&lt;/code&gt; — Complete Learning Handbook&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; &amp;lt;strong&amp;gt;Overview:&amp;lt;/strong&amp;gt; Python&apos;s &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;dataclasses&amp;lt;/code&amp;gt; module (introduced in Python 3.7) provides a &amp;lt;strong&amp;gt;decorator (装饰器)&amp;lt;/strong&amp;gt; that automatically generates boilerplate methods — &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;&lt;strong&gt;init&lt;/strong&gt;&amp;lt;/code&amp;gt;, &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;&lt;strong&gt;repr&lt;/strong&gt;&amp;lt;/code&amp;gt;, &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;&lt;strong&gt;eq&lt;/strong&gt;&amp;lt;/code&amp;gt; — for classes that primarily store data. It sits between a plain class and a full ORM/validation framework, offering clean syntax with zero runtime overhead beyond standard Python. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1. Installation &amp;amp; Import&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;dataclasses&lt;/code&gt; is part of the Python standard library — no installation required.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from dataclasses import dataclass, field, fields, asdict, astuple, replace, KW_ONLY
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Defining a Dataclass (定义数据类)&lt;/h2&gt;
&lt;h3&gt;1) Basic Definition&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from dataclasses import dataclass

@dataclass
class Point:
    x: float
    y: float

p = Point(x=1.0, y=2.0)
print(p)          # Point(x=1.0, y=2.0)
print(p.x)        # 1.0
print(p == Point(1.0, 2.0))   # True
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;@dataclass&lt;/code&gt; decorator auto-generates:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;&lt;strong&gt;init&lt;/strong&gt;(self, x, y)&amp;lt;/code&amp;gt; — constructor&lt;/li&gt;
&lt;li&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;&lt;strong&gt;repr&lt;/strong&gt;&amp;lt;/code&amp;gt; — pretty string representation&lt;/li&gt;
&lt;li&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;&lt;strong&gt;eq&lt;/strong&gt;&amp;lt;/code&amp;gt; — field-by-field equality comparison&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;2) Fields with Default Values (默认值)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;@dataclass
class User:
    name: str
    age: int = 0
    active: bool = True

u = User(name=&quot;Alice&quot;)
print(u)   # User(name=&apos;Alice&apos;, age=0, active=True)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Fields with defaults must come after fields without defaults&amp;lt;/span&amp;gt; — same rule as regular Python function parameters. Violating this raises a &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;TypeError&amp;lt;/code&amp;gt;.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;3) &lt;code&gt;field()&lt;/code&gt; — Advanced Field Configuration&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from dataclasses import dataclass, field

@dataclass
class Config:
    tags: list[str] = field(default_factory=list)     # Mutable default
    name: str = field(default=&quot;unnamed&quot;)
    _secret: str = field(default=&quot;&quot;, repr=False)       # Hidden from repr
    metadata: dict = field(default_factory=dict, compare=False)  # Excluded from ==
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;code&gt;field()&lt;/code&gt; Parameter Reference&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;Effect&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;default&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;MISSING&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Scalar default value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;default_factory&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;MISSING&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Callable that produces the default (for mutable types)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;repr&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;True&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Include field in &lt;code&gt;__repr__&lt;/code&gt; output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;compare&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;True&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Include field in &lt;code&gt;__eq__&lt;/code&gt; and &lt;code&gt;__lt__&lt;/code&gt; etc.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;hash&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;None&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Include field in &lt;code&gt;__hash__&lt;/code&gt; (None = follow &lt;code&gt;compare&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;init&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;True&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Include field as a parameter in &lt;code&gt;__init__&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;metadata&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;None&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Arbitrary read-only mapping attached to the field&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;kw_only&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;False&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Force this field to be keyword-only in &lt;code&gt;__init__&lt;/code&gt; (3.10+)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Never use a mutable object (list, dict, set) directly as a default value.&amp;lt;/span&amp;gt; Use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;field(default_factory=list)&amp;lt;/code&amp;gt; — otherwise all instances share the same object.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;4) Nested Dataclasses (嵌套数据类)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;@dataclass
class Address:
    city: str
    country: str

@dataclass
class Person:
    name: str
    address: Address

p = Person(name=&quot;Bob&quot;, address=Address(city=&quot;NYC&quot;, country=&quot;US&quot;))
print(p.address.city)   # NYC
print(p)
# Person(name=&apos;Bob&apos;, address=Address(city=&apos;NYC&apos;, country=&apos;US&apos;))
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;3. &lt;code&gt;@dataclass&lt;/code&gt; Decorator Options (装饰器配置)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;@dataclass(frozen=True, order=True, eq=True, repr=True, unsafe_hash=False, slots=True)
class Config:
    x: int
    y: int
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Option Reference Table&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;Effect&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;init&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;True&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Generate &lt;code&gt;__init__&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;repr&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;True&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Generate &lt;code&gt;__repr__&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;eq&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;True&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Generate &lt;code&gt;__eq__&lt;/code&gt; based on field values&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;order&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;False&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Generate &lt;code&gt;__lt__&lt;/code&gt;, &lt;code&gt;__le__&lt;/code&gt;, &lt;code&gt;__gt__&lt;/code&gt;, &lt;code&gt;__ge__&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;frozen&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;False&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Make instances &amp;lt;strong&amp;gt;immutable (不可变)&amp;lt;/strong&amp;gt; — raises &lt;code&gt;FrozenInstanceError&lt;/code&gt; on assignment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;unsafe_hash&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;False&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Force generate &lt;code&gt;__hash__&lt;/code&gt; even if &lt;code&gt;eq=True&lt;/code&gt; (use with care)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;slots&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;False&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;__slots__&lt;/code&gt; for faster attribute access and lower memory (3.10+)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;kw_only&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;False&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;All fields must be passed as keyword arguments (3.10+)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;match_args&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;True&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Generate &lt;code&gt;__match_args__&lt;/code&gt; for structural pattern matching (3.10+)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h3&gt;1) &lt;code&gt;frozen=True&lt;/code&gt; — Immutable Dataclass&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;@dataclass(frozen=True)
class ImmutablePoint:
    x: float
    y: float

p = ImmutablePoint(x=1.0, y=2.0)
p.x = 99.0   # ❌ FrozenInstanceError: cannot assign to field &apos;x&apos;

# Frozen dataclasses are hashable and can be used as dict keys
d = {p: &quot;origin&quot;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Configuration objects (配置对象), cache keys, value objects (值对象) that must never be mutated.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;2) &lt;code&gt;order=True&lt;/code&gt; — Sortable Dataclasses&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;@dataclass(order=True)
class Version:
    major: int
    minor: int
    patch: int

versions = [Version(1, 2, 0), Version(1, 0, 5), Version(2, 0, 0)]
print(sorted(versions))
# [Version(major=1, minor=0, patch=5), Version(major=1, minor=2, patch=0), Version(major=2, minor=0, patch=0)]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Sorting records, priority queues, range-based comparisons.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;3) &lt;code&gt;slots=True&lt;/code&gt; — Memory-efficient Dataclass (Python 3.10+)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;@dataclass(slots=True)
class FastPoint:
    x: float
    y: float

# __slots__ prevents arbitrary attribute addition and speeds up attribute access
p = FastPoint(1.0, 2.0)
p.z = 3.0   # ❌ AttributeError: &apos;FastPoint&apos; object has no attribute &apos;z&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Creating millions of small instances (数百万小对象) — data pipelines, geometry, particle simulations.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;4) &lt;code&gt;kw_only=True&lt;/code&gt; — Keyword-only Fields (Python 3.10+)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;@dataclass(kw_only=True)
class Request:
    url: str
    method: str = &quot;GET&quot;
    timeout: float = 30.0

r = Request(url=&quot;https://api.example.com&quot;)
# r = Request(&quot;https://api.example.com&quot;)  ❌ TypeError
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Use &lt;code&gt;KW_ONLY&lt;/code&gt; sentinel to make only some fields keyword-only:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from dataclasses import KW_ONLY

@dataclass
class Mixed:
    x: int
    y: int
    _: KW_ONLY          # Everything after this is keyword-only
    label: str = &quot;&quot;
    weight: float = 1.0

m = Mixed(1, 2, label=&quot;point&quot;)   # x and y are positional, label is kw-only
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;4. &lt;code&gt;__post_init__&lt;/code&gt; — Post-initialization Hook (初始化后钩子)&lt;/h2&gt;
&lt;p&gt;Runs automatically after &lt;code&gt;__init__&lt;/code&gt; completes. Use it for validation, derived fields, or type coercion.&lt;/p&gt;
&lt;h3&gt;1) Validation&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;@dataclass
class Temperature:
    celsius: float

    def __post_init__(self):
        if self.celsius &amp;lt; -273.15:
            raise ValueError(f&quot;Temperature {self.celsius}°C is below absolute zero!&quot;)

t = Temperature(-300)   # ❌ ValueError
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;2) Derived Fields with &lt;code&gt;field(init=False)&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import math

@dataclass
class Circle:
    radius: float
    area: float = field(init=False)        # Not in __init__
    circumference: float = field(init=False)

    def __post_init__(self):
        self.area = math.pi * self.radius ** 2
        self.circumference = 2 * math.pi * self.radius

c = Circle(radius=5.0)
print(c.area)           # 78.539...
print(c.circumference)  # 31.415...
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;3) Type Coercion&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;@dataclass
class Coordinate:
    lat: float
    lon: float

    def __post_init__(self):
        # Auto-convert strings to float
        self.lat = float(self.lat)
        self.lon = float(self.lon)

coord = Coordinate(lat=&quot;51.5&quot;, lon=&quot;-0.1&quot;)
print(coord.lat, type(coord.lat))   # 51.5 &amp;lt;class &apos;float&apos;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;4) &lt;code&gt;InitVar&lt;/code&gt; — Init-only Parameters (仅初始化参数)&lt;/h3&gt;
&lt;p&gt;Fields that exist in &lt;code&gt;__init__&lt;/code&gt; but are NOT stored as instance attributes:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from dataclasses import dataclass, field, InitVar

@dataclass
class HashedPassword:
    username: str
    raw_password: InitVar[str]           # Passed to __init__ but not stored
    password_hash: str = field(init=False)

    def __post_init__(self, raw_password: str):
        import hashlib
        self.password_hash = hashlib.sha256(raw_password.encode()).hexdigest()

u = HashedPassword(username=&quot;alice&quot;, raw_password=&quot;secret123&quot;)
print(u.password_hash)    # sha256 hash
# print(u.raw_password)   # ❌ AttributeError — not stored
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;5. Utility Functions (工具函数)&lt;/h2&gt;
&lt;h3&gt;1) &lt;code&gt;asdict()&lt;/code&gt; — Convert to Dictionary&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from dataclasses import asdict

@dataclass
class Point:
    x: float
    y: float

p = Point(1.0, 2.0)
print(asdict(p))   # {&apos;x&apos;: 1.0, &apos;y&apos;: 2.0}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Works recursively on nested dataclasses:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;person = Person(name=&quot;Bob&quot;, address=Address(city=&quot;NYC&quot;, country=&quot;US&quot;))
print(asdict(person))
# {&apos;name&apos;: &apos;Bob&apos;, &apos;address&apos;: {&apos;city&apos;: &apos;NYC&apos;, &apos;country&apos;: &apos;US&apos;}}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;2) &lt;code&gt;astuple()&lt;/code&gt; — Convert to Tuple&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from dataclasses import astuple

print(astuple(p))   # (1.0, 2.0)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;3) &lt;code&gt;replace()&lt;/code&gt; — Copy with Changes (不可变更新)&lt;/h3&gt;
&lt;p&gt;Creates a &lt;strong&gt;new instance&lt;/strong&gt; with specified fields replaced — the original is unchanged:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from dataclasses import replace

p = Point(x=1.0, y=2.0)
p2 = replace(p, x=99.0)
print(p2)   # Point(x=99.0, y=2.0)
print(p)    # Point(x=1.0, y=2.0)  ← original unchanged
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Immutable update patterns — building modified configurations, functional state updates.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;4) &lt;code&gt;fields()&lt;/code&gt; — Inspect Field Definitions&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from dataclasses import fields

for f in fields(Point):
    print(f.name, f.type, f.default)
# x  float  MISSING
# y  float  MISSING
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Writing generic serializers, validators, or introspection utilities (内省工具).&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;5) &lt;code&gt;is_dataclass()&lt;/code&gt; — Runtime Type Check&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from dataclasses import is_dataclass

print(is_dataclass(Point))      # True  (class)
print(is_dataclass(Point(1,2))) # True  (instance)
print(is_dataclass(int))        # False
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;6. Inheritance (继承)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;@dataclass
class Animal:
    name: str
    age: int

@dataclass
class Dog(Animal):
    breed: str
    trained: bool = False

d = Dog(name=&quot;Rex&quot;, age=3, breed=&quot;Husky&quot;)
print(d)   # Dog(name=&apos;Rex&apos;, age=3, breed=&apos;Husky&apos;, trained=False)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;If a parent class has a field with a default value, the child class cannot add fields &amp;lt;em&amp;gt;without&amp;lt;/em&amp;gt; a default — this violates the &quot;defaults must come last&quot; rule and raises a &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;TypeError&amp;lt;/code&amp;gt;.&amp;lt;/span&amp;gt; Use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;kw_only=True&amp;lt;/code&amp;gt; on the child to avoid this.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@dataclass
class Base:
    x: int = 0     # Has default

@dataclass(kw_only=True)
class Child(Base):
    y: int         # No default — OK because kw_only avoids ordering conflict
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;7. Hashing &amp;amp; Usage as Dict Keys (哈希与字典键)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;# eq=True + frozen=True → hashable
@dataclass(frozen=True)
class Point:
    x: float
    y: float

p = Point(1.0, 2.0)
cache = {p: &quot;result&quot;}   # ✅ Can be used as dict key or in set

# eq=True + frozen=False (default) → NOT hashable
@dataclass
class MutablePoint:
    x: float
    y: float

mp = MutablePoint(1.0, 2.0)
# {mp: &quot;x&quot;}  ❌ TypeError: unhashable type
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;eq&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;frozen&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;__hash__&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;False&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;False&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Inherited from &lt;code&gt;object&lt;/code&gt; (id-based)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;True&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;False&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set to &lt;code&gt;None&lt;/code&gt; — &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;unhashable&amp;lt;/span&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;True&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;True&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Generated — &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;hashable&amp;lt;/span&amp;gt; ✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;True&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;False&lt;/code&gt; + &lt;code&gt;unsafe_hash=True&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Force-generated — use with caution&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;8. Pattern Matching with Dataclasses (Python 3.10+)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;@dataclass
class Point:
    x: float
    y: float

def describe(p):
    match p:
        case Point(x=0, y=0):
            return &quot;Origin&quot;
        case Point(x=0, y=y):
            return f&quot;On Y-axis at {y}&quot;
        case Point(x=x, y=0):
            return f&quot;On X-axis at {x}&quot;
        case Point(x=x, y=y):
            return f&quot;Point at ({x}, {y})&quot;

print(describe(Point(0, 5)))     # On Y-axis at 5
print(describe(Point(3, 4)))     # Point at (3, 4)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;9. Comparison: &lt;code&gt;dataclasses&lt;/code&gt; vs Alternatives&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;&lt;code&gt;dataclasses&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;attrs&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;msgspec.Struct&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;pydantic&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Stdlib&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Auto &lt;code&gt;__init__&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Validation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ Manual&lt;/td&gt;
&lt;td&gt;✅ (validators)&lt;/td&gt;
&lt;td&gt;✅ Built-in&lt;/td&gt;
&lt;td&gt;✅ Built-in&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Serialization&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ Manual&lt;/td&gt;
&lt;td&gt;❌ Manual&lt;/td&gt;
&lt;td&gt;✅ JSON/MsgPack&lt;/td&gt;
&lt;td&gt;✅ JSON&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Performance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;⚡ Fast&lt;/td&gt;
&lt;td&gt;⚡ Fast&lt;/td&gt;
&lt;td&gt;⚡⚡ Fastest&lt;/td&gt;
&lt;td&gt;🐢→⚡ (v1→v2)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Frozen support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;__slots__&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ (3.10+)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ (C-level)&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Inheritance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ (limited)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ecosystem fit&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Standard Python&lt;/td&gt;
&lt;td&gt;Power users&lt;/td&gt;
&lt;td&gt;High-perf I/O&lt;/td&gt;
&lt;td&gt;Web APIs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;10. Real-World Scenarios (实战场景)&lt;/h2&gt;
&lt;h3&gt;1) Configuration Object&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from dataclasses import dataclass, field

@dataclass(frozen=True)
class AppConfig:
    host: str = &quot;0.0.0.0&quot;
    port: int = 8080
    debug: bool = False
    allowed_origins: tuple[str, ...] = (&quot;*&quot;,)

config = AppConfig(port=9000, debug=True)
print(config)
# AppConfig(host=&apos;0.0.0.0&apos;, port=9000, debug=True, allowed_origins=(&apos;*&apos;,))
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;2) Data Pipeline Record&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from dataclasses import dataclass, field
from datetime import datetime

@dataclass(slots=True)
class LogRecord:
    timestamp: datetime
    level: str
    message: str
    tags: list[str] = field(default_factory=list)

records = [LogRecord(datetime.now(), &quot;INFO&quot;, f&quot;event {i}&quot;) for i in range(1_000_000)]
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;3) API Request / Response Model&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import json
from dataclasses import dataclass, asdict, field
from typing import Optional

@dataclass
class CreateUserRequest:
    username: str
    email: str
    age: Optional[int] = None

@dataclass
class UserResponse:
    id: int
    username: str
    email: str

req = CreateUserRequest(username=&quot;alice&quot;, email=&quot;alice@example.com&quot;)
resp = UserResponse(id=42, username=req.username, email=req.email)
print(json.dumps(asdict(resp)))
# {&quot;id&quot;: 42, &quot;username&quot;: &quot;alice&quot;, &quot;email&quot;: &quot;alice@example.com&quot;}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;4) State Machine Node&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from dataclasses import dataclass, replace
from typing import Literal

@dataclass(frozen=True)
class JobState:
    job_id: str
    status: Literal[&quot;pending&quot;, &quot;running&quot;, &quot;done&quot;, &quot;failed&quot;]
    retries: int = 0

# Immutable state transitions
initial = JobState(job_id=&quot;abc&quot;, status=&quot;pending&quot;)
running = replace(initial, status=&quot;running&quot;)
failed  = replace(running, status=&quot;failed&quot;, retries=running.retries + 1)
retry   = replace(failed,  status=&quot;running&quot;, retries=failed.retries)

print(initial)   # JobState(job_id=&apos;abc&apos;, status=&apos;pending&apos;, retries=0)
print(retry)     # JobState(job_id=&apos;abc&apos;, status=&apos;running&apos;, retries=1)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt; Use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;@dataclass&amp;lt;/code&amp;gt; as your default data container, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;frozen=True&amp;lt;/code&amp;gt; for immutable value objects and hashable keys, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;&lt;strong&gt;post_init&lt;/strong&gt;&amp;lt;/code&amp;gt; for validation and derived fields, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;replace()&amp;lt;/code&amp;gt; for functional state updates, and &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;slots=True&amp;lt;/code&amp;gt; when creating millions of instances.&amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>python msgspec.struct</title><link>https://lxy-alexander.github.io/blog/posts/python/python-msgspecstruct/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/python/python-msgspecstruct/</guid><description>python msgspec.struct</description><pubDate>Sun, 08 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;&lt;strong&gt;I. &lt;code&gt;msgspec.Struct&lt;/code&gt; — High-Performance Typed Data Structures&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt; &amp;lt;strong&amp;gt;Overview:&amp;lt;/strong&amp;gt; &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;msgspec.Struct&amp;lt;/code&amp;gt; is a &amp;lt;strong&amp;gt;high-performance (高性能)&amp;lt;/strong&amp;gt;, &amp;lt;strong&amp;gt;type-safe (类型安全)&amp;lt;/strong&amp;gt; data class alternative from the &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;msgspec&amp;lt;/code&amp;gt; library. It is designed as a faster, leaner replacement for Python &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;dataclasses&amp;lt;/code&amp;gt;, &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;attrs&amp;lt;/code&amp;gt;, and Pydantic models — with native support for &amp;lt;strong&amp;gt;JSON / MessagePack serialization (序列化)&amp;lt;/strong&amp;gt; and &amp;lt;strong&amp;gt;validation (验证)&amp;lt;/strong&amp;gt; baked in at the C level. &amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1. Installation &amp;amp; Import&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;pip install msgspec
import msgspec
from msgspec import Struct, field
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Defining a Struct (定义结构体)&lt;/h2&gt;
&lt;h3&gt;1) Basic Definition&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from msgspec import Struct

class Point(Struct):
    x: float
    y: float

p = Point(x=1.0, y=2.0)
print(p)        # Point(x=1.0, y=2.0)
print(p.x)      # 1.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Unlike Python &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;dataclasses&amp;lt;/code&amp;gt;, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Struct&amp;lt;/code&amp;gt; instances are &amp;lt;strong&amp;gt;immutable by default (默认不可变)&amp;lt;/strong&amp;gt; and implemented in C — construction and attribute access are significantly faster.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;2) Fields with Default Values (默认值)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;class User(Struct):
    name: str
    age: int = 0
    active: bool = True

u = User(name=&quot;Alice&quot;)
print(u)   # User(name=&apos;Alice&apos;, age=0, active=True)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;3) &lt;code&gt;field()&lt;/code&gt; — Advanced Field Configuration&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from msgspec import Struct, field

class Config(Struct):
    tags: list[str] = field(default_factory=list)   # Mutable default
    name: str = field(default=&quot;unnamed&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Never use a mutable object (list, dict) directly as a default value.&amp;lt;/span&amp;gt; Use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;field(default_factory=list)&amp;lt;/code&amp;gt; instead — just like with Python dataclasses.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;4) Nested Structs (嵌套结构体)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;class Address(Struct):
    city: str
    country: str

class Person(Struct):
    name: str
    address: Address

p = Person(name=&quot;Bob&quot;, address=Address(city=&quot;NYC&quot;, country=&quot;US&quot;))
print(p.address.city)   # NYC
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;3. Struct Configuration Options (结构体配置)&lt;/h2&gt;
&lt;p&gt;Pass options to the class definition via keyword arguments:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class MyStruct(Struct, frozen=True, order=True, eq=True, kw_only=True):
    x: int
    y: int
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;1) Option Reference Table&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;Effect&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;frozen=True&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;False&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Makes the struct &amp;lt;strong&amp;gt;immutable (不可变)&amp;lt;/strong&amp;gt; — fields cannot be reassigned after creation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;order=True&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;False&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enables &lt;code&gt;&amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;=&lt;/code&gt;, &lt;code&gt;&amp;gt;=&lt;/code&gt; comparison operators&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;eq=True&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;True&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enables &lt;code&gt;==&lt;/code&gt; / &lt;code&gt;!=&lt;/code&gt; based on field values&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;kw_only=True&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;False&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;All fields must be passed as keyword arguments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;array_like=True&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;False&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Serializes as a JSON array &lt;code&gt;[...]&lt;/code&gt; instead of object &lt;code&gt;{...}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;gc=False&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;True&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Disables garbage collector tracking — faster for structs with no reference cycles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;weakref=True&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;False&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enables weak references to the struct instance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;rename&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;None&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Rename fields during (de)serialization — e.g., &lt;code&gt;rename=&quot;camel&quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;tag&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;None&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Adds a type tag for &amp;lt;strong&amp;gt;tagged unions (标签联合)&amp;lt;/strong&amp;gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;tag_field&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&quot;type&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The field name used to store the tag value&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h3&gt;2) &lt;code&gt;frozen=True&lt;/code&gt; — Immutable Struct&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;class ImmutablePoint(Struct, frozen=True):
    x: float
    y: float

p = ImmutablePoint(x=1.0, y=2.0)
p.x = 99.0   # ❌ TypeError: immutable type
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Configuration objects, cache keys, value objects (值对象) that should never change.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;3) &lt;code&gt;order=True&lt;/code&gt; — Sortable Structs&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;class Version(Struct, order=True):
    major: int
    minor: int
    patch: int

versions = [Version(1, 2, 0), Version(1, 0, 5), Version(2, 0, 0)]
print(sorted(versions))
# [Version(1,0,5), Version(1,2,0), Version(2,0,0)]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Sorting records, priority queues, range comparisons.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;4) &lt;code&gt;rename=&quot;camel&quot;&lt;/code&gt; — Field Name Mapping&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;class ApiResponse(Struct, rename=&quot;camel&quot;):
    user_name: str
    created_at: str

import msgspec
obj = ApiResponse(user_name=&quot;Alice&quot;, created_at=&quot;2025-01-01&quot;)
print(msgspec.json.encode(obj))
# b&apos;{&quot;userName&quot;:&quot;Alice&quot;,&quot;createdAt&quot;:&quot;2025-01-01&quot;}&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;rename&lt;/code&gt; value&lt;/th&gt;
&lt;th&gt;Effect&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;&quot;camel&quot;&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;user_name&lt;/code&gt; → &lt;code&gt;userName&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;&quot;pascal&quot;&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;user_name&lt;/code&gt; → &lt;code&gt;UserName&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;&quot;lower&quot;&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;userName&lt;/code&gt; → &lt;code&gt;username&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;dict&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Explicit per-field mapping&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Interoperating with REST APIs that use camelCase JSON keys.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4. Serialization &amp;amp; Deserialization (序列化与反序列化)&lt;/h2&gt;
&lt;h3&gt;1) JSON Encoding&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import msgspec

class Order(Struct):
    id: int
    item: str
    price: float

order = Order(id=1, item=&quot;book&quot;, price=9.99)

# Encode to JSON bytes
data = msgspec.json.encode(order)
print(data)   # b&apos;{&quot;id&quot;:1,&quot;item&quot;:&quot;book&quot;,&quot;price&quot;:9.99}&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;2) JSON Decoding with Type Validation&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# Decode + validate in one step
order2 = msgspec.json.decode(data, type=Order)
print(order2)          # Order(id=1, item=&apos;book&apos;, price=9.99)
print(order2 == order) # True
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;msgspec.json.decode()&amp;lt;/code&amp;gt; performs &amp;lt;strong&amp;gt;schema validation (模式验证)&amp;lt;/strong&amp;gt; at decode time. If the JSON does not match the expected type, it raises &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;msgspec.ValidationError&amp;lt;/code&amp;gt; with a descriptive message.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;3) MessagePack Encoding (二进制序列化)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# Encode to binary MessagePack
binary = msgspec.msgpack.encode(order)
print(binary)   # b&apos;\x83\xa2id\x01\xa4item\xa4book\xa5price\xcb@#\xeb...&apos;

# Decode from binary
order3 = msgspec.msgpack.decode(binary, type=Order)
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;th&gt;Output&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;JSON&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;msgspec.json.encode/decode&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Human-readable bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MessagePack&lt;/td&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;msgspec.msgpack.encode/decode&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Compact binary&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h3&gt;4) &lt;code&gt;array_like=True&lt;/code&gt; — Array Serialization&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;class Point(Struct, array_like=True):
    x: float
    y: float

p = Point(1.0, 2.0)
print(msgspec.json.encode(p))   # b&apos;[1.0,2.0]&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Compact serialization for large volumes of records (matrices, time series, coordinate data).&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;5) Handling Validation Errors&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;bad_json = b&apos;{&quot;id&quot;: &quot;not-a-number&quot;, &quot;item&quot;: &quot;book&quot;, &quot;price&quot;: 9.99}&apos;

try:
    msgspec.json.decode(bad_json, type=Order)
except msgspec.ValidationError as e:
    print(e)
    # Expected `int`, got `str` - at `$.id`
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;5. Type Annotations &amp;amp; Supported Types (类型注解)&lt;/h2&gt;
&lt;h3&gt;1) Built-in Types&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;class Example(Struct):
    a: int
    b: float
    c: str
    d: bool
    e: bytes
    f: None
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;2) Collections (集合类型)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from typing import Optional

class Collections(Struct):
    items: list[str]
    mapping: dict[str, int]
    pair: tuple[int, str]
    unique: set[int]
    maybe: Optional[str] = None        # str | None
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;3) &lt;code&gt;Optional&lt;/code&gt; and &lt;code&gt;Union&lt;/code&gt; Types&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from typing import Union

class Response(Struct):
    data: Union[str, int, None]        # Can be str, int, or None
    error: str | None = None           # Python 3.10+ shorthand
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;4) &lt;code&gt;Literal&lt;/code&gt; Types — Constrained Values (约束值)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from typing import Literal

class Status(Struct):
    state: Literal[&quot;pending&quot;, &quot;running&quot;, &quot;done&quot;, &quot;failed&quot;]

s = Status(state=&quot;running&quot;)
msgspec.json.decode(b&apos;{&quot;state&quot;:&quot;invalid&quot;}&apos;, type=Status)
# ❌ ValidationError: Expected one of &apos;pending&apos;, &apos;running&apos;, &apos;done&apos;, &apos;failed&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Enforcing valid enum-like values without a full &lt;code&gt;Enum&lt;/code&gt; class.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;5) &lt;code&gt;datetime&lt;/code&gt;, &lt;code&gt;UUID&lt;/code&gt;, &lt;code&gt;Decimal&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from datetime import datetime
from uuid import UUID
from decimal import Decimal

class Event(Struct):
    id: UUID
    timestamp: datetime
    amount: Decimal
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;datetime&amp;lt;/code&amp;gt; is serialized as an ISO 8601 string. &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;UUID&amp;lt;/code&amp;gt; as a string. &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;Decimal&amp;lt;/code&amp;gt; as a JSON number string.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;6. Tagged Unions (标签联合) — Polymorphic Types&lt;/h2&gt;
&lt;h3&gt;1) Defining a Tagged Union&lt;/h3&gt;
&lt;p&gt;Use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;tag=True&amp;lt;/code&amp;gt; (or a custom tag string) to enable discriminated unions (判别联合):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from typing import Union

class Cat(Struct, tag=True):
    name: str
    indoor: bool

class Dog(Struct, tag=True):
    name: str
    breed: str

Animal = Union[Cat, Dog]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When serialized, a &lt;code&gt;&quot;type&quot;&lt;/code&gt; field is added automatically:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cat = Cat(name=&quot;Whiskers&quot;, indoor=True)
print(msgspec.json.encode(cat))
# b&apos;{&quot;type&quot;:&quot;Cat&quot;,&quot;name&quot;:&quot;Whiskers&quot;,&quot;indoor&quot;:true}&apos;

dog = Dog(name=&quot;Rex&quot;, breed=&quot;Husky&quot;)
print(msgspec.json.encode(dog))
# b&apos;{&quot;type&quot;:&quot;Dog&quot;,&quot;name&quot;:&quot;Rex&quot;,&quot;breed&quot;:&quot;Husky&quot;}&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;2) Decoding a Tagged Union&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;data = b&apos;{&quot;type&quot;:&quot;Dog&quot;,&quot;name&quot;:&quot;Rex&quot;,&quot;breed&quot;:&quot;Husky&quot;}&apos;
animal = msgspec.json.decode(data, type=Animal)
print(type(animal))   # &amp;lt;class &apos;Dog&apos;&amp;gt;
print(animal.breed)   # Husky
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Event systems, polymorphic API responses, command/event patterns where the same endpoint can return different shapes.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;3) Custom Tag Values&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;class Circle(Struct, tag=&quot;circle&quot;):
    radius: float

class Rectangle(Struct, tag=&quot;rect&quot;):
    width: float
    height: float

Shape = Union[Circle, Rectangle]

c = Circle(radius=5.0)
print(msgspec.json.encode(c))
# b&apos;{&quot;type&quot;:&quot;circle&quot;,&quot;radius&quot;:5.0}&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;7. Utility Methods (工具方法)&lt;/h2&gt;
&lt;h3&gt;1) &lt;code&gt;msgspec.structs.asdict()&lt;/code&gt; — Convert to Dict&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from msgspec import structs

p = Point(x=1.0, y=2.0)
d = structs.asdict(p)
print(d)   # {&apos;x&apos;: 1.0, &apos;y&apos;: 2.0}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;2) &lt;code&gt;msgspec.structs.astuple()&lt;/code&gt; — Convert to Tuple&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;t = structs.astuple(p)
print(t)   # (1.0, 2.0)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;3) &lt;code&gt;msgspec.structs.replace()&lt;/code&gt; — Copy with Changes&lt;/h3&gt;
&lt;p&gt;Like &lt;code&gt;dataclasses.replace()&lt;/code&gt; — creates a new instance with some fields updated:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;p2 = structs.replace(p, x=99.0)
print(p2)   # Point(x=99.0, y=2.0)
print(p)    # Point(x=1.0, y=2.0)  ← original unchanged
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Immutable update patterns (不可变更新模式) — create a modified copy without mutating the original.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;4) &lt;code&gt;msgspec.structs.fields()&lt;/code&gt; — Inspect Field Definitions&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;for f in structs.fields(Point):
    print(f.name, f.type, f.default)
# x  &amp;lt;class &apos;float&apos;&amp;gt;  NODEFAULT
# y  &amp;lt;class &apos;float&apos;&amp;gt;  NODEFAULT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Writing generic serializers, validators, or introspection tools.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;8. Inheritance (继承)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Animal(Struct):
    name: str
    age: int

class Dog(Animal):
    breed: str

d = Dog(name=&quot;Rex&quot;, age=3, breed=&quot;Husky&quot;)
print(d)   # Dog(name=&apos;Rex&apos;, age=3, breed=&apos;Husky&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;A child Struct cannot override a field defined in the parent.&amp;lt;/span&amp;gt; Fields defined in the parent always come first in the constructor signature.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;9. Performance Comparison (性能对比)&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Library&lt;/th&gt;
&lt;th&gt;Construct&lt;/th&gt;
&lt;th&gt;JSON Encode&lt;/th&gt;
&lt;th&gt;JSON Decode+Validate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;msgspec.Struct&amp;lt;/span&amp;gt;&lt;/td&gt;
&lt;td&gt;⚡ Fastest&lt;/td&gt;
&lt;td&gt;⚡ Fastest&lt;/td&gt;
&lt;td&gt;⚡ Fastest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dataclasses&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fast&lt;/td&gt;
&lt;td&gt;Needs &lt;code&gt;json.dumps&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No validation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;attrs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fast&lt;/td&gt;
&lt;td&gt;Needs extra lib&lt;/td&gt;
&lt;td&gt;No validation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pydantic v2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Fast&lt;/td&gt;
&lt;td&gt;Fast (Rust core)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pydantic v1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Slow&lt;/td&gt;
&lt;td&gt;Slow&lt;/td&gt;
&lt;td&gt;Slow&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; In benchmarks, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;msgspec&amp;lt;/code&amp;gt; is typically &amp;lt;strong&amp;gt;5–10× faster than Pydantic v1&amp;lt;/strong&amp;gt; and &amp;lt;strong&amp;gt;2–3× faster than Pydantic v2&amp;lt;/strong&amp;gt; for both encoding and decoding, while using significantly less memory.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;10. Real-World Scenarios (实战场景)&lt;/h2&gt;
&lt;h3&gt;1) FastAPI / HTTP API Request/Response Models&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from msgspec import Struct
import msgspec

class CreateUserRequest(Struct):
    username: str
    email: str
    age: int | None = None

class UserResponse(Struct):
    id: int
    username: str
    email: str

# Decoding incoming JSON body
body = b&apos;{&quot;username&quot;:&quot;alice&quot;,&quot;email&quot;:&quot;alice@example.com&quot;}&apos;
req = msgspec.json.decode(body, type=CreateUserRequest)

# Encoding outgoing response
resp = UserResponse(id=42, username=req.username, email=req.email)
print(msgspec.json.encode(resp))
# b&apos;{&quot;id&quot;:42,&quot;username&quot;:&quot;alice&quot;,&quot;email&quot;:&quot;alice@example.com&quot;}&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;2) Config File Parsing with Validation&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;import msgspec
from msgspec import Struct
from typing import Literal

class ServerConfig(Struct):
    host: str = &quot;0.0.0.0&quot;
    port: int = 8080
    mode: Literal[&quot;debug&quot;, &quot;production&quot;] = &quot;production&quot;
    workers: int = 4

config_json = b&apos;{&quot;host&quot;:&quot;127.0.0.1&quot;,&quot;port&quot;:9000,&quot;mode&quot;:&quot;debug&quot;}&apos;
config = msgspec.json.decode(config_json, type=ServerConfig)
print(config.mode)   # debug
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;3) High-throughput MessagePack Messaging (e.g., vLLM, message queues)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;class InferenceRequest(Struct):
    request_id: str
    prompt: str
    max_tokens: int = 512
    temperature: float = 1.0

class InferenceResponse(Struct):
    request_id: str
    output: str
    finish_reason: Literal[&quot;stop&quot;, &quot;length&quot;, &quot;error&quot;]

# Fast binary serialization for IPC / queue transport
req = InferenceRequest(request_id=&quot;req-001&quot;, prompt=&quot;Hello!&quot;)
binary = msgspec.msgpack.encode(req)

resp_data = msgspec.msgpack.decode(binary, type=InferenceRequest)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;4) Event / Command Pattern with Tagged Unions&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;from typing import Union
from msgspec import Struct
import msgspec

class StartJob(Struct, tag=True):
    job_id: str
    config: dict

class StopJob(Struct, tag=True):
    job_id: str
    reason: str

Command = Union[StartJob, StopJob]

# Dispatcher
def handle(data: bytes):
    cmd = msgspec.json.decode(data, type=Command)
    if isinstance(cmd, StartJob):
        print(f&quot;Starting job {cmd.job_id}&quot;)
    elif isinstance(cmd, StopJob):
        print(f&quot;Stopping job {cmd.job_id}: {cmd.reason}&quot;)

handle(b&apos;{&quot;type&quot;:&quot;StartJob&quot;,&quot;job_id&quot;:&quot;abc&quot;,&quot;config&quot;:{}}&apos;)
# Starting job abc
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;msgspec.Struct&amp;lt;/code&amp;gt; gives you the ergonomics of a dataclass, the validation of Pydantic, and the serialization speed of hand-written C — use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;frozen=True&amp;lt;/code&amp;gt; for immutable value objects, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;tag=True&amp;lt;/code&amp;gt; for polymorphic types, and &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;rename=&quot;camel&quot;&amp;lt;/code&amp;gt; for seamless REST API integration.&amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>vllm contributor</title><link>https://lxy-alexander.github.io/blog/posts/vllm/vllm-contributor/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/vllm/vllm-contributor/</guid><description>vllm contributor</description><pubDate>Sun, 08 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;&lt;strong&gt;I. Contributing to vLLM — Development Guide&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt;
&amp;lt;strong&amp;gt;Overview:&amp;lt;/strong&amp;gt; This document covers the complete workflow for contributing to vLLM, including environment setup, two installation paths (Python-only vs. CUDA/C++ compilation), linting, documentation preview, test execution, and PR submission guidelines. Whether you are contributing for the first time or working on daily development, this guide serves as a handy reference.
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1. Contributing to vLLM&lt;/h2&gt;
&lt;p&gt;Ways to contribute include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Reporting bugs / opening issues&lt;/li&gt;
&lt;li&gt;Adding support for new models&lt;/li&gt;
&lt;li&gt;Implementing new features&lt;/li&gt;
&lt;li&gt;Improving documentation&lt;/li&gt;
&lt;li&gt;Helping others, reviewing PRs&lt;/li&gt;
&lt;li&gt;Starring the repo, writing articles — these count too&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Developing&lt;/h2&gt;
&lt;h3&gt;1) Step 1: Clone the Repository&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;git clone https://github.com/vllm-project/vllm.git
cd vllm
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) Step 2: Create a Python Environment (Recommended: uv)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;uv venv --python 3.12 --seed
source .venv/bin/activate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you don&apos;t have uv, install it first:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;curl -LsSf https://astral.sh/uv/install.sh | sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Why Python 3.12? Because vLLM&apos;s CI (official automated tests) primarily uses 3.12. Using the same version prevents situations where tests pass locally but fail in CI.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;To delete the virtual environment:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;rm -rf .venv
uv cache clean
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;3. Installing vLLM (Two Paths)&lt;/h2&gt;
&lt;h3&gt;1) Path A: Python-only Changes (Fastest, Recommended)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;VLLM_USE_PRECOMPILED=1 uv pip install -e .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What this means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Installs in &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Editable Mode&amp;lt;/span&amp;gt; (&lt;code&gt;-e&lt;/code&gt;) — changes to source files take effect immediately&lt;/li&gt;
&lt;li&gt;Does &lt;strong&gt;not&lt;/strong&gt; compile C++/CUDA locally&lt;/li&gt;
&lt;li&gt;Downloads pre-compiled binaries from the corresponding pre-built wheel&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;👉 Advantage: Very fast, suitable for the majority of PRs.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;2) Path B: CUDA/C++ Changes (Requires Local Compilation)&lt;/h3&gt;
&lt;p&gt;If you previously ran Path A, first &lt;strong&gt;force-remove&lt;/strong&gt; the installed &lt;code&gt;vllm&lt;/code&gt; Python package:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;uv pip uninstall vllm
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Install PyTorch (cu129):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;uv pip install torch torchvision torchaudio \
  --extra-index-url https://download.pytorch.org/whl/cu129
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Install the current project in Editable Mode:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CCACHE_NOHASHDIR=&quot;true&quot; uv pip install --no-build-isolation -e . -v
CCACHE_NOHASHDIR=&quot;true&quot; uv pip install -e . -v
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;uv pip install -e .&amp;lt;/code&amp;gt; installs the project in the current directory in editable mode. &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;.&amp;lt;/code&amp;gt; refers to the current directory (i.e., the vllm repo root). It reads &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;pyproject.toml&amp;lt;/code&amp;gt; (primary) or &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;setup.py&amp;lt;/code&amp;gt; (legacy), then installs the project into your virtual environment.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h4&gt;Common Error: &lt;code&gt;ImportError: undefined symbol&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;If you encounter the following error:&amp;lt;/span&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(vllm) [xli49@ghpc008 vllm]$ python examples/offline_inference/basic/basic.py
Traceback (most recent call last):
  ...
  File &quot;/data/home/xli49/vllm/vllm/platforms/cuda.py&quot;, line 16, in &amp;lt;module&amp;gt;
    import vllm._C  # noqa
    ^^^^^^^^^^^^^^
ImportError: /data/home/xli49/vllm/vllm/_C.abi3.so: undefined symbol: _ZN3c104cuda9SetDeviceEa
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The cause is a mismatch between the torch ABI used at compile time and the torch version at runtime. Ensure you use &lt;code&gt;--no-build-isolation&lt;/code&gt; and recompile with the correct CUDA version:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;uv pip install -e . --no-build-isolation
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Why Does vLLM Require &lt;code&gt;--no-build-isolation&lt;/code&gt;?&lt;/h4&gt;
&lt;p&gt;Because compiling vLLM&apos;s C++/CUDA extensions depends heavily on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;torch&lt;/code&gt; installed in your current environment&lt;/li&gt;
&lt;li&gt;The matching CUDA version (cu129/cu128, etc.)&lt;/li&gt;
&lt;li&gt;Other compilation-related packages&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Without this flag, the build system uses an isolated temporary environment, which may result in:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A mismatched &lt;code&gt;torch&lt;/code&gt; being installed in the temporary environment&lt;/li&gt;
&lt;li&gt;The current torch&apos;s CUDA configuration not being found&lt;/li&gt;
&lt;li&gt;Compilation failures or incompatible binaries being generated&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;4. Linting (Code Style &amp;amp; Formatting)&lt;/h2&gt;
&lt;p&gt;vLLM uses &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;pre-commit&amp;lt;/span&amp;gt; to enforce a unified code style.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;uv pip install pre-commit&amp;lt;/code&amp;gt;: installs the pre-commit tool&lt;/li&gt;
&lt;li&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;pre-commit install&amp;lt;/code&amp;gt;: installs hooks into &lt;code&gt;.git/hooks/&lt;/code&gt; so that checks run automatically on every &lt;code&gt;git commit&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;1) Install and Enable&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;uv pip install pre-commit
pre-commit install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From now on, every &lt;code&gt;git commit&lt;/code&gt; will automatically run the checks ✅&lt;/p&gt;
&lt;h3&gt;2) Run Manually&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;pre-commit run      # Check only staged files
pre-commit run -a   # Check all files (= --all-files)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) CI-only Hooks (Trigger Locally on Demand)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;pre-commit run --hook-stage manual markdownlint
pre-commit run --hook-stage manual mypy-3.10
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;5. Documentation&lt;/h2&gt;
&lt;p&gt;vLLM&apos;s docs are built with &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;MkDocs&amp;lt;/span&amp;gt;.&lt;/p&gt;
&lt;h3&gt;1) Install Documentation Dependencies&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;uv pip install -r requirements/docs.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) Preview the Docs Site Locally&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;mkdocs serve
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) Faster Preview (Skip API Reference Generation)&lt;/h3&gt;
&lt;p&gt;Controls whether the API Reference is generated.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;API_AUTONAV_EXCLUDE=vllm mkdocs serve
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Ensure your Python version is compatible with the plugins. For example, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;mkdocs-awesome-nav&amp;lt;/code&amp;gt; requires Python 3.10+.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;4) Forward the Port from a Remote Server&lt;/h3&gt;
&lt;p&gt;&amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;-L&amp;lt;/code&amp;gt; = Local port forwarding: &lt;strong&gt;maps a port on the remote machine to a port on your local machine&lt;/strong&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssh -L 8000:127.0.0.1:8000 xli49@spiedie.binghamton.edu
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5) Connect to a Remote GPU Node via Jump Host&lt;/h3&gt;
&lt;p&gt;&amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;-J&amp;lt;/code&amp;gt; = Jump host: &lt;strong&gt;connect to a target machine by hopping through an intermediate host first&lt;/strong&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssh -J xli49@spiedie.binghamton.edu -L 8000:127.0.0.1:8000 xli49@ghpc005
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;6. Testing&lt;/h2&gt;
&lt;p&gt;vLLM uses &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;pytest&amp;lt;/span&amp;gt;.&lt;/p&gt;
&lt;h3&gt;1) Path A: Full CI-equivalent Setup (CUDA)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;uv pip install -r requirements/common.txt -r requirements/dev.txt --torch-backend=auto
pytest tests/
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) Path B: Minimal Test Tooling Only&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;uv pip install pytest pytest-asyncio
pytest tests/
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) Run a Single Test File (Useful for Debugging)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;pytest -s -v tests/test_logger.py
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;7. Common Errors&lt;/h2&gt;
&lt;h3&gt;1) Missing &lt;code&gt;Python.h&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;If you encounter the following error during compilation or dependency installation:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Python.h: No such file or directory
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Fix on Ubuntu:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt install python3-dev
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;8. Important Warnings&lt;/h2&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;✅ The repository is not yet fully covered by mypy&amp;lt;/span&amp;gt; — do not rely on mypy being fully green.&lt;/p&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;⚠️ Not all tests pass on CPU&amp;lt;/span&amp;gt; — without a GPU, many tests will fail locally. The official stance is: rely on CI for those tests.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;9. PR Submission Guidelines&lt;/h2&gt;
&lt;h3&gt;1) DCO Sign-off&lt;/h3&gt;
&lt;p&gt;Every commit must include a &lt;code&gt;Signed-off-by&lt;/code&gt; line:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git commit -s -m &quot;xxx&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) PR Title Must Include a Category Prefix&lt;/h3&gt;
&lt;p&gt;Examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;[Bugfix] ...&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[Kernel] ...&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[Core] ...&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[Doc] ...&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[CI/Build] ...&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;PRs without a valid prefix may not be reviewed.&amp;lt;/span&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt; For Python-only changes, use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;VLLM_USE_PRECOMPILED=1 uv pip install -e .&amp;lt;/code&amp;gt; to get started in seconds; for CUDA/C++ changes, always compile with &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;--no-build-isolation&amp;lt;/code&amp;gt; and match your torch CUDA version to avoid ABI symbol errors.&amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Note Prompt</title><link>https://lxy-alexander.github.io/blog/posts/prompt/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/prompt/</guid><description>Note Prompt</description><pubDate>Sat, 07 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Prompt for Claude&lt;/h1&gt;
&lt;p&gt;学习一个东西,应该是他是做什么的什么?为什么需要学习他? 和其他的对比&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Generate English study notes in Typora-compatible Markdown format with the following requirements: Please explain the content concisely. The example code can be executed independently.

## 1. Language
- All explanations written in **English**
- The explanation should be concise, in one sentence, easy to remember, and in an interview tone
- All **technical terms** must include a Chinese annotation in parentheses
- Example: `The Time Complexity (时间复杂度) is O(n).`

## 2. Heading Structure
- Level 1 heading: Roman numerals → `# I.`
- Level 2 heading: Arabic numerals → `## 1.`
- Level 3 heading: Parenthesis form → `### 1)`

## 3. Typora Rules
1. Use `$$ $$` for math formulas, not `\[ \]`
2. After the content of the level 1 and level 2 headings is completed, a blank line should be left.

## 6. Content
Paste your content:

&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>RoPE</title><link>https://lxy-alexander.github.io/blog/posts/llm/rope/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/llm/rope/</guid><description>RoPE</description><pubDate>Wed, 04 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;这个公式其实是数学中的**旋转矩阵（Rotation Matrix）&lt;strong&gt;在二维空间的应用。我们可以从&lt;/strong&gt;极坐标（Polar Coordinates）**的角度来推导，这样最直观。&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;1. 准备工作：极坐标表示&lt;/h3&gt;
&lt;p&gt;假设一个点 $P(x, y)$ 在圆心为原点的圆上，它距离原点的距离（半径）为 $r$，与 $x$ 轴的正方向夹角为 $\alpha$。&lt;/p&gt;
&lt;p&gt;根据三角函数，我们可以把它的坐标写成：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$x = r \cos\alpha$&lt;/li&gt;
&lt;li&gt;$y = r \sin\alpha$&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 进行旋转&lt;/h3&gt;
&lt;p&gt;现在，我们要把这个点&lt;strong&gt;逆时针旋转&lt;/strong&gt;一个角度 $\theta$。旋转后的新点记为 $P&apos;(x&apos;, y&apos;)$。&lt;/p&gt;
&lt;p&gt;新点 $P&apos;$ 的半径依然是 $r$，但它与 $x$ 轴的夹角变成了 $\alpha + \theta$。&lt;/p&gt;
&lt;p&gt;所以新坐标可以表示为：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$x&apos; = r \cos(\alpha + \theta)$&lt;/li&gt;
&lt;li&gt;$y&apos; = r \sin(\alpha + \theta)$&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 使用三角恒等式展开&lt;/h3&gt;
&lt;p&gt;利用高中的三角函数加法公式：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;$\cos(\alpha + \theta) = \cos\alpha \cos\theta - \sin\alpha \sin\theta$&lt;/p&gt;
&lt;p&gt;$\sin(\alpha + \theta) = \sin\alpha \cos\theta + \cos\alpha \sin\theta$&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我们将这两个展开式代入 $x&apos;$ 和 $y&apos;$ 的表达式中：&lt;/p&gt;
&lt;p&gt;$$x&apos; = r(\cos\alpha \cos\theta - \sin\alpha \sin\theta) = (r \cos\alpha) \cos\theta - (r \sin\alpha) \sin\theta$$&lt;/p&gt;
&lt;p&gt;$$y&apos; = r(\sin\alpha \cos\theta + \cos\alpha \sin\theta) = (r \sin\alpha) \cos\theta + (r \cos\alpha) \sin\theta$$&lt;/p&gt;
&lt;h3&gt;4. 代回原始坐标&lt;/h3&gt;
&lt;p&gt;记得第一步里的 $x = r \cos\alpha$ 和 $y = r \sin\alpha$ 吗？把它们替换进去，就得到了你看到的公式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;$x&apos; = x \cos\theta - y \sin\theta$&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$y&apos; = y \cos\theta + x \sin\theta$&lt;/strong&gt; （整理一下顺序即 $x \sin\theta + y \cos\theta$）&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;线性代数视角（更进一步）&lt;/h3&gt;
&lt;p&gt;在机器学习和 Transformer 的论文（如 RoPE）中，通常会写成矩阵乘法的形式，看起来更简洁：&lt;/p&gt;
&lt;p&gt;$$\begin{bmatrix} x&apos; \ y&apos; \end{bmatrix} = \begin{bmatrix} \cos\theta &amp;amp; -\sin\theta \ \sin\theta &amp;amp; \cos\theta \end{bmatrix} \begin{bmatrix} x \ y \end{bmatrix}$$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;为什么 RoPE 要用这个？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;因为这种旋转变换是&lt;strong&gt;线性&lt;/strong&gt;的，而且它有一个神奇的特性：&lt;strong&gt;它保持向量的长度（模长）不变，只改变方向。&lt;/strong&gt; 这使得模型在处理位置信息时，不会因为位置太远而导致向量数值爆炸。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;下面我给一个“能手算完”的玩具例子：5 个词、8 维（4 个 pair），分别算一遍不使用 RoPE 和使用 RoPE 的 $QK^\top$。我会把每一步都写出来（包括每个 pair 的旋转、每个距离的点积分解与求和），最后把 $5 \times 5$ 的分数矩阵写出来。&lt;/p&gt;
&lt;h3&gt;设定前提（为了让计算可控）&lt;/h3&gt;
&lt;p&gt;这些简化不影响你要理解的结论：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;位置&lt;/strong&gt;：5 个词的位置是 $p=0, 1, 2, 3, 4$&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;维度&lt;/strong&gt;：$d=8 \Rightarrow 4$ 个二维 pair：Pair 1 (维 1-2), Pair 2 (维 3-4), Pair 3 (维 5-6), Pair 4 (维 7-8)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;角速度&lt;/strong&gt;：每个 pair 的“每移动 1 步的旋转角速度”取一个简单的值（玩具版）：&lt;/p&gt;
&lt;p&gt;$$\omega_1=1, \quad \omega_2=0.5, \quad \omega_3=0.25, \quad \omega_4=0.125 \quad (\text{单位：弧度/位置})$$&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(注：真实 RoPE 用的是 $\omega_i=\theta^{-2i/d}$ 这种形式，但计算流程完全一样。)&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;最关键的简化&lt;/strong&gt;：所有 Token 的原始 $Q$ 和 $K$ 都取同一个向量：&lt;/p&gt;
&lt;p&gt;$$v=[1, 0, 1, 0, 1, 0, 1, 0]$$&lt;/p&gt;
&lt;p&gt;也就是每个 pair 都是 $[1, 0]$。这样 RoPE 后每个 pair 会变成 $[\cos(p\omega), \sin(p\omega)]$，点积能清晰地看到“只和距离 $\Delta=t-p$ 有关”。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;1. 不使用 RoPE：直接算 $QK^\top$&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1.1 先写出 $Q$ 和 $K$&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;5 个词都一样，所以：&lt;/p&gt;
&lt;p&gt;$$Q = \begin{bmatrix} v \ v \ v \ v \ v \end{bmatrix}, \quad K = \begin{bmatrix} v \ v \ v \ v \ v \end{bmatrix}$$&lt;/p&gt;
&lt;p&gt;其中 $v=[1, 0, 1, 0, 1, 0, 1, 0]$。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1.2 计算任意一个分数项 $(QK^\top)_{p,t} = Q_p \cdot K_t = v \cdot v$&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;把 8 维逐项相乘再求和：&lt;/p&gt;
&lt;p&gt;$$v \cdot v = 1\cdot1 + 0\cdot0 + 1\cdot1 + 0\cdot0 + 1\cdot1 + 0\cdot0 + 1\cdot1 + 0\cdot0 = 4$$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1.3 得出完整的 $QK^\top$ 矩阵&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;整个 $QK^\top$ 是一个全 4 的矩阵：&lt;/p&gt;
&lt;p&gt;$$QK^\top = \begin{bmatrix} 4 &amp;amp; 4 &amp;amp; 4 &amp;amp; 4 &amp;amp; 4 \ 4 &amp;amp; 4 &amp;amp; 4 &amp;amp; 4 &amp;amp; 4 \ 4 &amp;amp; 4 &amp;amp; 4 &amp;amp; 4 &amp;amp; 4 \ 4 &amp;amp; 4 &amp;amp; 4 &amp;amp; 4 &amp;amp; 4 \ 4 &amp;amp; 4 &amp;amp; 4 &amp;amp; 4 &amp;amp; 4 \end{bmatrix}$$&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;解释&lt;/strong&gt;：不带位置时，注意力分数完全不知道谁离谁近，所有位置一视同仁。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h3&gt;2. 使用 RoPE：先旋转，再算 $Q&apos;K&apos;^\top$&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;2.1 RoPE 对每个 pair 怎么旋转&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;对任意位置 $p$ 和任意一个 pair（角速度 $\omega$），二维旋转结果是：&lt;/p&gt;
&lt;p&gt;原始 pair 是 $[1, 0]$，旋转角为 $\theta=p\omega$，所以：&lt;/p&gt;
&lt;p&gt;$$[1, 0] \xrightarrow{R(p)} [\cos(p\omega), \sin(p\omega)]$$&lt;/p&gt;
&lt;p&gt;因此 token 在位置 $p$ 的 8 维向量（4 个 pair 拼起来）是：&lt;/p&gt;
&lt;p&gt;$$q&apos;_p = k&apos;_p = [\cos(p\omega_1), \sin(p\omega_1), \cos(p\omega_2), \sin(p\omega_2), \cos(p\omega_3), \sin(p\omega_3), \cos(p\omega_4), \sin(p\omega_4)]$$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2.2 算出 5 个位置的 $q&apos;_p$（四舍五入到 4 位小数）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;位置 $p=0$&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;$$q&apos;_0 = [1.0000, 0.0000, 1.0000, 0.0000, 1.0000, 0.0000, 1.0000, 0.0000]$$&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;位置 $p=1$&lt;/strong&gt;（角分别是 1, 0.5, 0.25, 0.125）：&lt;/p&gt;
&lt;p&gt;$$q&apos;_1 = [0.5403, 0.8415, 0.8776, 0.4794, 0.9689, 0.2474, 0.9922, 0.1247]$$&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;位置 $p=2$&lt;/strong&gt;（角分别是 2, 1, 0.5, 0.25）：&lt;/p&gt;
&lt;p&gt;$$q&apos;_2 = [-0.4161, 0.9093, 0.5403, 0.8415, 0.8776, 0.4794, 0.9689, 0.2474]$$&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;位置 $p=3$&lt;/strong&gt;（角分别是 3, 1.5, 0.75, 0.375）：&lt;/p&gt;
&lt;p&gt;$$q&apos;_3 = [-0.9900, 0.1411, 0.0707, 0.9975, 0.7317, 0.6816, 0.9305, 0.3663]$$&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;位置 $p=4$&lt;/strong&gt;（角分别是 4, 2, 1, 0.5）：&lt;/p&gt;
&lt;p&gt;$$q&apos;_4 = [-0.6536, -0.7568, -0.4161, 0.9093, 0.5403, 0.8415, 0.8776, 0.4794]$$&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(因为我们设定了 $Q=K$，所以 $k&apos;_p=q&apos;_p$)&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;3. 一步一步计算 $Q&apos;K&apos;^\top$&lt;/h3&gt;
&lt;p&gt;分数矩阵元素是：$(Q&apos;K&apos;^\top)_{p,t} = q&apos;_p \cdot k&apos;_t = q&apos;_p \cdot q&apos;_t$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3.1 先完整算一个具体例子：$(p=0, t=1)$&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;把 8 维逐项乘加。因为 $q&apos;_0$ 的每个 pair 都是 $[1, 0]$，所以每个 pair 只取到了对方的 $\cos$ 分量：&lt;/p&gt;
&lt;p&gt;$$q&apos;_0 \cdot q&apos;_1 = 0.5403 + 0.8776 + 0.9689 + 0.9922 \approx 3.3790$$&lt;/p&gt;
&lt;p&gt;所以 $(Q&apos;K&apos;^\top)_{0,1} \approx 3.3790$。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3.2 再算一个“不是从全 1 取 $\cos$”的例子：$(p=1, t=2)$&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这里必须把每个 pair 都做完：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Pair 1 ($\omega_1=1$)&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;$$[0.5403, 0.8415] \cdot [-0.4161, 0.9093] = -0.2248 + 0.7652 \approx 0.5403$$&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(理论值即 $\cos(1)$)&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Pair 2 ($\omega_2=0.5$)&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;$$[0.8776, 0.4794] \cdot [0.5403, 0.8415] = 0.4742 + 0.4034 \approx 0.8776$$&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Pair 3 ($\omega_3=0.25$)&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;$$[0.9689, 0.2474] \cdot [0.8776, 0.4794] = 0.8503 + 0.1186 \approx 0.9689$$&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Pair 4 ($\omega_4=0.125$)&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;$$[0.9922, 0.1247] \cdot [0.9689, 0.2474] = 0.9613 + 0.0309 \approx 0.9922$$&lt;/p&gt;
&lt;p&gt;把 4 个 pair 加起来：&lt;/p&gt;
&lt;p&gt;$$q&apos;_1 \cdot q&apos;_2 \approx 0.5403 + 0.8776 + 0.9689 + 0.9922 = 3.3790$$&lt;/p&gt;
&lt;p&gt;你会发现：距离 $\Delta=1$ 的 $(1, 2)$ 得分与 $(0, 1)$ 完全一样！&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;4. 把“所有距离”都算出来，然后拼成 $5 \times 5$ 矩阵&lt;/h3&gt;
&lt;p&gt;由于每个 pair 满足三角恒等式：&lt;/p&gt;
&lt;p&gt;$$\cos(p\omega)\cos(t\omega) + \sin(p\omega)\sin(t\omega) = \cos((t-p)\omega)$$&lt;/p&gt;
&lt;p&gt;总分数公式直接化简为：&lt;/p&gt;
&lt;p&gt;$$\text{Score}(p,t) = \sum_{i=1}^4 \cos((t-p)\omega_i)$$&lt;/p&gt;
&lt;p&gt;接下来我们对每个距离 $\Delta = |t-p|$ 进行求和：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;$\Delta=0$&lt;/strong&gt;：$S_0 = 1 + 1 + 1 + 1 = 4.0000$&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$\Delta=1$&lt;/strong&gt;：$S_1 = 0.5403 + 0.8776 + 0.9689 + 0.9922 = 3.3790$&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$\Delta=2$&lt;/strong&gt;：$S_2 = -0.4161 + 0.5403 + 0.8776 + 0.9689 = 1.9707$&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$\Delta=3$&lt;/strong&gt;：$S_3 = -0.9900 + 0.0707 + 0.7317 + 0.9305 = 0.7429$&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$\Delta=4$&lt;/strong&gt;：$S_4 = -0.6536 - 0.4161 + 0.5403 + 0.8776 = 0.3481$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最终拼出的矩阵（每条对角线相同，即 Toeplitz 矩阵）：&lt;/p&gt;
&lt;p&gt;$$Q&apos;K&apos;^\top = \begin{bmatrix} S_0 &amp;amp; S_1 &amp;amp; S_2 &amp;amp; S_3 &amp;amp; S_4 \ S_1 &amp;amp; S_0 &amp;amp; S_1 &amp;amp; S_2 &amp;amp; S_3 \ S_2 &amp;amp; S_1 &amp;amp; S_0 &amp;amp; S_1 &amp;amp; S_2 \ S_3 &amp;amp; S_2 &amp;amp; S_1 &amp;amp; S_0 &amp;amp; S_1 \ S_4 &amp;amp; S_3 &amp;amp; S_2 &amp;amp; S_1 &amp;amp; S_0 \end{bmatrix} \approx \begin{bmatrix} 4.0000 &amp;amp; 3.3790 &amp;amp; 1.9707 &amp;amp; 0.7429 &amp;amp; 0.3481 \ 3.3790 &amp;amp; 4.0000 &amp;amp; 3.3790 &amp;amp; 1.9707 &amp;amp; 0.7429 \ 1.9707 &amp;amp; 3.3790 &amp;amp; 4.0000 &amp;amp; 3.3790 &amp;amp; 1.9707 \ 0.7429 &amp;amp; 1.9707 &amp;amp; 3.3790 &amp;amp; 4.0000 &amp;amp; 3.3790 \ 0.3481 &amp;amp; 0.7429 &amp;amp; 1.9707 &amp;amp; 3.3790 &amp;amp; 4.0000 \end{bmatrix}$$&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;5. 你要的“距离长短”到底在哪里&lt;/h3&gt;
&lt;p&gt;对比两种结果：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;不使用 RoPE&lt;/strong&gt;：所有位置之间分数都一样（全是 4.0000），模型从 $QK^\top$ 里完全看不出距离。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;使用 RoPE&lt;/strong&gt;：分数随距离 $|t-p|$ 的增大而稳步下降（4.0000 $\rightarrow$ 3.3790 $\rightarrow$ 1.9707 $\rightarrow$ 0.7429 $\rightarrow$ 0.3481），距离信息就完美体现在了最终的 $Q&apos;K&apos;^\top$ 数值衰减里。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
</content:encoded></item><item><title>uvloop</title><link>https://lxy-alexander.github.io/blog/posts/python/uvloop/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/python/uvloop/</guid><description>uvloop</description><pubDate>Tue, 03 Mar 2026 00:00:00 GMT</pubDate><content:encoded/></item><item><title>Tuple</title><link>https://lxy-alexander.github.io/blog/posts/python/tuple/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/python/tuple/</guid><description>Tuple</description><pubDate>Sun, 01 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;📌 元组（Tuple）&lt;/h2&gt;
&lt;p&gt;A tuple is an ordered and immutable collection of elements.&lt;/p&gt;
&lt;p&gt;Features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ordered&lt;/li&gt;
&lt;li&gt;Immutable (cannot be changed after creation)&lt;/li&gt;
&lt;li&gt;Can contain different data types&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;t = (1, 2, 3)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Single-element tuple:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;t = (5,)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tuples are immutable:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;t[0] = 10  # Error
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;🔹 Tuple vs List&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Tuple&lt;/th&gt;
&lt;th&gt;List&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;符号&lt;/td&gt;
&lt;td&gt;&lt;code&gt;()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;是否可修改&lt;/td&gt;
&lt;td&gt;❌ 不可改&lt;/td&gt;
&lt;td&gt;✅ 可改&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;用途&lt;/td&gt;
&lt;td&gt;固定数据&lt;/td&gt;
&lt;td&gt;可变数据&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
</content:encoded></item><item><title>ccache</title><link>https://lxy-alexander.github.io/blog/posts/llm/ccache/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/llm/ccache/</guid><description>ccache</description><pubDate>Wed, 11 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;I. ccache — Compiler Cache (编译缓存)&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;ccache wraps your compiler (&lt;code&gt;gcc&lt;/code&gt;, &lt;code&gt;g++&lt;/code&gt;, &lt;code&gt;nvcc&lt;/code&gt;) and caches object files. Same source + same flags = instant replay, no recompilation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;1. How It Works (工作原理)&lt;/h2&gt;
&lt;p&gt;$$ \text{Cache Key (缓存键)} = \text{Hash}(\text{source content} + \text{flags} + \text{compiler version} + \text{headers}) $$&lt;/p&gt;
&lt;p&gt;On a &lt;strong&gt;hit (命中)&lt;/strong&gt;: return cached &lt;code&gt;.o&lt;/code&gt; file immediately. On a &lt;strong&gt;miss (未命中)&lt;/strong&gt;: compile normally, store result.&lt;/p&gt;
&lt;p&gt;Two modes: &lt;strong&gt;Direct mode (直接模式)&lt;/strong&gt; — fastest, hashes source directly. &lt;strong&gt;Preprocessed mode (预处理模式)&lt;/strong&gt; — slower, more accurate, used as fallback.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Installation (无root安装)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;# Download &amp;amp; link
wget https://github.com/ccache/ccache/releases/download/v4.10.2/ccache-4.10.2-linux-x86_64.tar.xz
tar xf ccache-4.10.2-linux-x86_64.tar.xz -C $HOME/local
ln -s $HOME/local/ccache-4.10.2-linux-x86_64/ccache $HOME/.local/bin/ccache
echo &apos;export PATH=&quot;$HOME/.local/bin:$PATH&quot;&apos; &amp;gt;&amp;gt; ~/.bashrc &amp;amp;&amp;amp; source ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;3. Integration (接入构建系统)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;# CMake projects
cmake -DCMAKE_C_COMPILER_LAUNCHER=ccache \
      -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
      -DCMAKE_CUDA_COMPILER_LAUNCHER=ccache ..

# pip installs (e.g. vLLM)
export CMAKE_C_COMPILER_LAUNCHER=ccache
export CMAKE_CXX_COMPILER_LAUNCHER=ccache
export CMAKE_CUDA_COMPILER_LAUNCHER=ccache
pip install -e .
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;4. Reading Stats (读懂统计)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;ccache -s
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Hits = 0% on 1st build&lt;/td&gt;
&lt;td&gt;Normal — cache is cold (冷缓存), next build hits ~100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hits = 0% on 2nd build&lt;/td&gt;
&lt;td&gt;ccache &lt;strong&gt;not intercepting&lt;/strong&gt; — check &lt;code&gt;CMAKE_*_LAUNCHER&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cleanups: 240&lt;/td&gt;
&lt;td&gt;Cache limit too small → run &lt;code&gt;ccache --max-size=20G&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;5. HPC-Specific Tips (HPC注意事项)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;# Avoid $HOME quota — move cache to scratch (临时文件系统)
export CCACHE_DIR=/scratch/$USER/.ccache

# Normalize absolute paths across nodes (跨节点路径归一化)
export CCACHE_BASEDIR=$HOME

# Increase size limit (扩大缓存上限)
ccache --max-size=20G
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;6. Quick Reference (命令速查)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;ccache -s              # stats (统计)
ccache -z              # reset stats (重置)
ccache -C              # clear cache (清空)
CCACHE_DISABLE=1 make  # disable temporarily (临时禁用)
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Linux&amp;Slurm Common Command</title><link>https://lxy-alexander.github.io/blog/posts/tools/linuxslurm-common-command/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/tools/linuxslurm-common-command/</guid><description>Linux&amp;Slurm Common Command</description><pubDate>Wed, 11 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;&lt;strong&gt;I. HPC Resource Inspection Commands — Linux &amp;amp; SLURM&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt;
&amp;lt;strong&amp;gt;Overview:&amp;lt;/strong&amp;gt; These are essential Linux and &amp;lt;strong&amp;gt;SLURM (Simple Linux Utility for Resource Management)&amp;lt;/strong&amp;gt; commands used on HPC clusters to inspect &amp;lt;strong&amp;gt;memory, CPU, GPU resources, and job status&amp;lt;/strong&amp;gt;.
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1. &lt;code&gt;lscpu&lt;/code&gt; — CPU Hardware Info&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;lscpu
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Displays &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;CPU hardware information&amp;lt;/span&amp;gt;: core count, clock frequency, NUMA topology, cache sizes, and more.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. &lt;code&gt;scontrol show node ... | grep -i gres&lt;/code&gt; — Node GPU Resources&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;scontrol show node ghpc008 | grep -i gres
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Shows the node&apos;s &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;GPU resources&amp;lt;/span&amp;gt; — the number and type of GPUs allocated or available on that node.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. &lt;code&gt;top&lt;/code&gt; — Real-time Process Monitor&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;top
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Displays a live, continuously refreshed view of &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;running processes, CPU usage, and memory usage&amp;lt;/span&amp;gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4. &lt;code&gt;scontrol show job $SLURM_JOB_ID&lt;/code&gt; — Current Job Details&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;scontrol show job $SLURM_JOB_ID
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Displays &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;detailed information about the current SLURM job&amp;lt;/span&amp;gt;, including assigned CPUs, GPUs, memory allocation, target node, and runtime status.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt; Use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;lscpu&amp;lt;/code&amp;gt; for CPU specs, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;scontrol show node ... | grep gres&amp;lt;/code&amp;gt; for GPU inventory, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;top&amp;lt;/code&amp;gt; for live process monitoring, and &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;scontrol show job&amp;lt;/code&amp;gt; to inspect your running SLURM job&apos;s resource allocation.&amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Nvidia System Management Interface</title><link>https://lxy-alexander.github.io/blog/posts/tools/nvidia-system-management-interface/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/tools/nvidia-system-management-interface/</guid><description>Nvidia System Management Interface</description><pubDate>Wed, 11 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;&lt;strong&gt;I. &lt;code&gt;nvidia-smi&lt;/code&gt; — NVIDIA System Management Interface&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt;
&amp;lt;strong&amp;gt;Overview:&amp;lt;/strong&amp;gt; &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;nvidia-smi&amp;lt;/code&amp;gt; is the &amp;lt;strong&amp;gt;NVIDIA System Management Interface (系统管理接口)&amp;lt;/strong&amp;gt;. It is used to monitor and manage GPU device status, including GPU memory usage, GPU utilization, temperature, and power consumption.
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1. Show Overall GPU Status&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;nvidia-smi
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Most commonly used to &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;quickly check whether GPUs are idle or busy&amp;lt;/span&amp;gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Monitor in Real Time&lt;/h2&gt;
&lt;p&gt;Refresh every second:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nvidia-smi -l 1
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;3. List All GPUs&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;nvidia-smi -L
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;4. Show Processes Using GPUs&lt;/h2&gt;
&lt;p&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;pmon&amp;lt;/code&amp;gt; = &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;process monitor&amp;lt;/span&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nvidia-smi pmon
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Refresh every 2 seconds:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nvidia-smi pmon -d 2
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;1) &lt;code&gt;pmon&lt;/code&gt; Column Reference&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Column&lt;/th&gt;
&lt;th&gt;Full Name&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;pid&amp;lt;/span&amp;gt;&lt;/td&gt;
&lt;td&gt;Process ID&lt;/td&gt;
&lt;td&gt;The Linux process ID using the GPU&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;type&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Process type&lt;/td&gt;
&lt;td&gt;GPU workload type: &lt;strong&gt;C&lt;/strong&gt; = Compute (CUDA), &lt;strong&gt;G&lt;/strong&gt; = Graphics, &lt;strong&gt;C+G&lt;/strong&gt; = Both&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;sm&amp;lt;/span&amp;gt;&lt;/td&gt;
&lt;td&gt;Streaming Multiprocessor utilization&lt;/td&gt;
&lt;td&gt;Percentage of &lt;strong&gt;GPU compute cores&lt;/strong&gt; being used by the process&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;mem&amp;lt;/span&amp;gt;&lt;/td&gt;
&lt;td&gt;Memory controller utilization&lt;/td&gt;
&lt;td&gt;Percentage of &lt;strong&gt;GPU memory bandwidth&lt;/strong&gt; used by the process&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;enc&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Encoder utilization&lt;/td&gt;
&lt;td&gt;Usage of the &lt;strong&gt;NVENC video encoder&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;dec&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Decoder utilization&lt;/td&gt;
&lt;td&gt;Usage of the &lt;strong&gt;NVDEC video decoder&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;jpg&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;JPEG engine utilization&lt;/td&gt;
&lt;td&gt;Usage of the &lt;strong&gt;hardware JPEG decoder/encoder&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ofa&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Optical Flow Accelerator utilization&lt;/td&gt;
&lt;td&gt;Usage of the &lt;strong&gt;hardware optical-flow engine&lt;/strong&gt; (video/vision tasks)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;fb&amp;lt;/span&amp;gt;&lt;/td&gt;
&lt;td&gt;Frame Buffer memory&lt;/td&gt;
&lt;td&gt;Amount of &lt;strong&gt;GPU VRAM used&lt;/strong&gt; by the process (in MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ccpm&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Compute &amp;amp; Copy Engine / Protected Memory&lt;/td&gt;
&lt;td&gt;Internal GPU engine / protection state info; often &lt;strong&gt;0&lt;/strong&gt; on most systems&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Key columns to watch: &amp;lt;/span&amp;gt;
&amp;lt;strong&amp;gt;pid&amp;lt;/strong&amp;gt; — shows which process is using the GPU.
&amp;lt;strong&amp;gt;sm&amp;lt;/strong&amp;gt; — indicates whether GPU cores are actively computing.
&amp;lt;strong&amp;gt;fb&amp;lt;/strong&amp;gt; — VRAM usage in MB; shows how much memory is consumed.
&amp;lt;strong&amp;gt;mem&amp;lt;/strong&amp;gt; — memory bandwidth utilization; indicates I/O pressure on GPU memory.
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;2) Example Output&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;[xli49@ghpc008 ~]$ nvidia-smi pmon -i 0 -s um
# gpu         pid   type     sm    mem    enc    dec    jpg    ofa     fb   ccpm    command
# Idx           #    C/G      %      %      %      %      %      %     MB     MB    name
    0          -     -      -      -      -      -      -      -      -      -    -
    0          -     -      -      -      -      -      -      -      -      -    -

[xli49@ghpc008 ~]$ nvidia-smi pmon -i 0
# gpu         pid   type     sm    mem    enc    dec    jpg    ofa    command
# Idx           #    C/G      %      %      %      %      %      %    name
    0          -     -      -      -      -      -      -      -    -
    0          -     -      -      -      -      -      -      -    -
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All dashes (&lt;code&gt;-&lt;/code&gt;) indicate &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;GPU 0 is currently idle&amp;lt;/span&amp;gt; — no processes are running on it.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;5. Custom Query of GPU Information&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;nvidia-smi --query-gpu=name,memory.used,utilization.gpu --format=csv
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Commonly used for scripts, logging, and automated monitoring.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;6. Log GPU Status to a File&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;nvidia-smi -l 5 -f gpu.log
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Records GPU information every &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;5 seconds&amp;lt;/span&amp;gt; and appends it to &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;gpu.log&amp;lt;/code&amp;gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt; Use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;nvidia-smi&amp;lt;/code&amp;gt; for a quick snapshot, &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;nvidia-smi pmon&amp;lt;/code&amp;gt; to watch per-process GPU activity in real time, and &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;--query-gpu&amp;lt;/code&amp;gt; for scriptable, structured output — focus on &amp;lt;strong&amp;gt;pid&amp;lt;/strong&amp;gt;, &amp;lt;strong&amp;gt;sm&amp;lt;/strong&amp;gt;, &amp;lt;strong&amp;gt;fb&amp;lt;/strong&amp;gt;, and &amp;lt;strong&amp;gt;mem&amp;lt;/strong&amp;gt; for the most actionable signals.&amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Bash Shell Script</title><link>https://lxy-alexander.github.io/blog/posts/tools/bash-shell-script/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/tools/bash-shell-script/</guid><description>Bash Shell Script</description><pubDate>Sat, 07 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;&lt;strong&gt;I. Bash Shell Script Fundamentals&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Bash Shell Script (Bash脚本)&amp;lt;/span&amp;gt; is a scripting language executed by the &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Unix Shell (Unix命令解释器)&amp;lt;/span&amp;gt;. It is widely used for system automation, environment configuration, and workflow management on &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Linux Systems (Linux系统)&amp;lt;/span&amp;gt; and &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;High Performance Computing (高性能计算, HPC)&amp;lt;/span&amp;gt; environments.&lt;/p&gt;
&lt;p&gt;Unlike compiled languages, Bash follows an &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Interpreter Model (解释执行模型)&amp;lt;/span&amp;gt;, meaning commands are executed line by line by the shell.&lt;/p&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;&amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;1.&amp;lt;/span&amp;gt; &lt;strong&gt;What is a Bash Script&lt;/strong&gt;&lt;/h2&gt;
&lt;h3&gt;1) Core Definition&lt;/h3&gt;
&lt;p&gt;A &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Bash Script (Bash脚本)&amp;lt;/span&amp;gt; is a text file containing a sequence of shell commands that are executed by the &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Bash Interpreter (Bash解释器)&amp;lt;/span&amp;gt;.&lt;/p&gt;
&lt;p&gt;Its main purposes include:&lt;/p&gt;
&lt;p&gt;1）Automating repetitive command execution
2）Managing &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Environment Variables (环境变量)&amp;lt;/span&amp;gt;
3）Controlling program flow using &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Conditional Statements (条件语句)&amp;lt;/span&amp;gt; and &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Loops (循环)&amp;lt;/span&amp;gt;
4）Serving as initialization scripts in Linux and HPC environments&lt;/p&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;Therefore&amp;lt;/span&amp;gt;, Bash acts as a bridge between system commands and automated workflows.&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;
&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt;
Bash is a &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Scripting Language (脚本语言)&amp;lt;/span&amp;gt;, not a compiled language. Scripts are interpreted directly by the shell rather than compiled into machine code.
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;&amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;2.&amp;lt;/span&amp;gt; &lt;strong&gt;Script Execution Methods&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;There are three common ways to run a Bash script.&lt;/p&gt;
&lt;h3&gt;1) Direct Execution with Bash&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;bash script.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This launches a &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Subshell (子Shell)&amp;lt;/span&amp;gt; to execute the script.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;2) Executable Script&lt;/h3&gt;
&lt;p&gt;First give execution permission:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;chmod +x script.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then run the script:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;./script.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This uses the system&apos;s &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Shebang (解释器声明)&amp;lt;/span&amp;gt; if defined.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/bin/bash
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;3) Source Execution&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;source script.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;. script.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This executes the script inside the &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Current Shell (当前Shell)&amp;lt;/span&amp;gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;Execution Behavior Comparison&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;New Subshell (子Shell)&lt;/th&gt;
&lt;th&gt;Variables Persist (变量保留)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;bash script.sh&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;./script.sh&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;source script.sh&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;
&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt;
The &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;source Command (source命令)&amp;lt;/span&amp;gt; is commonly used when configuring shell environments such as &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;.bashrc&amp;lt;/code&amp;gt;, CUDA paths, or Conda environments.
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;&lt;strong&gt;II. Basic Bash Syntax&lt;/strong&gt;&lt;/h1&gt;
&lt;h2&gt;&amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;1.&amp;lt;/span&amp;gt; &lt;strong&gt;Variables and Environment Variables&lt;/strong&gt;&lt;/h2&gt;
&lt;h3&gt;1) Normal Variables&lt;/h3&gt;
&lt;p&gt;Variables are assigned without spaces around the equals sign.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;name=&quot;Alice&quot;
echo $name
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Variable Assignment (变量赋值)&amp;lt;/span&amp;gt; defines the variable&lt;/li&gt;
&lt;li&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Variable Expansion (变量展开)&amp;lt;/span&amp;gt; occurs using &lt;code&gt;$&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;2) Environment Variables&lt;/h3&gt;
&lt;p&gt;Environment variables are exported using the &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;export&amp;lt;/code&amp;gt; command.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export PATH=/usr/bin:$PATH
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Properties:&lt;/p&gt;
&lt;p&gt;1）Visible to &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Child Processes (子进程)&amp;lt;/span&amp;gt;
2）Frequently used for software configuration such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;PATH&amp;lt;/span&amp;gt;&lt;/li&gt;
&lt;li&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;CUDA&amp;lt;/span&amp;gt;&lt;/li&gt;
&lt;li&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Conda&amp;lt;/span&amp;gt;&lt;/li&gt;
&lt;li&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;MPI&amp;lt;/span&amp;gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;&amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;2.&amp;lt;/span&amp;gt; &lt;strong&gt;Conditional Statements&lt;/strong&gt;&lt;/h2&gt;
&lt;h3&gt;1) Basic &lt;code&gt;if&lt;/code&gt; Structure&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;if condition; then
    statement
fi
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if [ -f file.txt ]; then
    echo &quot;file exists&quot;
fi
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here the brackets use the &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Test Command (测试命令)&amp;lt;/span&amp;gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;2) Common File Test Operators&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operator&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;-f&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;regular file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;-d&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;-e&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;exists&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;-x&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;executable&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;These belong to the &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;POSIX Test Syntax (POSIX测试语法)&amp;lt;/span&amp;gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;&amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;3.&amp;lt;/span&amp;gt; &lt;strong&gt;Loop Structures&lt;/strong&gt;&lt;/h2&gt;
&lt;h3&gt;1) &lt;code&gt;for&lt;/code&gt; Loop&lt;/h3&gt;
&lt;p&gt;The most common loop used to iterate through lists or files.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;for f in *.txt; do
    echo $f
done
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Typical use cases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;File iteration&lt;/li&gt;
&lt;li&gt;Command results&lt;/li&gt;
&lt;li&gt;Parameter lists&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;2) &lt;code&gt;while&lt;/code&gt; Loop&lt;/h3&gt;
&lt;p&gt;Often used for reading files line by line.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;while read line; do
    echo $line
done &amp;lt; file.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This uses &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Input Redirection (输入重定向)&amp;lt;/span&amp;gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;&lt;strong&gt;III. Command Execution Model&lt;/strong&gt;&lt;/h1&gt;
&lt;h2&gt;&amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;1.&amp;lt;/span&amp;gt; &lt;strong&gt;Subshell Execution&lt;/strong&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;bash script.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Characteristics:&lt;/p&gt;
&lt;p&gt;1）Creates a new &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Process (进程)&amp;lt;/span&amp;gt;
2）Variables do not affect the current shell&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;&amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;2.&amp;lt;/span&amp;gt; &lt;strong&gt;Source Execution&lt;/strong&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;source script.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;. script.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Purpose:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Execute script content in the &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Current Shell Environment (当前Shell环境)&amp;lt;/span&amp;gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Typical usage scenarios:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;.bashrc&amp;lt;/code&amp;gt;&lt;/li&gt;
&lt;li&gt;environment setup&lt;/li&gt;
&lt;li&gt;CUDA configuration&lt;/li&gt;
&lt;li&gt;HPC module initialization&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h1&gt;&lt;strong&gt;IV. File and Path Operations&lt;/strong&gt;&lt;/h1&gt;
&lt;h2&gt;&amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;1.&amp;lt;/span&amp;gt; &lt;strong&gt;File Testing&lt;/strong&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;[ -d dir ]
[ -f file ]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These checks are part of the &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;POSIX Test System (POSIX测试系统)&amp;lt;/span&amp;gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;&amp;lt;span style=&quot;color:#E8600A&quot;&amp;gt;2.&amp;lt;/span&amp;gt; &lt;strong&gt;Path Expansion and Wildcards&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;~/.bashrc.d/*
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Meaning:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symbol&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;~&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;user home directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;wildcard matching all files&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;This mechanism is called &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Filename Expansion (文件名扩展)&amp;lt;/span&amp;gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h1&gt;&lt;strong&gt;V. Modular Configuration Example&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;A typical &lt;code&gt;.bashrc&lt;/code&gt; uses modular configuration loading.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if [ -d ~/.bashrc.d ]; then
    for rc in ~/.bashrc.d/*; do
        if [ -f &quot;$rc&quot; ]; then
            . &quot;$rc&quot;
        fi
    done
fi
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Explanation:&lt;/p&gt;
&lt;p&gt;1）Check whether the directory exists
2）Iterate through each script file
3）Execute each script using &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;source&amp;lt;/code&amp;gt;&lt;/p&gt;
&lt;p&gt;This allows a &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Modular Configuration System (模块化配置系统)&amp;lt;/span&amp;gt;.&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;
&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt;
This design pattern is widely used in Linux distributions and &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;HPC Initialization Scripts (HPC初始化脚本)&amp;lt;/span&amp;gt; to organize configuration files into reusable modules.
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Bash Scripts (Bash脚本)&amp;lt;/span&amp;gt; provide a powerful automation mechanism for Linux systems by combining command execution, environment configuration, and control flow using an interpreter-based shell environment.&lt;/p&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Linux Tools</title><link>https://lxy-alexander.github.io/blog/posts/tools/linux-tools/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/tools/linux-tools/</guid><description>Linux Tools</description><pubDate>Sat, 07 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;&lt;strong&gt;I. HPC Setup Without &lt;code&gt;sudo&lt;/code&gt; — CUDA, CMake &amp;amp; &lt;code&gt;.bashrc&lt;/code&gt; Configuration&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt;
&amp;lt;strong&amp;gt;Overview:&amp;lt;/strong&amp;gt; This guide covers three tasks on HPC clusters where you have no root access: installing the &amp;lt;strong&amp;gt;CUDA Toolkit&amp;lt;/strong&amp;gt; to your home directory without &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;sudo&amp;lt;/code&amp;gt;; installing &amp;lt;strong&amp;gt;CMake&amp;lt;/strong&amp;gt; locally from a pre-built binary; and structuring your &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;~/.bashrc&amp;lt;/code&amp;gt; with a modular &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;~/.bashrc.d/&amp;lt;/code&amp;gt; system for managing CUDA versions, libtorch, cuDNN, and custom paths.
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1. Installing CUDA Without &lt;code&gt;sudo&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;CUDA only truly requires two things: the &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;nvcc compiler&amp;lt;/span&amp;gt; and user-space libraries like &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;libcudart&amp;lt;/span&amp;gt;. Both can be installed entirely inside your home directory:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$HOME/cuda
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;1) Check Your Linux Distribution and Architecture&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;cat /etc/os-release
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;NAME=&quot;Rocky Linux&quot;
VERSION=&quot;9.7 (Blue Onyx)&quot;
ID=&quot;rocky&quot;
...
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;uname -m
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;x86_64
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) Download the Runfile from NVIDIA&lt;/h3&gt;
&lt;p&gt;Go to: &lt;a href=&quot;https://developer.nvidia.com/cuda-downloads&quot;&gt;https://developer.nvidia.com/cuda-downloads&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Select your OS/architecture and choose the &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;runfile (local)&amp;lt;/span&amp;gt; installer type.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-c69d652d2a0747fab9aad1fab48ff742.r2.dev/images/image-20260207201609158&quot; alt=&quot;CUDA download page&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The downloaded file will be named something like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cuda_12.9.1_575.57.08_linux.run
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; To install an older CUDA version, visit the archive at &amp;lt;a href=&quot;https://developer.nvidia.com/cuda-toolkit-archive&quot;&amp;gt;https://developer.nvidia.com/cuda-toolkit-archive&amp;lt;/a&amp;gt;. Make sure the CUDA version you choose is &amp;lt;strong&amp;gt;less than or equal to&amp;lt;/strong&amp;gt; the CUDA Driver version reported by &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;nvidia-smi&amp;lt;/code&amp;gt;.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;3) Install the Toolkit Only (No Driver)&lt;/h3&gt;
&lt;p&gt;Make the runfile executable:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;chmod +x cuda_*.run
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run the installer with driver installation disabled:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;./cuda_12.9*.run \
  --silent \
  --toolkit \
  --toolkitpath=$HOME/cuda-12.9 \
  --no-drm \
  --no-man-page
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Flag&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;--silent&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Non-interactive installation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;--toolkit&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Install CUDA Toolkit only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;--toolkitpath&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Target installation directory (user home)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No driver flag&lt;/td&gt;
&lt;td&gt;Avoids any root requirement&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;4) Configure Environment Variables (Modular &lt;code&gt;.bashrc.d&lt;/code&gt;)&lt;/h3&gt;
&lt;p&gt;Create the directory:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mkdir -p ~/.bashrc.d
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a CUDA config file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nano ~/.bashrc.d/cuda.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Write the following:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# ===== Default CUDA =====
export CUDA_HOME=$HOME/cuda-12.9
export PATH=$CUDA_HOME/bin:$PATH
export LD_LIBRARY_PATH=$CUDA_HOME/lib64:$LD_LIBRARY_PATH

# ===== CUDA version switcher =====
use_cuda () {
    local ver=$1

    if [ ! -d &quot;$HOME/cuda-$ver&quot; ]; then
        echo &quot;CUDA $ver not found in \$HOME&quot;
        return 1
    fi

    export CUDA_HOME=$HOME/cuda-$ver
    export PATH=$CUDA_HOME/bin:$PATH
    export LD_LIBRARY_PATH=$CUDA_HOME/lib64:$LD_LIBRARY_PATH

    echo &quot;Switched to CUDA $ver&quot;
    nvcc --version | head -n 1
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ensure &lt;code&gt;~/.bashrc&lt;/code&gt; loads all files in &lt;code&gt;~/.bashrc.d/&lt;/code&gt; (add if not present):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if [ -d ~/.bashrc.d ]; then
    for rc in ~/.bashrc.d/*; do
        [ -f &quot;$rc&quot; ] &amp;amp;&amp;amp; . &quot;$rc&quot;
    done
fi
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reload the environment:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;source ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5) Verify the Installation&lt;/h3&gt;
&lt;p&gt;Check the compiler:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nvcc -V
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If a CUDA version string is printed, the toolkit is installed correctly.&lt;/p&gt;
&lt;p&gt;Check GPU availability:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nvidia-smi
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;This is a critical and often overlooked distinction.&amp;lt;/span&amp;gt; If &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;nvidia-smi&amp;lt;/code&amp;gt; runs successfully, the server already has a GPU driver installed and you can use the GPU. If it fails, the GPU driver is missing — without &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;sudo&amp;lt;/code&amp;gt; you cannot install the driver yourself, meaning CUDA can only be used for compilation, not for GPU execution.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Installing CMake Locally (No Root)&lt;/h2&gt;
&lt;h3&gt;1) Download the Official Pre-built Installer&lt;/h3&gt;
&lt;p&gt;The official binary installer requires no source compilation, no &lt;code&gt;gcc&lt;/code&gt; or &lt;code&gt;make&lt;/code&gt;, installs quickly, and is compatible with most Linux environments.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cd ~
wget https://github.com/Kitware/CMake/releases/download/v3.29.6/cmake-3.29.6-linux-x86_64.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) Install to Your User Directory&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;bash cmake-3.29.6-linux-x86_64.sh --skip-license --prefix=$HOME/.local
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Flag&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;--skip-license&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Skip the interactive license confirmation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;--prefix=$HOME/.local&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;Install into the user-level software directory (Linux convention)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;3) Add to &lt;code&gt;PATH&lt;/code&gt; and Verify&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;echo &apos;export PATH=$HOME/.local/bin:$PATH&apos; &amp;gt;&amp;gt; ~/.bashrc
source ~/.bashrc
cmake --version
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;3. &lt;code&gt;.bash_profile&lt;/code&gt; — Auto-load &lt;code&gt;.bashrc&lt;/code&gt; on Login&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;# .bash_profile

# Load aliases and functions from .bashrc
if [ -f ~/.bashrc ]; then
    . ~/.bashrc
fi
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This ensures &lt;code&gt;.bashrc&lt;/code&gt; is sourced automatically on every SSH login session.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4. &lt;code&gt;.bashrc&lt;/code&gt; Section-by-Section Walkthrough&lt;/h2&gt;
&lt;h3&gt;1) System Initialization&lt;/h3&gt;
&lt;p&gt;Loads the system-level bash configuration (modules, colors, completions, etc.):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if [ -f /etc/bashrc ]; then
    . /etc/bashrc
fi
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) User &lt;code&gt;PATH&lt;/code&gt; Initialization&lt;/h3&gt;
&lt;p&gt;Adds user program directories to &lt;code&gt;PATH&lt;/code&gt; without creating duplicates:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Only add if not already present (prevents duplicate PATH entries)
if ! [[ &quot;$PATH&quot; =~ &quot;$HOME/.local/bin:$HOME/bin:&quot; ]]
then
    # Prepend user-level bin dirs so locally installed tools take priority
    PATH=&quot;$HOME/.local/bin:$HOME/bin:$PATH&quot;
fi

# Export so child processes (Python, bash, etc.) inherit this PATH
export PATH
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) The &lt;code&gt;.bashrc.d&lt;/code&gt; Modular Config System&lt;/h3&gt;
&lt;p&gt;Splits shell configuration into separate, focused files. &amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;Recommended when managing&amp;lt;/span&amp;gt; multiple CUDA versions, multiple Conda environments, multi-project research, or many custom aliases.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Load all user config modules from ~/.bashrc.d/
if [ -d ~/.bashrc.d ]; then
    for rc in ~/.bashrc.d/*; do
        # Only source regular files (not directories or other types)
        if [ -f &quot;$rc&quot; ]; then
            # Source the file — equivalent to: source &quot;$rc&quot;
            # Makes aliases, functions, and exports take effect immediately
            . &quot;$rc&quot;
        fi
    done
fi
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4) PATH and Library Configuration&lt;/h3&gt;
&lt;h4&gt;CUDA 12.6 Environment&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;# ===== CUDA 12.6 =====
export CUDA_HOME=$HOME/cuda-12.6
export PATH=$CUDA_HOME/bin:$PATH
export LD_LIBRARY_PATH=$CUDA_HOME/lib64:$LD_LIBRARY_PATH
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sets the default CUDA version to &lt;strong&gt;12.6&lt;/strong&gt;.&lt;/p&gt;
&lt;h4&gt;libtorch Headers and Library Paths&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;# ===== libtorch — PyTorch&apos;s official C++ API and runtime =====
export CPATH=$HOME/libtorch/include:$HOME/libtorch/include/torch/csrc/api/include:$CPATH
export LIBRARY_PATH=$HOME/libtorch/lib:$LIBRARY_PATH
export LD_LIBRARY_PATH=$HOME/libtorch/lib:$LD_LIBRARY_PATH
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Enables calling PyTorch from C++.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;th&gt;Keep?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Writing C++/CUDA code with libtorch&lt;/td&gt;
&lt;td&gt;✔ Required&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Python-only PyTorch usage&lt;/td&gt;
&lt;td&gt;❌ Can be removed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;cuDNN Paths&lt;/h4&gt;
&lt;p&gt;Only needed for C++ builds, custom CUDA kernels, or TensorRT:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# ===== cuDNN =====
export CPATH=$HOME/cudnn/include:$CPATH
export LIBRARY_PATH=$HOME/cudnn/lib:$LIBRARY_PATH
export LD_LIBRARY_PATH=$HOME/cudnn/lib:$LD_LIBRARY_PATH
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;CUTLASS&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;# ===== CUTLASS =====
export CUTLASS=$HOME/cutlass
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Custom Command Paths&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;# ===== PATH: tells the shell where to find executables =====
export PATH=$HOME/.local/bin:$PATH
export PATH=&quot;$HOME/bin:$PATH&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt; On a no-root HPC cluster: install CUDA with &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;--toolkitpath=$HOME/cuda-X.Y --no-drm&amp;lt;/code&amp;gt;, install CMake with &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;--prefix=$HOME/.local&amp;lt;/code&amp;gt;, and keep your shell config clean by splitting everything into &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;~/.bashrc.d/&amp;lt;/code&amp;gt; modules — remember that CUDA without a GPU driver can compile but not run.&amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Custom Command</title><link>https://lxy-alexander.github.io/blog/posts/tools/custom-command/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/tools/custom-command/</guid><description>Custom Command</description><pubDate>Thu, 05 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;&lt;strong&gt;I. &lt;code&gt;sr&lt;/code&gt; — Custom SLURM Interactive Job Launcher&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt;
&amp;lt;strong&amp;gt;Overview:&amp;lt;/strong&amp;gt; &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;sr&amp;lt;/code&amp;gt; is a small wrapper script around &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;srun&amp;lt;/code&amp;gt; that lets you launch an interactive GPU session on an HPC cluster with a short, memorable command — optionally targeting a specific node.
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1. Write the Script&lt;/h2&gt;
&lt;p&gt;Create a file named &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;sr&amp;lt;/code&amp;gt; with the following content:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/bin/bash

# ===== Argument check =====
if [ -z &quot;$1&quot; ]; then
    echo &quot;Usage:&quot;
    echo &quot;  sr &amp;lt;gpu_type&amp;gt; [node_id]&quot;
    echo &quot;&quot;
    echo &quot;Examples:&quot;
    echo &quot;  sr h100&quot;
    echo &quot;  sr h100 007&quot;
    echo &quot;  sr a100&quot;
    exit 1
fi

GPU_TYPE=&quot;$1&quot;
PARTITION=&quot;gpucompute-$GPU_TYPE&quot;
NODE_ARG=&quot;&quot;

# ===== Optional node targeting =====
if [ -n &quot;$2&quot; ]; then
    NODE_ARG=&quot;--nodelist=ghpc$2&quot;
fi

# ===== Launch interactive session =====
srun \
  --gpus-per-node=1 \
  --cpus-per-gpu=4 \
  $NODE_ARG \
  --partition=$PARTITION \
  --time=12:00:00 \
  --pty /bin/bash
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Install the Script&lt;/h2&gt;
&lt;p&gt;Move the script to &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;~/bin&amp;lt;/code&amp;gt; and make it executable:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mkdir -p ~/bin
mv sr ~/bin/
chmod +x ~/bin/sr
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ensure &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;~/bin&amp;lt;/code&amp;gt; is on your &lt;code&gt;PATH&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;echo &apos;export PATH=&quot;$HOME/bin:$PATH&quot;&apos; &amp;gt;&amp;gt; ~/.bashrc
source ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;3. Usage&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;sr h100          # Request any H100 node
sr h100 007      # Request H100 node ghpc007 specifically
sr a100          # Request any A100 node
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt; Drop &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;sr&amp;lt;/code&amp;gt; into &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;~/bin&amp;lt;/code&amp;gt;, add &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;~/bin&amp;lt;/code&amp;gt; to your &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;PATH&amp;lt;/code&amp;gt;, and replace verbose &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;srun&amp;lt;/code&amp;gt; commands with &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;sr h100&amp;lt;/code&amp;gt; or &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;sr h100 007&amp;lt;/code&amp;gt; to spin up an interactive GPU session instantly.&amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Conda</title><link>https://lxy-alexander.github.io/blog/posts/tools/conda/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/tools/conda/</guid><description>Conda</description><pubDate>Tue, 03 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;&lt;strong&gt;I. Conda Cache Cleanup — Free Up Disk Space&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt;
&amp;lt;strong&amp;gt;Overview:&amp;lt;/strong&amp;gt; Conda accumulates downloaded packages, extracted tarballs, and index caches over time. Cleaning these up frees disk space and reduces environment cruft — without touching any of your existing environments.
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1. Check Conda Cache Usage (Optional)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;conda info
conda config --show pkgs_dirs
du -sh ~/miniconda3/pkgs 2&amp;gt;/dev/null
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;2. One-command Cache Cleanup (Recommended)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;conda clean -a -y
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Flag&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;-a&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;all&lt;/strong&gt; — cleans every type of cache and leftover&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;-y&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;yes&lt;/strong&gt; — auto-confirms without prompting&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;This removes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Downloaded &lt;code&gt;.tar.bz2&lt;/code&gt; / &lt;code&gt;.conda&lt;/code&gt; package archives&lt;/li&gt;
&lt;li&gt;Extracted package caches&lt;/li&gt;
&lt;li&gt;Index caches&lt;/li&gt;
&lt;li&gt;Unused package caches&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;3. Also Clean pip Cache (If Used Inside Conda Envs)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;pip cache purge
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;4. Verify the Space Was Freed (Optional)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;du -sh ~/miniconda3/pkgs 2&amp;gt;/dev/null
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;conda clean -a&amp;lt;/code&amp;gt; &amp;lt;strong&amp;gt;does not delete your environments&amp;lt;/strong&amp;gt; — it only removes cached downloads. The trade-off is that the next time you install a package, conda may need to re-download it.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt; Run &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;conda clean -a -y&amp;lt;/code&amp;gt; to safely reclaim disk space from package caches — your environments stay completely intact.&amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Git</title><link>https://lxy-alexander.github.io/blog/posts/tools/git/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/tools/git/</guid><description>Git</description><pubDate>Tue, 03 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;&lt;strong&gt;I. Git — Clean, Fork Sync &amp;amp; Rebase&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt;
&amp;lt;strong&amp;gt;Overview:&amp;lt;/strong&amp;gt; This note covers three related Git workflows: removing tracked and untracked files from the working directory with &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;git clean&amp;lt;/code&amp;gt; and &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;git restore&amp;lt;/code&amp;gt;; keeping a fork up to date with its upstream repository via merge or rebase; and understanding why &amp;lt;strong&amp;gt;rebase is preferred over merge&amp;lt;/strong&amp;gt; for fork synchronization.
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1. Removing Files from the Working Directory&lt;/h2&gt;
&lt;h3&gt;1) Remove Git-tracked Files&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;git restore .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Discards all unstaged changes to tracked files and restores them to the last committed state.&lt;/p&gt;
&lt;h3&gt;2) Remove Untracked Files (&lt;code&gt;git clean&lt;/code&gt;)&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Danger&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;git clean -f&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;⭐&lt;/td&gt;
&lt;td&gt;Deletes a small number of untracked files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;git clean -fd&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;⭐⭐&lt;/td&gt;
&lt;td&gt;Also removes untracked directories (e.g., build dirs)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;git clean -fdx&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;🔥🔥🔥&lt;/td&gt;
&lt;td&gt;&amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Removes almost all locally generated content&amp;lt;/span&amp;gt;, including files ignored by &lt;code&gt;.gitignore&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;git clean -fdxn&amp;lt;/code&amp;gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-n&lt;/code&gt; = dry-run: preview what would be deleted without actually deleting&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Always run &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;git clean -fdxn&amp;lt;/code&amp;gt; first to preview the files that would be removed before committing to &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;-fdx&amp;lt;/code&amp;gt;. This operation is &amp;lt;strong&amp;gt;irreversible&amp;lt;/strong&amp;gt;.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Updating a Forked Repository&lt;/h2&gt;
&lt;h3&gt;1) Method 1: Sync Upstream Locally (Recommended)&lt;/h3&gt;
&lt;h4&gt;Step 1: Navigate into Your Local Repository&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;cd your-repo
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Step 2: View Existing Remotes&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;git remote -v
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Step 3: Add the Upstream Remote (One-time Setup)&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;git remote add upstream https://github.com/ORIGINAL_OWNER/ORIGINAL_REPO.git
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Step 4: Fetch the Latest Changes from Upstream&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;git fetch upstream
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Step 5: Integrate Upstream Changes into Your Branch&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Option A — Merge:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git checkout main
git merge upstream/main
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the upstream default branch is &lt;code&gt;master&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git checkout master
git merge upstream/master
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Option B — Rebase (Recommended):&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git checkout main
git fetch upstream
git rebase upstream/main
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Step 6: Push the Updated Branch to Your Fork&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;After merge:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git push origin main
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;After rebase:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git push origin main --force-with-lease
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;2) Method 2: Sync Directly on GitHub (Web UI)&lt;/h3&gt;
&lt;p&gt;Open your fork on GitHub, then click:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Sync fork → Update branch
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;No local commands required.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. Why Rebase Is Preferred for Fork Sync&lt;/h2&gt;
&lt;p&gt;When syncing a personal fork with its upstream, &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;rebase is the recommended approach&amp;lt;/span&amp;gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Typical scenario:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;upstream&lt;/strong&gt; has moved ahead with new commits&lt;/li&gt;
&lt;li&gt;Your &lt;strong&gt;fork is behind&lt;/strong&gt; upstream&lt;/li&gt;
&lt;li&gt;You have your own local commits on top&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-c69d652d2a0747fab9aad1fab48ff742.r2.dev/images/image-20260206214648820&quot; alt=&quot;Before rebase or merge&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;1) Using Merge&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Produces an extra &amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Merge commit (M)&amp;lt;/span&amp;gt;&lt;/li&gt;
&lt;li&gt;History becomes tree-shaped and cluttered&lt;/li&gt;
&lt;li&gt;PRs look noisy and harder to review&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-c69d652d2a0747fab9aad1fab48ff742.r2.dev/images/image-20260206215456002&quot; alt=&quot;After merge&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;2) Using Rebase&lt;/h3&gt;
&lt;p&gt;After rebasing, your original commits are &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;replayed on top of the upstream&amp;lt;/span&amp;gt;. The old commits are discarded from the branch history, and Git creates new commits with the same content but &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;brand-new commit IDs (D → D&apos;)&amp;lt;/span&amp;gt;.&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; This is why &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;--force-with-lease&amp;lt;/code&amp;gt; is required after a rebase push. The remote still has the old commit D, while your local branch now has D&apos;. A regular &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;git push&amp;lt;/code&amp;gt; would be rejected; &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;--force-with-lease&amp;lt;/code&amp;gt; force-pushes safely by checking that no one else has pushed in the meantime.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;Benefits of rebase:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;Clean, linear commit history&amp;lt;/span&amp;gt;&lt;/li&gt;
&lt;li&gt;&amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;Clearer, easier-to-review PRs&amp;lt;/span&amp;gt;&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;standard practice&lt;/strong&gt; for syncing a fork with upstream&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-c69d652d2a0747fab9aad1fab48ff742.r2.dev/images/image-20260206215538616&quot; alt=&quot;After rebase&quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;4. Handling Conflicts in Merge / Rebase / Sync Fork&lt;/h2&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; Conflict resolution content to be added here.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt; Use &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;git restore .&amp;lt;/code&amp;gt; for tracked files and &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;git clean -fdxn&amp;lt;/code&amp;gt; (dry-run first!) for untracked ones; when syncing a fork, always prefer &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;rebase&amp;lt;/code&amp;gt; over &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;merge&amp;lt;/code&amp;gt; to keep a clean, linear history — then &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;push --force-with-lease&amp;lt;/code&amp;gt; to update the remote safely.&amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Python Pydantic</title><link>https://lxy-alexander.github.io/blog/posts/python/python-pydantic/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/python/python-pydantic/</guid><description>Python Pydantic</description><pubDate>Mon, 02 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pydantic is a Python data validation and settings management library.&lt;/strong&gt;（Pydantic 是一个用于 Python 数据校验和配置管理的库）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;1) &lt;code&gt;BaseModel&lt;/code&gt; (Define models)&lt;/h2&gt;
&lt;p&gt;It defines a typed data structure (schema). Pydantic will create an object and validate the input based on the type hints.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pydantic import BaseModel

class User(BaseModel):
    id: int
    name: str

u = User(id=1, name=&quot;Tom&quot;)
print(u)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;2) Validation + Type Coercion&lt;/h2&gt;
&lt;p&gt;It validates input types and will automatically convert compatible values (e.g. &lt;code&gt;&quot;123&quot;&lt;/code&gt; → &lt;code&gt;123&lt;/code&gt;). If it can’t validate/convert, it raises an error.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pydantic import BaseModel

class User(BaseModel):
    id: int

u = User(id=&quot;123&quot;)  # &quot;123&quot; -&amp;gt; 123
print(u.id, type(u.id))
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;3) &lt;code&gt;Field&lt;/code&gt; (Constraints / Metadata)&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Field()&lt;/code&gt; lets you add constraints (like min/max) and extra metadata (like description). Pydantic will enforce the constraints during validation.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pydantic import BaseModel, Field

class User(BaseModel):
    age: int = Field(ge=0, le=150, description=&quot;Age must be between 0 and 150&quot;)

u = User(age=18)
print(u)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;4) Nested Models&lt;/h2&gt;
&lt;p&gt;A model can contain another model as a field. Pydantic will validate nested dictionaries and convert them into nested model objects automatically.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pydantic import BaseModel

class Address(BaseModel):
    city: str

class User(BaseModel):
    name: str
    address: Address

u = User(name=&quot;Tom&quot;, address={&quot;city&quot;: &quot;Shanghai&quot;})
print(u.address.city)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;5) Optional Fields + Defaults&lt;/h2&gt;
&lt;p&gt;Optional fields (&lt;code&gt;T | None&lt;/code&gt;) can be missing or set to &lt;code&gt;None&lt;/code&gt;. Default values make the field optional during initialization.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pydantic import BaseModel

class User(BaseModel):
    name: str
    email: str | None = None

u1 = User(name=&quot;Tom&quot;)
u2 = User(name=&quot;Jerry&quot;, email=&quot;jerry@example.com&quot;)

print(u1)
print(u2)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;6) &lt;code&gt;@field_validator&lt;/code&gt; (Field-level validation)&lt;/h2&gt;
&lt;p&gt;It lets you write custom validation logic for a specific field (e.g., trimming spaces, format checks, rejecting certain values).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pydantic import BaseModel, field_validator

class User(BaseModel):
    name: str

    @field_validator(&quot;name&quot;)
    @classmethod
    def name_not_empty(cls, v: str):
        if not v.strip():
            raise ValueError(&quot;name must not be empty&quot;)
        return v.strip()

u = User(name=&quot;  Tom  &quot;)
print(u)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;7) &lt;code&gt;@model_validator&lt;/code&gt; (Model-level validation)&lt;/h2&gt;
&lt;p&gt;It validates the model as a whole, which is useful for rules involving multiple fields (cross-field validation).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pydantic import BaseModel, model_validator

class Login(BaseModel):
    username: str
    password: str

    @model_validator(mode=&quot;after&quot;)
    def check_password(self):
        if len(self.password) &amp;lt; 6:
            raise ValueError(&quot;password too short (min 6)&quot;)
        return self

ok = Login(username=&quot;tom&quot;, password=&quot;123456&quot;)
print(ok)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;8) &lt;code&gt;model_dump()&lt;/code&gt; (Export to dict)&lt;/h2&gt;
&lt;p&gt;It converts a validated model instance into a standard Python dictionary, often used for business logic or API responses.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pydantic import BaseModel

class User(BaseModel):
    id: int
    name: str

u = User(id=1, name=&quot;Tom&quot;)
print(u.model_dump())
print(type(u.model_dump()))
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;9) &lt;code&gt;model_dump_json()&lt;/code&gt; (Export to JSON)&lt;/h2&gt;
&lt;p&gt;It converts the model into a JSON string, convenient for HTTP responses, logs, caches, or storing data.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pydantic import BaseModel

class User(BaseModel):
    id: int
    name: str

u = User(id=1, name=&quot;Tom&quot;)
print(u.model_dump_json())
print(type(u.model_dump_json()))
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;10) &lt;code&gt;BaseSettings&lt;/code&gt; (Settings management)&lt;/h2&gt;
&lt;p&gt;It reads configuration from environment variables (and other sources) into a typed model, validating types just like &lt;code&gt;BaseModel&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pip install pydantic-settings
from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    app_name: str = &quot;demo&quot;
    debug: bool = False

s = Settings()
print(s.app_name, s.debug)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(Optional) run with environment variables:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export APP_NAME=myapp
export DEBUG=true
python your_file.py
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Astro with Mermaid</title><link>https://lxy-alexander.github.io/blog/posts/guide/astro-with-mermaid/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/guide/astro-with-mermaid/</guid><description>Astro with Mermaid</description><pubDate>Sun, 01 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;设置 Mermaid in Astro&lt;/h1&gt;
&lt;h2&gt;1）安装 Mermaid&lt;/h2&gt;
&lt;p&gt;在项目根目录执行：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm i mermaid
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你用 pnpm：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pnpm add mermaid
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你用 yarn：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;yarn add mermaid
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;2）添加&lt;code&gt;src/components/Mermaid.astro&lt;/code&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;---
// src/components/Mermaid.astro
// 完整交互版：主界面滚动条 + Ctrl缩放(zoom) + 双击全屏
// 全屏：拖拽平移 + Ctrl缩放(transform)
---

&amp;lt;script&amp;gt;
  import mermaid from &quot;mermaid&quot;;

  const isDark = document.documentElement.classList.contains(&quot;dark&quot;);
  console.log(&quot;isDark =&quot;, isDark);

  const BASE_FONT = 11;

  mermaid.initialize({
    startOnLoad: false,
    theme: isDark ? &quot;dark&quot; : &quot;default&quot;,
    securityLevel: &quot;loose&quot;,

    // ✅ Mermaid v10+ 类型不允许直接写 flowchart.fontSize / sequence.fontSize 等
    // ✅ 统一通过 themeVariables + CSS 强制字体大小更稳定
    themeVariables: {
      fontSize: `${BASE_FONT}px`,
      fontFamily: &quot;Arial, sans-serif&quot;,
    },
  });

  function logMermaidAllLabels(): void {
    document.querySelectorAll&amp;lt;SVGSVGElement&amp;gt;(&quot;.mermaid svg&quot;).forEach((svg, svgIndex) =&amp;gt; {
      console.log(`====== [SVG ${svgIndex}] ======`);

      // 1) 普通 svg text
      svg.querySelectorAll&amp;lt;SVGTextElement&amp;gt;(&quot;text&quot;).forEach((node, i) =&amp;gt; {
        const style = getComputedStyle(node);
        console.log(
          `[text ${i}]`,
          node.textContent?.trim(),
          &quot;font-size:&quot;,
          style.fontSize,
          &quot;font-family:&quot;,
          style.fontFamily
        );
      });

      // 2) foreignObject (HTML label)
      svg.querySelectorAll&amp;lt;SVGForeignObjectElement&amp;gt;(&quot;foreignObject&quot;).forEach((fo, i) =&amp;gt; {
        const div = fo.querySelector&amp;lt;HTMLElement&amp;gt;(&quot;div, span, p&quot;) || (fo as unknown as HTMLElement);
        const style = getComputedStyle(div);
        console.log(
          `[foreignObject ${i}]`,
          div.textContent?.trim(),
          &quot;font-size:&quot;,
          style.fontSize,
          &quot;font-family:&quot;,
          style.fontFamily
        );
      });
    });
  }

  function forceSvgOverflow(div: HTMLElement): void {
    const wrapper = div.querySelector&amp;lt;HTMLElement&amp;gt;(&quot;.mermaid-interactive-wrapper&quot;);
    const svg = div.querySelector&amp;lt;SVGSVGElement&amp;gt;(&quot;.mermaid-interactive-wrapper svg&quot;);
    if (!wrapper || !svg) return;

    // ✅ 永远不要让 svg 自动 fit 容器
    svg.style.width = &quot;auto&quot;;
    svg.style.maxWidth = &quot;none&quot;;
    svg.style.height = &quot;auto&quot;;
    svg.style.display = &quot;block&quot;;

    requestAnimationFrame(() =&amp;gt; {
      const wrapperWidth = wrapper.clientWidth;

      // ✅ 优先使用 viewBox，更稳定（特别是 gantt）
      const vb = svg.viewBox?.baseVal;
      const viewBoxWidth = vb?.width || 0;

      // fallback：bbox
      const bboxWidth = svg.getBBox().width || 0;

      const realWidth = Math.ceil((viewBoxWidth || bboxWidth) + 40);

      // ✅ 用真实宽度撑开 svg
      svg.style.width = `${realWidth}px`;

      // ✅ 超出才滚动
      if (realWidth &amp;gt; wrapperWidth) {
        wrapper.classList.add(&quot;has-scroll&quot;);
      } else {
        wrapper.classList.remove(&quot;has-scroll&quot;);
      }

      console.log(
        &quot;[forceSvgOverflow]&quot;,
        &quot;wrapperWidth=&quot;,
        wrapperWidth,
        &quot;realWidth=&quot;,
        realWidth,
        &quot;viewBoxWidth=&quot;,
        viewBoxWidth,
        &quot;bboxWidth=&quot;,
        bboxWidth
      );
    });
  }

  function extractMermaidText(container: Element): string {
    const code = container.querySelector&amp;lt;HTMLElement&amp;gt;(&quot;code&quot;);
    if (code) {
      const text = (code.innerText || code.textContent || &quot;&quot;).trim();
      return cleanMermaidText(text);
    }

    const linesNodeList = container.querySelectorAll&amp;lt;HTMLElement&amp;gt;(&apos;[class*=&quot;line&quot;]:not([class*=&quot;number&quot;])&apos;);
    if (linesNodeList.length &amp;gt; 0) {
      const lines = Array.from(linesNodeList) as HTMLElement[];
      const text = lines
        .map((lineEl) =&amp;gt; {
          const clone = lineEl.cloneNode(true) as HTMLElement;
          clone
            .querySelectorAll&amp;lt;HTMLElement&amp;gt;(&apos;[class*=&quot;number&quot;], .line-number, .ln&apos;)
            .forEach((el) =&amp;gt; el.remove());
          return (clone.innerText || clone.textContent || &quot;&quot;).trim();
        })
        .filter((line) =&amp;gt; line.length &amp;gt; 0)
        .join(&quot;\n&quot;);

      return cleanMermaidText(text);
    }

    const text = ((container as HTMLElement).innerText || container.textContent || &quot;&quot;).trim();
    return cleanMermaidText(text);
  }

  function cleanMermaidText(text: string): string {
    const lines = text.split(&quot;\n&quot;);
    const cleanedLines: string[] = [];

    for (let line of lines) {
      line = line.trim();
      if (/^\d+$/.test(line)) continue;
      line = line.replace(/^\d+\s+/, &quot;&quot;);
      if (line.length &amp;gt; 0) cleanedLines.push(line);
    }

    const result = cleanedLines.join(&quot;\n&quot;).trim();
    console.log(&quot;[Mermaid] After cleaning:&quot;, result);
    return result;
  }

  function findMermaidBlocks(): Element[] {
    const results: Element[] = [];

    document.querySelectorAll(&quot;pre&quot;).forEach((pre) =&amp;gt; {
      const code = pre.querySelector(&quot;code&quot;);
      const cls = (code?.className || pre.className || &quot;&quot;).toLowerCase();
      if (cls.includes(&quot;mermaid&quot;)) {
        results.push(pre);
      }
    });

    document.querySelectorAll(&quot;figure, div.expressive-code, [class*=&apos;astro-code&apos;]&quot;).forEach((box) =&amp;gt; {
      const hasMermaidLabel = Array.from(box.querySelectorAll(&quot;*&quot;)).some((n) =&amp;gt; {
        const text = (n.textContent || &quot;&quot;).trim().toUpperCase();
        return text === &quot;MERMAID&quot;;
      });

      if (hasMermaidLabel) {
        results.push(box);
      }
    });

    document.querySelectorAll&amp;lt;HTMLElement&amp;gt;(&apos;[data-language=&quot;mermaid&quot;]&apos;).forEach((el) =&amp;gt; {
      const container = el.closest(&quot;figure, div, pre&quot;) || el;
      results.push(container);
    });

    return Array.from(new Set(results));
  }

  // 创建模态框
  function createModal(): void {
    if (document.getElementById(&quot;mermaid-modal&quot;)) return;

    const modal = document.createElement(&quot;div&quot;);
    modal.id = &quot;mermaid-modal&quot;;
    modal.className = &quot;mermaid-modal&quot;;
    modal.innerHTML = `
      &amp;lt;div class=&quot;mermaid-modal-overlay&quot;&amp;gt;&amp;lt;/div&amp;gt;
      &amp;lt;div class=&quot;mermaid-modal-content&quot;&amp;gt;
        &amp;lt;button class=&quot;mermaid-modal-close&quot; aria-label=&quot;关闭&quot;&amp;gt;×&amp;lt;/button&amp;gt;
        &amp;lt;div class=&quot;mermaid-modal-controls&quot;&amp;gt;
          &amp;lt;button class=&quot;mermaid-zoom-btn&quot; data-action=&quot;zoom-in&quot; title=&quot;放大&quot;&amp;gt;🔍+&amp;lt;/button&amp;gt;
          &amp;lt;button class=&quot;mermaid-zoom-btn&quot; data-action=&quot;zoom-out&quot; title=&quot;缩小&quot;&amp;gt;🔍-&amp;lt;/button&amp;gt;
          &amp;lt;button class=&quot;mermaid-zoom-btn&quot; data-action=&quot;reset&quot; title=&quot;重置&quot;&amp;gt;↺&amp;lt;/button&amp;gt;
          &amp;lt;span class=&quot;mermaid-zoom-level&quot;&amp;gt;100%&amp;lt;/span&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div class=&quot;mermaid-modal-body&quot;&amp;gt;&amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    `;
    document.body.appendChild(modal);

    const closeBtn = modal.querySelector&amp;lt;HTMLButtonElement&amp;gt;(&quot;.mermaid-modal-close&quot;);
    const overlay = modal.querySelector&amp;lt;HTMLElement&amp;gt;(&quot;.mermaid-modal-overlay&quot;);

    const closeModal = () =&amp;gt; {
      modal.classList.remove(&quot;active&quot;);
      document.body.style.overflow = &quot;&quot;;
    };

    closeBtn?.addEventListener(&quot;click&quot;, closeModal);
    overlay?.addEventListener(&quot;click&quot;, closeModal);

    document.addEventListener(&quot;keydown&quot;, (e: KeyboardEvent) =&amp;gt; {
      if (e.key === &quot;Escape&quot; &amp;amp;&amp;amp; modal.classList.contains(&quot;active&quot;)) {
        closeModal();
      }
    });
  }

  // 更新缩放比例显示
  function updateZoomDisplay(container: HTMLElement, scale: number): void {
    const zoomLevel = container.closest(&quot;.mermaid-modal&quot;)?.querySelector&amp;lt;HTMLElement&amp;gt;(&quot;.mermaid-zoom-level&quot;);
    if (zoomLevel) {
      zoomLevel.textContent = `${Math.round(scale * 100)}%`;
    }
  }

  // ✅ 主界面滚动条缩放（使用 zoom，保证 overflow-x 生效）
  function makeScrollableZoom(wrapper: HTMLElement, content: HTMLElement): void {
    let scale = 1;

    const applyZoom = () =&amp;gt; {
      scale = Math.min(Math.max(scale, 0.5), 3); // ✅ 主界面缩放范围
      (content.style as any).zoom = String(scale); // zoom 不是标准属性，TS 可能不认识
      console.log(&quot;zoom =&quot;, (content.style as any).zoom);
    };

    wrapper.addEventListener(
      &quot;wheel&quot;,
      (e: WheelEvent) =&amp;gt; {
        if (!e.ctrlKey &amp;amp;&amp;amp; !e.metaKey) return;
        e.preventDefault();

        const delta = e.deltaY &amp;gt; 0 ? -0.1 : 0.1;
        scale += delta;
        applyZoom();
      },
      { passive: false }
    );

    applyZoom();
  }

  type TransformController = {
    get scale(): number;
    set scale(v: number);

    get translateX(): number;
    set translateX(v: number);

    get translateY(): number;
    set translateY(v: number);

    applyTransform: () =&amp;gt; void;
  };

  // ✅ 全屏交互：拖拽 + transform 缩放（保证永远返回 TransformController）
  function makeInteractive(wrapper: HTMLElement, options = { enableDrag: true }): TransformController {
    let scale = 1;
    let translateX = 0;
    let translateY = 0;
    let isDragging = false;
    let startX = 0;
    let startY = 0;
    let lastTranslateX = 0;
    let lastTranslateY = 0;

    const svg = wrapper.querySelector&amp;lt;SVGSVGElement&amp;gt;(&quot;svg&quot;);

    const applyTransform = () =&amp;gt; {
      if (!svg) return;
      svg.style.transform = `translate(${translateX}px, ${translateY}px) scale(${scale})`;
      svg.style.transformOrigin = &quot;0 0&quot;;
      svg.style.transition = isDragging ? &quot;none&quot; : &quot;transform 0.15s ease&quot;;
    };

    // ✅ 如果没 SVG，也返回一个空 controller，避免 transform 变 undefined
    if (!svg) {
      return {
        get scale() {
          return scale;
        },
        set scale(v: number) {
          scale = v;
        },
        get translateX() {
          return translateX;
        },
        set translateX(v: number) {
          translateX = v;
        },
        get translateY() {
          return translateY;
        },
        set translateY(v: number) {
          translateY = v;
        },
        applyTransform,
      };
    }

    // ✅ 拖拽（可选）
    if (options.enableDrag) {
      wrapper.addEventListener(&quot;mousedown&quot;, (e: MouseEvent) =&amp;gt; {
        if (e.button !== 0) return;

        isDragging = true;
        startX = e.clientX;
        startY = e.clientY;
        lastTranslateX = translateX;
        lastTranslateY = translateY;
        wrapper.style.cursor = &quot;grabbing&quot;;
        e.preventDefault();
      });

      document.addEventListener(&quot;mousemove&quot;, (e: MouseEvent) =&amp;gt; {
        if (!isDragging) return;

        const deltaX = e.clientX - startX;
        const deltaY = e.clientY - startY;

        translateX = lastTranslateX + deltaX;
        translateY = lastTranslateY + deltaY;

        applyTransform();
      });

      document.addEventListener(&quot;mouseup&quot;, () =&amp;gt; {
        if (isDragging) {
          isDragging = false;
          wrapper.style.cursor = &quot;grab&quot;;
        }
      });
    }

    // Ctrl + 滚轮缩放（全屏）
    wrapper.addEventListener(
      &quot;wheel&quot;,
      (e: WheelEvent) =&amp;gt; {
        if (!e.ctrlKey &amp;amp;&amp;amp; !e.metaKey) return;

        e.preventDefault();

        const delta = e.deltaY &amp;gt; 0 ? -0.1 : 0.1;
        const newScale = Math.min(Math.max(0.3, scale + delta), 5);

        // 以鼠标位置为中心缩放
        const rect = wrapper.getBoundingClientRect();
        const mouseX = e.clientX - rect.left;
        const mouseY = e.clientY - rect.top;

        const scaleChange = newScale / scale;
        translateX = mouseX - (mouseX - translateX) * scaleChange;
        translateY = mouseY - (mouseY - translateY) * scaleChange;

        scale = newScale;
        applyTransform();

        updateZoomDisplay(wrapper, scale);
      },
      { passive: false }
    );

    wrapper.style.cursor = options.enableDrag ? &quot;grab&quot; : &quot;default&quot;;
    wrapper.style.overflow = &quot;hidden&quot;;
    applyTransform();

    return {
      get scale() {
        return scale;
      },
      set scale(v: number) {
        scale = v;
      },
      get translateX() {
        return translateX;
      },
      set translateX(v: number) {
        translateX = v;
      },
      get translateY() {
        return translateY;
      },
      set translateY(v: number) {
        translateY = v;
      },
      applyTransform,
    };
  }

  // 为普通视图添加交互
  function addInteractiveFeature(mermaidDiv: HTMLElement): void {
    const wrapper = document.createElement(&quot;div&quot;);
    wrapper.className = &quot;mermaid-interactive-wrapper&quot;;

    const content = document.createElement(&quot;div&quot;);
    content.className = &quot;mermaid-interactive-content&quot;;

    content.innerHTML = mermaidDiv.innerHTML;
    mermaidDiv.innerHTML = &quot;&quot;;

    wrapper.appendChild(content);
    mermaidDiv.appendChild(wrapper);

    const hint = document.createElement(&quot;div&quot;);
    hint.className = &quot;mermaid-hint&quot;;
    // hint.innerHTML = `&amp;lt;strong&amp;gt;提示：&amp;lt;/strong&amp;gt; 主界面 Ctrl/⌘ + 滚轮缩放；双击进入全屏；全屏可拖拽平移 + Ctrl/⌘ 缩放`;
    mermaidDiv.appendChild(hint);

    // ✅ 主界面：滚动条缩放（zoom）
    makeScrollableZoom(wrapper, content);

    // 双击全屏
    content.addEventListener(&quot;dblclick&quot;, () =&amp;gt; {
      openFullscreen(mermaidDiv);
    });
  }

  // 打开全屏模态框
  function openFullscreen(mermaidDiv: HTMLElement): void {
    const modal = document.getElementById(&quot;mermaid-modal&quot;);
    const modalBody = modal?.querySelector&amp;lt;HTMLElement&amp;gt;(&quot;.mermaid-modal-body&quot;);

    if (!modal || !modalBody) return;

    const svg = mermaidDiv.querySelector&amp;lt;SVGSVGElement&amp;gt;(&quot;svg&quot;);
    if (!svg) return;

    // 克隆 SVG 到模态框
    const clonedSvg = svg.cloneNode(true) as SVGSVGElement;
    modalBody.innerHTML = &quot;&quot;;
    modalBody.appendChild(clonedSvg);

    const transform = makeInteractive(modalBody, { enableDrag: true });

    const zoomIn = modal.querySelector&amp;lt;HTMLButtonElement&amp;gt;(&apos;[data-action=&quot;zoom-in&quot;]&apos;);
    const zoomOut = modal.querySelector&amp;lt;HTMLButtonElement&amp;gt;(&apos;[data-action=&quot;zoom-out&quot;]&apos;);
    const reset = modal.querySelector&amp;lt;HTMLButtonElement&amp;gt;(&apos;[data-action=&quot;reset&quot;]&apos;);

    zoomIn?.addEventListener(&quot;click&quot;, () =&amp;gt; {
      const newScale = Math.min(transform.scale + 0.2, 5);
      transform.scale = newScale;
      transform.applyTransform();
      updateZoomDisplay(modalBody, newScale);
    });

    zoomOut?.addEventListener(&quot;click&quot;, () =&amp;gt; {
      const newScale = Math.max(transform.scale - 0.2, 0.3);
      transform.scale = newScale;
      transform.applyTransform();
      updateZoomDisplay(modalBody, newScale);
    });

    reset?.addEventListener(&quot;click&quot;, () =&amp;gt; {
      transform.scale = 1;
      transform.translateX = 0;
      transform.translateY = 0;
      transform.applyTransform();
      updateZoomDisplay(modalBody, 1);
    });

    modal.classList.add(&quot;active&quot;);
    document.body.style.overflow = &quot;hidden&quot;;
    updateZoomDisplay(modalBody, 1);
  }

  async function renderMermaid(): Promise&amp;lt;void&amp;gt; {
    const blocks = findMermaidBlocks();
    console.log(&quot;[Mermaid] Found blocks:&quot;, blocks.length);

    let converted = 0;

    for (const container of blocks) {
      try {
        const text = extractMermaidText(container);

        const isMermaid =
          text.includes(&quot;graph&quot;) ||
          text.includes(&quot;sequenceDiagram&quot;) ||
          text.includes(&quot;classDiagram&quot;) ||
          text.includes(&quot;stateDiagram&quot;) ||
          text.includes(&quot;erDiagram&quot;) ||
          text.includes(&quot;journey&quot;) ||
          text.includes(&quot;gantt&quot;) ||
          text.includes(&quot;pie&quot;) ||
          text.includes(&quot;flowchart&quot;);

        if (!isMermaid) continue;

        const div = document.createElement(&quot;div&quot;);
        div.className = &quot;mermaid mermaid-container&quot;;
        div.textContent = text;

        container.replaceWith(div);
        converted++;
      } catch (e) {
        console.error(&quot;[Mermaid] Error processing block:&quot;, e);
      }
    }

    console.log(&quot;[Mermaid] Converted:&quot;, converted);

    if (converted &amp;gt; 0) {
      try {
        await mermaid.run({ querySelector: &quot;.mermaid&quot; });
        logMermaidAllLabels();
        console.log(&quot;[Mermaid] Rendered ✅&quot;);

        createModal();

        document.querySelectorAll&amp;lt;HTMLElement&amp;gt;(&quot;.mermaid-container&quot;).forEach((div) =&amp;gt; {
          if (!div.querySelector(&quot;.mermaid-interactive-wrapper&quot;)) {
            addInteractiveFeature(div);
          }

          // ✅ 强制让 SVG 产生溢出 -&amp;gt; 横向滚动条必出现
          forceSvgOverflow(div);
        });
      } catch (e) {
        console.error(&quot;[Mermaid] Render failed ❌&quot;, e);
      }
    }
  }

  // 初始渲染
  if (document.readyState === &quot;loading&quot;) {
    document.addEventListener(&quot;DOMContentLoaded&quot;, () =&amp;gt; {
      renderMermaid();
    });
  } else {
    renderMermaid();
  }

  // Swup 支持（window.swup 不是标准类型）
  const w = window as any;
  if (w.swup) {
    w.swup.hooks.on(&quot;page:view&quot;, renderMermaid);
  }
  document.addEventListener(&quot;swup:page:view&quot;, renderMermaid);
&amp;lt;/script&amp;gt;

&amp;lt;style is:global&amp;gt;
  /* 基础容器 */
  .mermaid-container {
    width: 100%;
    margin: 2rem 0;
    position: relative;
    border: 1px solid #e2e8f0;
    border-radius: 8px;
    padding: 1rem;
    background: #f8fafc;
  }

  :global(.dark) .mermaid-container {
    border-color: #374151;
    background: #1f2937;
  }

  /* ✅ 主界面滚动容器：横向滚动条 */
  .mermaid-interactive-wrapper {
    width: 100%;
    overflow-x: auto;
    overflow-y: hidden;
    position: relative;
    border-radius: 4px;
    background: white;
    scrollbar-width: thin;
  }

  :global(.dark) .mermaid-interactive-wrapper {
    background: #111827;
  }

  /* ✅ 内容容器必须由内容撑开，否则不会溢出 -&amp;gt; 没滚动条 */
  .mermaid-interactive-content {
    display: inline-block;
    width: max-content;
    height: auto;
  }

  .mermaid-interactive-content svg {
    display: block;
    max-width: none !important;
    height: auto;
  }

  /* ✅ 主界面字体强制（Mermaid 不同图类型都能覆盖到） */
  .mermaid-interactive-content svg text,
  .mermaid-interactive-content svg .nodeLabel,
  .mermaid-interactive-content svg .edgeLabel,
  .mermaid-interactive-content svg .label,
  .mermaid-interactive-content svg tspan {
    font-size: clamp(10px, 1em, 14px) !important;
    font-family: Arial, sans-serif !important;
  }

  /* 提示文字 */
  .mermaid-hint {
    text-align: center;
    font-size: 0.875rem;
    color: #64748b;
    margin-top: 1rem;
    padding: 0.5rem;
    background: #f1f5f9;
    border-radius: 4px;
  }

  .mermaid-hint strong {
    color: #475569;
    font-weight: 600;
  }

  :global(.dark) .mermaid-hint {
    color: #94a3b8;
    background: #334155;
  }

  :global(.dark) .mermaid-hint strong {
    color: #cbd5e1;
  }

  /* 模态框 */
  .mermaid-modal {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 9999;
    align-items: center;
    justify-content: center;
  }

  .mermaid-modal.active {
    display: flex;
  }

  .mermaid-modal-overlay {
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0.9);
    backdrop-filter: blur(4px);
  }

  .mermaid-modal-content {
    position: relative;
    width: 95vw;
    height: 95vh;
    background: white;
    border-radius: 12px;
    box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    z-index: 1;
  }

  :global(.dark) .mermaid-modal-content {
    background: #1f2937;
  }

  /* 关闭按钮 */
  .mermaid-modal-close {
    position: absolute;
    top: 1rem;
    right: 1rem;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    border: none;
    background: rgba(0, 0, 0, 0.7);
    color: white;
    font-size: 2rem;
    line-height: 1;
    cursor: pointer;
    z-index: 10;
    display: flex;
    align-items: center;
    justify-content: center;
  }

  /* 控制面板 */
  .mermaid-modal-controls {
    position: absolute;
    bottom: 2rem;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    gap: 0.5rem;
    align-items: center;
    background: rgba(0, 0, 0, 0.8);
    padding: 0.75rem 1.5rem;
    border-radius: 100px;
    z-index: 10;
    backdrop-filter: blur(8px);
  }

  .mermaid-zoom-btn {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    border: 1px solid rgba(255, 255, 255, 0.2);
    background: rgba(255, 255, 255, 0.1);
    color: white;
    font-size: 1rem;
    cursor: pointer;
  }

  .mermaid-zoom-level {
    color: white;
    font-size: 0.875rem;
    font-weight: 600;
    min-width: 50px;
    text-align: center;
    margin: 0 0.5rem;
  }

  .mermaid-modal-body {
    flex: 1;
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
    position: relative;
  }

  /* ✅ 模态框字体也 clamp(10~14) */
  .mermaid-modal-body svg text,
  .mermaid-modal-body svg .nodeLabel,
  .mermaid-modal-body svg .edgeLabel,
  .mermaid-modal-body svg .label,
  .mermaid-modal-body svg tspan {
    font-size: clamp(10px, 1em, 14px) !important;
    font-family: Arial, sans-serif !important;
  }

  /* 响应式 */
  @media (max-width: 768px) {
    .mermaid-modal-content {
      width: 100vw;
      height: 100vh;
      border-radius: 0;
    }

    .mermaid-modal-controls {
      bottom: 1rem;
      padding: 0.5rem 1rem;
    }

    .mermaid-zoom-btn {
      width: 32px;
      height: 32px;
      font-size: 0.875rem;
    }

    .mermaid-hint {
      font-size: 0.75rem;
    }
  }
&amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;3）修改&lt;code&gt;src/layouts/Layout.astro&lt;/code&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;---
.....
+ import Mermaid from &quot;../components/Mermaid.astro&quot;;
---
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;	&amp;lt;body class=&quot; min-h-screen transition &quot; class:list={[{&quot;lg:is-home&quot;: isHomePage, &quot;enable-banner&quot;: enableBanner}]}
		  data-overlayscrollbars-initialize
	&amp;gt;
		&amp;lt;ConfigCarrier&amp;gt;&amp;lt;/ConfigCarrier&amp;gt;
        + &amp;lt;Mermaid client:load /&amp;gt;
		+ &amp;lt;slot /&amp;gt;

		&amp;lt;!-- increase the page height during page transition to prevent the scrolling animation from jumping --&amp;gt;
		&amp;lt;div id=&quot;page-height-extend&quot; class=&quot;hidden h-[300vh]&quot;&amp;gt;&amp;lt;/div&amp;gt;
	&amp;lt;/body&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;4） 修改样式&lt;code&gt;src/styles/global.css&lt;/code&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;.mermaid-interactive-wrapper {
  width: 100%;
  overflow-x: auto !important; /* ✅ 溢出才显示滚动条 */
  overflow-y: hidden !important;
  background: transparent;
}

/* ✅ 滚动条高度（不要太大，否则会挤压图） */
.mermaid-interactive-wrapper::-webkit-scrollbar {
  height: 14px;
}

/* ✅ 轨道 */
.mermaid-interactive-wrapper::-webkit-scrollbar-track {
  background: #f1f5f9;
  border-radius: 999px;
}

/* ✅ thumb */
.mermaid-interactive-wrapper::-webkit-scrollbar-thumb {
  background: #94a3b8;
  border-radius: 999px;

  /* ✅ 让 thumb 变细并居中 */
  border: 4px solid #f1f5f9;
  background-clip: padding-box;
}

/* hover */
.mermaid-interactive-wrapper::-webkit-scrollbar-thumb:hover {
  background: #64748b;
}

:global(.dark) .mermaid-interactive-wrapper::-webkit-scrollbar-track {
  background: #1f2937;
}

:global(.dark) .mermaid-interactive-wrapper::-webkit-scrollbar-thumb {
  background: #475569;
  border: 4px solid #1f2937;
  background-clip: padding-box;
}

:global(.dark) .mermaid-interactive-wrapper::-webkit-scrollbar-thumb:hover {
  background: #64748b;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Mermaid 测试&lt;/h1&gt;
&lt;p&gt;在你的 Markdown 文件中添加以下测试代码：&lt;/p&gt;
&lt;h2&gt;测试案例&lt;/h2&gt;
&lt;h3&gt;流程图&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;graph TD
    A[开始] --&amp;gt; B{是否成功?}
    B --&amp;gt;|是| C[完成]
    B --&amp;gt;|否| D[重试]
    D --&amp;gt; A
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;时序图&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;sequenceDiagram
    participant A as 用户
    participant B as 系统
    A-&amp;gt;&amp;gt;B: 发送请求
    B--&amp;gt;&amp;gt;A: 返回响应
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;类图&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;classDiagram
    class Animal {
        +String name
        +int age
        +makeSound()
    }
    class Dog {
        +bark()
    }
    Animal &amp;lt;|-- Dog
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;时序图&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;sequenceDiagram
    participant A as 用户
    participant B as 系统
    A-&amp;gt;&amp;gt;B: 发送请求
    B--&amp;gt;&amp;gt;A: 返回响应
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;类图&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;classDiagram
    class Animal {
        +String name
        +int age
        +makeSound()
    }
    class Dog {
        +bark()
    }
    Animal &amp;lt;|-- Dog
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;状态图&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;stateDiagram-v2
    [*] --&amp;gt; 待处理
    待处理 --&amp;gt; 处理中
    处理中 --&amp;gt; 已完成
    处理中 --&amp;gt; 失败
    失败 --&amp;gt; 待处理
    已完成 --&amp;gt; [*]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;甘特图&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;gantt
    title 项目计划
    dateFormat  YYYY-MM-DD
    section 设计
    需求分析      :a1, 2024-01-01, 30d
    UI设计        :a2, after a1, 20d
    section 开发
    后端开发      :b1, 2024-02-01, 45d
    前端开发      :b2, 2024-02-10, 40d
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;饼图&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;pie title 技术栈占比
    &quot;JavaScript&quot; : 45
    &quot;Python&quot; : 30
    &quot;Go&quot; : 15
    &quot;其他&quot; : 10
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果显示正常现在你应该能在页面上看到渲染好的图表了！🎉&lt;/p&gt;
&lt;h2&gt;控制台检测&lt;/h2&gt;
&lt;p&gt;请检查：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;浏览器控制台&lt;/strong&gt;是否显示 &lt;code&gt;[Mermaid] Rendered ✅&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;控制台&lt;/strong&gt;中 &quot;Cleaned text&quot; 的内容是否正确（没有行号）&lt;/li&gt;
&lt;li&gt;页面上是否有 &lt;code&gt;&amp;lt;div class=&quot;mermaid&quot;&amp;gt;&lt;/code&gt; 元素&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果需要，可以截图或复制控制台的输出，我可以继续帮你调试。&lt;/p&gt;
&lt;h2&gt;额外优化建议&lt;/h2&gt;
&lt;p&gt;如果一切正常，你还可以添加暗色主题支持：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  import mermaid from &quot;mermaid&quot;;
  
  // 检测主题
  const isDark = document.documentElement.classList.contains(&apos;dark&apos;);
  
  mermaid.initialize({ 
    startOnLoad: false,
    theme: isDark ? &apos;dark&apos; : &apos;default&apos;,  // 👈 根据主题切换
    securityLevel: &apos;loose&apos;,
  });
  
  // ... 其余代码
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样 Mermaid 图表会自动适配你网站的深色/浅色主题！&lt;/p&gt;
</content:encoded></item><item><title>Sort</title><link>https://lxy-alexander.github.io/blog/posts/algorithm/sort/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/algorithm/sort/</guid><description>Sort</description><pubDate>Sat, 31 Jan 2026 00:00:00 GMT</pubDate><content:encoded/></item><item><title>Python Sort</title><link>https://lxy-alexander.github.io/blog/posts/python/python-sort/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/python/python-sort/</guid><description>Python Sort</description><pubDate>Sat, 31 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;1) Sort a List (In-Place)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;a = [3, 1, 2]
a.sort()
print(a)  # [1, 2, 3]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;2) Return a New Sorted List (Do Not Modify the Original)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;a = [3, 1, 2]
b = sorted(a)
print(b)  # [1, 2, 3]
print(a)  # [3, 1, 2]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;3) Sort in Descending Order&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;a.sort(reverse=True)
# or
b = sorted(a, reverse=True)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;4) Sort by Key (Common for Tuples / Objects)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;arr = [(1, 5), (2, 3), (3, 4)]
arr.sort(key=lambda x: x[1])
print(arr)  # sort by the second element

&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;5) Sort by multiple key and value&lt;/h2&gt;
&lt;h3&gt;1.Ascending, Ascending&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;arr.sort(key=lambda x: (x[0], x[1]))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;arr = [(2, 0), (1, 4), (2, 1), (1, 3)]
arr.sort(key=lambda x: (x[0], x[1]))
print(arr)
# [(1, 3), (1, 4), (2, 0), (2, 1)]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Meaning:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;sort by &lt;code&gt;x[0]&lt;/code&gt; first&lt;/li&gt;
&lt;li&gt;if &lt;code&gt;x[0]&lt;/code&gt; is equal, sort by &lt;code&gt;x[1]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;2.Ascending + Descending&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;arr.sort(key=lambda x: (x[0], -x[1]))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Ascending + Descending (most common in contests)
arr = [(2, 0), (1, 4), (2, 1), (1, 3)]
arr.sort(key=lambda x: (x[0], -x[1]))
print(arr)
# [(1, 4), (1, 3), (2, 1), (2, 0)]
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;3.All descending (two ways)&lt;/h3&gt;
&lt;h4&gt;Method A: reverse=True (global reverse) ==reverse=True==&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;arr.sort(key=lambda x: (x[0], x[1]), reverse=True)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;⚠️ This reverses the whole result, not “first asc, second desc”.&lt;/p&gt;
&lt;h4&gt;Method B: negate each key (more flexible)&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;arr.sort(key=lambda x: (-x[0], -x[1]))
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;4.key&amp;amp;value sorting for strings&lt;/h3&gt;
&lt;p&gt;Sort by length descending, then lexicographically ascending&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;words.sort(key=lambda s: (-len(s), s))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;words = [&quot;apple&quot;, &quot;bat&quot;, &quot;banana&quot;, &quot;app&quot;]
words.sort(key=lambda s: (-len(s), s))
print(words)
# [&apos;banana&apos;, &apos;apple&apos;, &apos;app&apos;, &apos;bat&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;5.Sort a dictionary / Counter by value, then key&lt;/h3&gt;
&lt;p&gt;Example: sort by frequency descending, then number ascending&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from collections import Counter

# Sort a dictionary / Counter by value, then key
nums = [1,1,1,2,2,3,3,4]
cnt = Counter(nums)
print(cnt)
res = sorted(cnt.items(), key=lambda x: (-x[1], x[0]))
print(res)
# Counter({1: 3, 2: 2, 3: 2, 4: 1})
# [(1, 3), (2, 2), (3, 2), (4, 1)]

nums = {4:1, 1:3, 2:2, 3:2}
print(nums)
res = sorted(nums.items(), key=lambda x: (-x[1], x[0]))
print(res)
# [(1, 3), (2, 2), (3, 2), (4, 1)]
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Passwordless Remote Login</title><link>https://lxy-alexander.github.io/blog/posts/tools/passwordless-remote-login/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/tools/passwordless-remote-login/</guid><description>Passwordless Remote Login</description><pubDate>Fri, 30 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;&lt;strong&gt;I. SSH Passwordless Login — Key-Based Authentication&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt;
&amp;lt;strong&amp;gt;Overview:&amp;lt;/strong&amp;gt; The goal is to log in to a remote server (e.g., &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;spiedie.binghamton.edu&amp;lt;/code&amp;gt;) from Mac/Windows/Linux &amp;lt;strong&amp;gt;without entering a password&amp;lt;/strong&amp;gt;, and to make VS Code Remote-SSH connections more stable. This is achieved by replacing password-based authentication with &amp;lt;strong&amp;gt;public/private key authentication (公钥/私钥认证)&amp;lt;/strong&amp;gt;.
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1. Core Concept: How SSH Passwordless Login Works&lt;/h2&gt;
&lt;p&gt;SSH &quot;passwordless login&quot; does not skip identity verification — it replaces password-based authentication with key-based authentication.&lt;/p&gt;
&lt;h3&gt;1) Two Key Files&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Private Key (私钥)&amp;lt;/span&amp;gt;: stored on your local machine
Example: &lt;code&gt;~/.ssh/id_ed25519&lt;/code&gt;
&amp;lt;span style=&quot;color:#C0392B;font-weight:600&quot;&amp;gt;Must never be leaked or shared&amp;lt;/span&amp;gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Public Key (公钥)&amp;lt;/span&amp;gt;: can be sent to the server
Example: &lt;code&gt;~/.ssh/id_ed25519.pub&lt;/code&gt;
Safe to share openly / copy to servers&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2) How Does the Server Remember You?&lt;/h3&gt;
&lt;p&gt;The server stores your public key in:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;~/.ssh/authorized_keys
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once added, the server will allow anyone who holds the corresponding private key to log in.&lt;/p&gt;
&lt;h3&gt;3) What Happens During Authentication (Simplified Flow)&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;You initiate a connection: &lt;code&gt;ssh user@host&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The server looks up your public key in &lt;code&gt;authorized_keys&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The server sends a random &lt;strong&gt;challenge&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Your machine signs the challenge using your &lt;strong&gt;private key&lt;/strong&gt; (the private key never leaves your machine)&lt;/li&gt;
&lt;li&gt;The server verifies the signature using your &lt;strong&gt;public key&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Verification passes → Login succeeds (no password prompt)&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Step-by-Step Setup&lt;/h2&gt;
&lt;p&gt;The following examples use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Username: &lt;code&gt;xli49&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Host: &lt;code&gt;spiedie.binghamton.edu&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;1) Step 1: Generate an SSH Key on Your Local Machine&lt;/h3&gt;
&lt;p&gt;Check whether a key already exists:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ls ~/.ssh/id_ed25519 ~/.ssh/id_rsa 2&amp;gt;/dev/null
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If not, generate one (ed25519 recommended):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssh-keygen -t ed25519 -C &quot;xli49@spiedie.binghamton.edu&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#F5F5F5;border-left:4px solid #E8600A;border-radius:0 6px 6px 0;padding:12px 16px;margin:14px 0;font-size:14px;line-height:1.85&quot;&amp;gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Note: &amp;lt;/span&amp;gt; For a completely password-free experience, press Enter to leave the passphrase empty. For better security, set a passphrase and use it with Keychain / &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;ssh-agent&amp;lt;/code&amp;gt;.&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;2) Step 2: Copy the Public Key to the Server&lt;/h3&gt;
&lt;p&gt;✅ Recommended (when &lt;code&gt;ssh-copy-id&lt;/code&gt; is available):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssh-copy-id xli49@spiedie.binghamton.edu
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Enter your password once — that&apos;s the last time.&lt;/p&gt;
&lt;h4&gt;Manual Method (if &lt;code&gt;ssh-copy-id&lt;/code&gt; is not available)&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;cat ~/.ssh/id_ed25519.pub | ssh xli49@spiedie.binghamton.edu \
&quot;mkdir -p ~/.ssh &amp;amp;&amp;amp; cat &amp;gt;&amp;gt; ~/.ssh/authorized_keys &amp;amp;&amp;amp; chmod 700 ~/.ssh &amp;amp;&amp;amp; chmod 600 ~/.ssh/authorized_keys&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3) Step 3: Test the Passwordless Login&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;ssh xli49@spiedie.binghamton.edu
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;✅ If no password prompt appears → setup successful.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. Configuring &lt;code&gt;~/.ssh/config&lt;/code&gt; (Recommended)&lt;/h2&gt;
&lt;p&gt;Edit the config file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nano ~/.ssh/config
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Recommended configuration (using the full hostname as the &lt;code&gt;Host&lt;/code&gt;):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Host spiedie.binghamton.edu
    HostName spiedie.binghamton.edu
    User xli49
    ServerAliveInterval 300
    ServerAliveCountMax 120
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After saving, connect with just:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssh spiedie.binghamton.edu
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;4. When Is &lt;code&gt;IdentityFile&lt;/code&gt; Needed?&lt;/h2&gt;
&lt;h3&gt;1) Usually Not Required&lt;/h3&gt;
&lt;p&gt;SSH automatically tries the following keys in order:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;~/.ssh/id_ed25519&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;~/.ssh/id_rsa&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Any keys already loaded into &lt;code&gt;ssh-agent&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If your key is in one of these default locations, you typically do not need to specify &lt;code&gt;IdentityFile&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;2) When You Must Specify &lt;code&gt;IdentityFile&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;You have multiple keys&amp;lt;/span&amp;gt; and SSH might pick the wrong one&lt;/li&gt;
&lt;li&gt;&amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;Your key is not in a default path&amp;lt;/span&amp;gt;&lt;/li&gt;
&lt;li&gt;&amp;lt;span style=&quot;color:#2980B9&quot;&amp;gt;The server requires a specific key&amp;lt;/span&amp;gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Host spiedie.binghamton.edu
    HostName spiedie.binghamton.edu
    User xli49
    IdentityFile ~/.ssh/id_ed25519
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;5. Preventing VS Code Remote-SSH Disconnections (KeepAlive)&lt;/h2&gt;
&lt;p&gt;Common causes of disconnection:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The school network or firewall clears long-idle connections&lt;/li&gt;
&lt;li&gt;The remote session is considered idle when there is no output for an extended period&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Solution: enable SSH heartbeat packets (KeepAlive):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ServerAliveInterval 30
ServerAliveCountMax 120
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What this means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Send a heartbeat packet every &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;30 seconds&amp;lt;/span&amp;gt;&lt;/li&gt;
&lt;li&gt;Allow up to &amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;120 consecutive non-responses&amp;lt;/span&amp;gt; (~1 hour) before disconnecting&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;6. Troubleshooting&lt;/h2&gt;
&lt;h3&gt;1) Check Which Key SSH Is Using&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;ssh -v xli49@spiedie.binghamton.edu
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Look for lines like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Offering public key: ...&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Authentication succeeded&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2) Check the Default Shell on the Server&lt;/h3&gt;
&lt;p&gt;Run remotely to rule out misconfiguration:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;echo $SHELL
getent passwd xli49 | cut -d: -f7
which zsh
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt; Generate a key with &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;ssh-keygen -t ed25519&amp;lt;/code&amp;gt;, copy it to the server once with &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;ssh-copy-id&amp;lt;/code&amp;gt;, add &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;ServerAliveInterval 30&amp;lt;/code&amp;gt; to &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;~/.ssh/config&amp;lt;/code&amp;gt;, and you&apos;ll never type a password or suffer a dropped VS Code connection again.&amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Double&amp;Triple Pointers</title><link>https://lxy-alexander.github.io/blog/posts/algorithm/doubletriple-pointers/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/algorithm/doubletriple-pointers/</guid><description>Double&amp;Triple Pointers</description><pubDate>Thu, 29 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Double Pointers&lt;/h1&gt;
&lt;hr /&gt;
&lt;h2&gt;Check if &lt;code&gt;p&lt;/code&gt; is a subsequence of &lt;code&gt;s&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Check whether &lt;code&gt;p&lt;/code&gt; is a subsequence of &lt;code&gt;s&lt;/code&gt;  after some characters of &lt;code&gt;s&lt;/code&gt; have been removed.&lt;/p&gt;
&lt;h3&gt;✅ 最优雅（推荐）——只写一个 if&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;j = 0
for i in range(m):
    if not removed[i] and j &amp;lt; len(p) and s[i] == p[j]:
        j += 1
return j == len(p)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;int j = 0;
for (int i = 0; i &amp;lt; m; i++) {
    if (!removed[i] &amp;amp;&amp;amp; j &amp;lt; (int)p.size() &amp;amp;&amp;amp; s[i] == p[j]) {
        j++;
    }
}
return j == (int)p.size();
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;✅ 更“清爽”的 continue 版（逻辑最直观）&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;j = 0
for i in range(m):
    if removed[i]:
        continue
    if j &amp;lt; len(p) and s[i] == p[j]:
        j += 1
return j == len(p)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;(int)&lt;/code&gt; 是为了避免 &lt;code&gt;int&lt;/code&gt; 和 &lt;code&gt;size_t(无符号)&lt;/code&gt; 混着比较导致警告/潜在bug。在 C++ 里如果你把 &lt;code&gt;j&lt;/code&gt; 定义成 &lt;code&gt;size_t&lt;/code&gt;（无符号），然后==写了 &lt;code&gt;j--&lt;/code&gt;，会出现一个非常坑的现象：不会变成 -1，而是“下溢”变成一个超级大的数。==&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int j = 0;
for (int i = 0; i &amp;lt; m; i++) {
    if (removed[i]) continue;
    if (j &amp;lt; (int)p.size() &amp;amp;&amp;amp; s[i] == p[j]) {
        j++;
    }
}
return j == (int)p.size();
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;✅ 用 for ch in s 更优雅（但要处理 removed）&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;j = 0
for ch in s:
    if j &amp;lt; len(p) and ch == p[j]:
        j += 1
return j == len(p)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;int j = 0;
for (char ch : s) {
    if (j &amp;lt; (int)p.size() &amp;amp;&amp;amp; ch == p[j]) {
        j++;
    }
}
return j == (int)p.size();
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
</content:encoded></item><item><title>zsh-autosuggestions</title><link>https://lxy-alexander.github.io/blog/posts/tools/zsh-autosuggestions/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/tools/zsh-autosuggestions/</guid><description>zsh-autosuggestions</description><pubDate>Thu, 29 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;&lt;strong&gt;I. zsh Plugins — Autosuggestions &amp;amp; Syntax Highlighting&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:#EBF0FF;border-left:4px solid #3B5BDB;border-radius:0 6px 6px 0;padding:14px 18px;margin:16px 0;line-height:1.9&quot;&amp;gt;
&amp;lt;strong&amp;gt;Overview:&amp;lt;/strong&amp;gt; Two essential zsh plugins for a better terminal experience: &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;zsh-autosuggestions&amp;lt;/code&amp;gt; provides auto-complete, parameter hints, and history-based suggestions; &amp;lt;code style=&quot;background:#E8F4FD;color:#1a3a5c;border-radius:4px;padding:1px 6px&quot;&amp;gt;zsh-syntax-highlighting&amp;lt;/code&amp;gt; colorizes valid commands and flags errors in real time as you type.
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;1. macOS&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Command suggestions&amp;lt;/span&amp;gt; appear in gray while typing (pulled from shell history)&lt;/li&gt;
&lt;li&gt;&amp;lt;span style=&quot;color:#E8600A;font-weight:700&quot;&amp;gt;Syntax highlighting&amp;lt;/span&amp;gt; colors valid commands and highlights mistakes inline&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;1) Install (Homebrew)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;brew install zsh-autosuggestions zsh-syntax-highlighting
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) Enable (add to &lt;code&gt;~/.zshrc&lt;/code&gt;)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;echo &apos;source /usr/local/share/zsh-autosuggestions/zsh-autosuggestions.zsh&apos; &amp;gt;&amp;gt; ~/.zshrc
echo &apos;source /usr/local/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh&apos; &amp;gt;&amp;gt; ~/.zshrc
source ~/.zshrc
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;✅ Works immediately.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&amp;lt;div style=&quot;background:linear-gradient(135deg,#EBF0FF 0%,#FFF3E0 100%);border:1.5px solid #c5d3ff;border-radius:8px;padding:14px 20px;margin-top:24px&quot;&amp;gt;&amp;lt;span style=&quot;color:#3B5BDB;font-weight:700&quot;&amp;gt;💡 One-line Takeaway&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt; Install both plugins via Homebrew, source them in &amp;lt;code style=&quot;background:#FFF3E0;color:#7a2e00;border-radius:4px;padding:1px 6px&quot;&amp;gt;~/.zshrc&amp;lt;/code&amp;gt;, and your shell gains history-based grey suggestions and live syntax coloring instantly.&amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Greedy&amp;Linear Scan</title><link>https://lxy-alexander.github.io/blog/posts/algorithm/greedylinear-scan/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/algorithm/greedylinear-scan/</guid><description>Greedy&amp;Linear Scan</description><pubDate>Tue, 27 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;This algorithm is a &lt;strong&gt;linear scan (one-pass) greedy counting algorithm&lt;/strong&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Linear Scan / One-pass Traversal&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Greedy&lt;/strong&gt; (always keeps the best answer so far)&lt;/li&gt;
&lt;li&gt;Can also be seen as a simple &lt;strong&gt;state tracking&lt;/strong&gt; approach&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h3&gt;&lt;a href=&quot;https://leetcode.com/problems/max-consecutive-ones/&quot;&gt;485. Max Consecutive Ones&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Given a binary array &lt;code&gt;nums&lt;/code&gt;, return &lt;em&gt;the maximum number of consecutive&lt;/em&gt; &lt;code&gt;1&lt;/code&gt;&lt;em&gt;&apos;s in the array&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: nums = [1,1,0,1,1,1]
Output: 3
Explanation: The first two digits or the last three digits are consecutive 1s. The maximum number of consecutive 1s is 3.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: nums = [1,0,1,1,0,1]
Output: 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Constraints:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= nums.length &amp;lt;= 105&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nums[i]&lt;/code&gt; is either &lt;code&gt;0&lt;/code&gt; or &lt;code&gt;1&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def findMaxConsecutiveOnes(self, nums: List[int]) -&amp;gt; int:
        mx = 0
        cnt = 0
        for x in nums:
            if x == 1:
                cnt += 1
            else:
                mx = max(mx, cnt)
                cnt = 0
            mx = max(mx, cnt)
        return mx
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
public:
    int findMaxConsecutiveOnes(vector&amp;lt;int&amp;gt;&amp;amp; nums) {
        int ans = 0;
        int cnt = 0;
        for (int x : nums) {
            if (x == 0) {
                ans = max(ans, cnt);
                cnt = 0;
            } else {
                cnt += 1;
            }
        }
        ans = max(ans, cnt);
        return ans;
    }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;a href=&quot;https://leetcode.com/problems/consecutive-characters/&quot;&gt;1446. Consecutive Characters&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;strong&gt;power&lt;/strong&gt; of the string is the maximum length of a non-empty substring that contains only one unique character.&lt;/p&gt;
&lt;p&gt;Given a string &lt;code&gt;s&lt;/code&gt;, return &lt;em&gt;the &lt;strong&gt;power&lt;/strong&gt; of&lt;/em&gt; &lt;code&gt;s&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: s = &quot;leetcode&quot;
Output: 2
Explanation: The substring &quot;ee&quot; is of length 2 with the character &apos;e&apos; only.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: s = &quot;abbcccddddeeeeedcba&quot;
Output: 5
Explanation: The substring &quot;eeeee&quot; is of length 5 with the character &apos;e&apos; only.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Constraints:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= s.length &amp;lt;= 500&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;s&lt;/code&gt; consists of only lowercase English letters.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def maxPower(self, s: str) -&amp;gt; int:
        ans = 1
        cnt = 1
        for i in range(len(s)):
            if s[i] == s[i - 1]:
                cnt += 1
            else:
                cnt = 1
            ans = max(ans, cnt)
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
public:
    int maxPower(string s) {
        int ans = 0;
        int l = 0;
        int n = s.size();
        map&amp;lt;char, int&amp;gt; cnt;
        for (int r = 0; r &amp;lt; n; r++) {
            cnt[s[r]]++;
            while (cnt.size() &amp;gt; 1) {
                cnt[s[l]]--;
                if (cnt[s[l]] == 0) {
                    cnt.erase(s[l]);
                }
                l++;
            }
            ans = max(ans, r - l + 1);
        }
        return ans;
    }
};
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>bisect left</title><link>https://lxy-alexander.github.io/blog/posts/python/bisect-left/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/python/bisect-left/</guid><description>bisect left</description><pubDate>Mon, 26 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;速记总结&lt;/h2&gt;
&lt;h3&gt;找 “第一个 &amp;gt;= x”&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;i = bisect_left(a, x)
if i &amp;lt; len(a):
    ans = a[i]   # 第一个 &amp;gt;= x
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;找 “最后一个 &amp;lt; x”&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;i = bisect_left(a, x)
if i &amp;gt; 0:
    ans = a[i-1]  # 最后一个 &amp;lt; x
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;（在 Heaters 里就是左边最近的 heater）&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;判断 x 是否存在于数组中&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;i = bisect_left(a, x)
exists = (i &amp;lt; len(a) and a[i] == x)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;找 “x 应该插入的位置”&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;pos = bisect_left(a, x)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;插入到 &lt;code&gt;pos&lt;/code&gt; 可以保持有序（并且插到同值最左边）&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;找 “小于 x 的元素个数”&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;count = bisect_left(a, x)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;因为 &lt;code&gt;i&lt;/code&gt; 左边全是 &lt;code&gt;&amp;lt; x&lt;/code&gt; 的。&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;找 “&amp;gt;= x 的元素个数”&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;count = len(a) - bisect_left(a, x)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;&lt;/h2&gt;
&lt;h2&gt;Example&lt;/h2&gt;
&lt;p&gt;r = len(nums) - 1 # 记得减1&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import bisect as b

nums = [1, 2, 2, 3]

# 第一个 &amp;gt;= x 的位置
a = b.bisect_left(nums, 2)
print(a) # 1

def find_bisect_left(nums, k):
    l = 0
    r = len(nums) - 1 # 记得减1
    while l &amp;lt;= r:
        m = (l + r) // 2
        if nums[m] &amp;gt;= k:
            r = m - 1
        else:
            l = m + 1
    return l

print(find_bisect_left(nums, 2))


# 第一个 &amp;gt; x 的位置
a = b.bisect(nums, 2)
print(a) # 3
a = b.bisect_right(nums, 2)
print(a) # 4

def find_bisect_right(nums, k):
    l = 0
    r = len(nums) - 1
    while l &amp;lt;= r:
        m = (l + r) // 2
        if nums[m] &amp;gt; k:
            r = m - 1
        else:
            l = m + 1
    return l

print(find_bisect_right(nums, 2))
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>typing feature - typedDict</title><link>https://lxy-alexander.github.io/blog/posts/python/typing-feature---typeddict/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/python/typing-feature---typeddict/</guid><description>typing feature - typedDict</description><pubDate>Mon, 26 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;来自 &lt;code&gt;typing&lt;/code&gt;（Python 3.11+）或 &lt;code&gt;typing_extensions&lt;/code&gt;（旧版本），作用是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在 &lt;code&gt;TypedDict&lt;/code&gt; 里把某个字段标记为 &lt;strong&gt;可选（不是必须提供）&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;示例&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;from typing import TypedDict, NotRequired

class User(TypedDict):
    name: str                 # 必填
    age: NotRequired[int]     # 可选
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样写就允许：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;u1: User = {&quot;name&quot;: &quot;Tom&quot;}              # ✅ ok
u2: User = {&quot;name&quot;: &quot;Tom&quot;, &quot;age&quot;: 18}   # ✅ ok
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果不用 NotRequired（默认都是必填）&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class User(TypedDict):
    name: str
    age: int
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那么：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{&quot;name&quot;: &quot;Tom&quot;}   # ❌ 类型检查会报缺少 age
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Mermaid</title><link>https://lxy-alexander.github.io/blog/posts/tools/mermaid/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/tools/mermaid/</guid><description>Mermaid</description><pubDate>Sun, 25 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Export using Mermaid CLI&lt;/h2&gt;
&lt;h2&gt;1) Install Mermaid CLI&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;npm install -g @mermaid-js/mermaid-cli
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;2) Save your diagram code into &lt;code&gt;arch.mmd&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Example content:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sequenceDiagram
    A-&amp;gt;&amp;gt;B: hi
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;3) Export as PNG&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;mmdc -i arch.mmd -o arch.png
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;4Export as SVG (optional)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;mmdc -i arch.mmd -o arch.svg
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Binary Search</title><link>https://lxy-alexander.github.io/blog/posts/algorithm/binary-search/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/algorithm/binary-search/</guid><description>Binary Search</description><pubDate>Sat, 24 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;&lt;strong&gt;I.&lt;/strong&gt; Binary Search Master Guide: From Logic to Universal Templates&lt;/h1&gt;
&lt;h2&gt;1. The Core Essence(底层/核心本质): Monotonicity&lt;/h2&gt;
&lt;p&gt;The core of Binary Search isn&apos;t &quot;sorting,&quot; but &lt;strong&gt;&quot;Binary Properties&quot; (Two-Segment Property(二段性))&lt;/strong&gt;. As long as a function &lt;code&gt;check(x)&lt;/code&gt; exists such that the search range presents one of the following two patterns, Binary Search is applicable(合适的，恰当的):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Find Minimum (First True):&lt;/strong&gt; &lt;code&gt;[False, False, ..., True, True]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Find Maximum (Last True):&lt;/strong&gt; &lt;code&gt;[True, True, ..., False, False]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;2. Determining the Search Range &lt;code&gt;[left, right]&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Set the boundaries based on the &lt;strong&gt;physical meaning of x&lt;/strong&gt;:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Type&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;left (Min Valid Value)&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;right (Definitely Feasible)&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Classic Problem&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Index&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;n - 1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Basic Search&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;min(time) * totalTrips&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://leetcode.com/problems/minimum-time-to-complete-trips/&quot;&gt;2187. Min Time&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Speed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;max(piles)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://leetcode.com/problems/koko-eating-bananas/&quot;&gt;875. Koko Eating Bananas&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Capacity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;max(weights)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sum(weights)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://leetcode.com/problems/capacity-to-ship-packages-within-d-days/&quot;&gt;1011. Ship Packages&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Divisor&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;max(nums)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://leetcode.com/problems/find-the-smallest-divisor-given-a-threshold/&quot;&gt;1283. Smallest Divisor&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Distance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0&lt;/code&gt; or &lt;code&gt;1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;max(pos) - min(pos)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://leetcode.com/problems/magnetic-force-between-two-balls/&quot;&gt;1552. Magnetic Force&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h3&gt;1) Time-based: &lt;a href=&quot;https://leetcode.com/problems/minimum-time-to-complete-trips/&quot;&gt;2187. Minimum Time to Complete Trips&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Summary&lt;/strong&gt;: Given the time each car takes to complete one trip, find the &lt;strong&gt;minimum total time&lt;/strong&gt; required for all cars to complete at least &lt;code&gt;totalTrips&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Why these boundaries&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;left = 1&lt;/code&gt;&lt;/strong&gt;: Time cannot be zero.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;right = min(time) * totalTrips&lt;/code&gt;&lt;/strong&gt;: This is a conservative upper bound. Even if only the &lt;strong&gt;fastest car&lt;/strong&gt; were running, the time it takes to finish all trips alone would certainly be enough.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2) Speed-based: &lt;a href=&quot;https://leetcode.com/problems/koko-eating-bananas/&quot;&gt;875. Koko Eating Bananas&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Summary&lt;/strong&gt;: There are $n$ piles of bananas. You must finish all of them within $h$ hours. Find the &lt;strong&gt;minimum eating speed&lt;/strong&gt; $K$ (bananas per hour). Note: Koko can only eat from one pile per hour.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Why these boundaries&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;left = 1&lt;/code&gt;&lt;/strong&gt;: Speed must be at least 1, or she will never finish.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;right = max(piles)&lt;/code&gt;&lt;/strong&gt;: If your speed equals the &lt;strong&gt;largest pile&lt;/strong&gt;, you are guaranteed to finish one pile per hour. Since you can&apos;t eat more than one pile an hour anyway, any speed higher than this is redundant.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3) Capacity-based: &lt;a href=&quot;https://leetcode.com/problems/capacity-to-ship-packages-within-d-days/&quot;&gt;1011. Capacity To Ship Packages Within D Days&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Summary&lt;/strong&gt;: Packages must be shipped in the order given within $D$ days. Find the &lt;strong&gt;minimum weight capacity&lt;/strong&gt; of the conveyor belt.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Why these boundaries&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;left = max(weights)&lt;/code&gt;&lt;/strong&gt;: The belt must be able to carry the &lt;strong&gt;heaviest&lt;/strong&gt; single package; otherwise, that package can never be shipped.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;right = sum(weights)&lt;/code&gt;&lt;/strong&gt;: The extreme case—shipping every single package on the very first day. The total sum of weights is the absolute maximum capacity needed.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4) Divisor-based: &lt;a href=&quot;https://leetcode.com/problems/find-the-smallest-divisor-given-a-threshold/&quot;&gt;1283. Find the Smallest Divisor Given a Threshold&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Summary&lt;/strong&gt;: Each number in an array is divided by $d$ (rounded up) and summed. The sum must be $\le$ a given threshold. Find the &lt;strong&gt;minimum&lt;/strong&gt; $d$.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Why these boundaries&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;left = 1&lt;/code&gt;&lt;/strong&gt;: The divisor cannot be zero.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;right = max(nums)&lt;/code&gt;&lt;/strong&gt;: When the divisor equals the maximum value in the array, every result becomes $1$ (except the max itself which also becomes 1). This is the effective boundary that reduces the &quot;sum&quot; to its minimum possible value (the length of the array $n$).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5) Distance-based: &lt;a href=&quot;https://leetcode.com/problems/magnetic-force-between-two-balls/&quot;&gt;1552. Magnetic Force Between Two Balls&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Summary&lt;/strong&gt;: Place $M$ balls in baskets such that the &lt;strong&gt;minimum distance&lt;/strong&gt; between any two balls is as large as possible. Find this &lt;strong&gt;maximum minimum distance&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Why these boundaries&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;left = 1&lt;/code&gt;&lt;/strong&gt;: The balls must be separated by at least 1 unit of distance (assuming distinct basket positions).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;right = max(pos) - min(pos)&lt;/code&gt;&lt;/strong&gt;: The theoretical maximum distance occurs when you place only two balls: one at the very first basket and one at the very last.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;strong&gt;Would you like me to combine all these English sections into one single, clean Markdown file for you to save?&lt;/strong&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. Core Templates: Closed Interval &lt;code&gt;while l &amp;lt;= r&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;This is the most robust implementation. It is recommended to use this consistently.&lt;/p&gt;
&lt;h3&gt;1) Find Minimum (First True)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Find the smallest $x$ such that &lt;code&gt;check(x)&lt;/code&gt; is True.&lt;/p&gt;
&lt;p&gt;Python&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;l, r = min_valid, max_feasible
while l &amp;lt;= r:
    mid = l + (r - l) // 2
    if check(mid): # Feasible, but look for smaller ones to the left
        r = mid - 1
    else:          # Not feasible, must increase x
        l = mid + 1
return l  # When loop ends, l points to the first True
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2) Find Maximum (Last True)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Find the largest $x$ such that &lt;code&gt;check(x)&lt;/code&gt; is True.&lt;/p&gt;
&lt;p&gt;Python&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;while l &amp;lt;= r:
    mid = l + (r - l) // 2
    if check(mid): # Feasible, try to find a larger one to the right
        l = mid + 1
    else:          # Not feasible, must decrease x
        r = mid - 1
return r  # When loop ends, r points to the last True
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;4. Advanced Tips &amp;amp; Mathematical Details&lt;/h2&gt;
&lt;h3&gt;1) Finding Left/Right Boundaries of Elements&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Left Boundary (Lower Bound):&lt;/strong&gt; First index where &lt;code&gt;element &amp;gt;= target&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Right Boundary (Upper Bound):&lt;/strong&gt; Last index where &lt;code&gt;element == target&lt;/code&gt;.
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Trick:&lt;/strong&gt; &lt;code&gt;lowerBound(target + 1) - 1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Principle:&lt;/strong&gt; Find the start of the first number &lt;code&gt;&amp;gt; target&lt;/code&gt;, then move back one spot. If &lt;code&gt;target + 1&lt;/code&gt; doesn&apos;t exist, the search returns &lt;code&gt;n&lt;/code&gt;, and &lt;code&gt;n - 1&lt;/code&gt; correctly identifies the last element.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2) Avoiding Overflow&lt;/h3&gt;
&lt;p&gt;In C++/Java, &lt;code&gt;left + right&lt;/code&gt; can exceed $2^{31} - 1$.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Standard approach:&lt;/strong&gt; &lt;code&gt;mid = left + (right - left) / 2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Python Note:&lt;/strong&gt; Though Python handles arbitrarily large integers, keeping this habit helps in understanding low-level memory constraints.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3) Ceiling Division Conversion&lt;/h3&gt;
&lt;p&gt;When calculating &quot;required days/trips,&quot; you often need $\lceil \frac{b}{a} \rceil$:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Universal Formula:&lt;/strong&gt; &lt;code&gt;(b + a - 1) // a&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Logic:&lt;/strong&gt; As long as $b$ is not perfectly divisible by $a$, adding $a-1$ will always force the integer division to round up by one.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;5. Post-Loop State Cheat Sheet&lt;/h2&gt;
&lt;p&gt;When the &lt;code&gt;while l &amp;lt;= r&lt;/code&gt; loop terminates:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Pointer&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Physical Meaning&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;l (low)&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Points to the &lt;strong&gt;first&lt;/strong&gt; element that &lt;strong&gt;satisfies condition&lt;/strong&gt; (or &lt;code&gt;&amp;gt;= target&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;r (high)&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Points to the &lt;strong&gt;last&lt;/strong&gt; element that &lt;strong&gt;fails condition&lt;/strong&gt; (or &lt;code&gt;&amp;lt; target&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h1&gt;&lt;strong&gt;II&lt;/strong&gt;.Binary Search Questions&lt;/h1&gt;
&lt;h2&gt;Find the boundary of element in sorted array&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.com/problems/find-first-and-last-position-of-element-in-sorted-array/&quot;&gt;34. Find First and Last Position of Element in Sorted Array&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Given an array of integers &lt;code&gt;nums&lt;/code&gt; sorted in non-decreasing order, find the starting and ending position of a given &lt;code&gt;target&lt;/code&gt; value.&lt;/p&gt;
&lt;p&gt;If &lt;code&gt;target&lt;/code&gt; is not found in the array, return &lt;code&gt;[-1, -1]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You must write an algorithm with &lt;code&gt;O(log n)&lt;/code&gt; runtime complexity.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: nums = [5,7,7,8,8,10], target = 8
Output: [3,4]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: nums = [5,7,7,8,8,10], target = 6
Output: [-1,-1]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 3:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: nums = [], target = 0
Output: [-1,-1]
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
private:
    int lowerBound(vector&amp;lt;int&amp;gt;&amp;amp; nums, int n, int target) {
        int left = 0;
        int right = n - 1;
        while (left &amp;lt;= right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] &amp;gt;= target) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return left;
    }
public:
    vector&amp;lt;int&amp;gt; searchRange(vector&amp;lt;int&amp;gt;&amp;amp; nums, int target) {
        int n = nums.size();
        int start = lowerBound(nums, n, target);
        if (start == n or nums[start] != target) {
            return {-1, -1};
        }
        // find the first element that is greater than target, then use its index to minus 1, we can get the the end position of target
        int end = lowerBound(nums, n, target + 1) - 1;
        return {start, end};
    }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def searchRange(self, nums: List[int], target: int) -&amp;gt; List[int]:
        def lower_bound(nums, n, target):
            l = 0
            r = n - 1
            while l &amp;lt;= r:
                m = (l + r) // 2
                if nums[m] &amp;gt;= target:
                    r -= 1
                else:
                    l += 1
            return l
        
        n = len(nums)
        start = lower_bound(nums, n, target)
        if start == n or nums[start] != target:
            return [-1, -1]
        end = lower_bound(nums, n, target + 1) - 1
        return [start, end]

&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;a href=&quot;https://leetcode.com/problems/find-the-smallest-divisor-given-a-threshold/&quot;&gt;1283. Find the Smallest Divisor Given a Threshold&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Given an array of integers &lt;code&gt;nums&lt;/code&gt; and an integer &lt;code&gt;threshold&lt;/code&gt;, we will choose a positive integer &lt;code&gt;divisor&lt;/code&gt;, divide all the array by it, and sum the division&apos;s result. Find the &lt;strong&gt;smallest&lt;/strong&gt; &lt;code&gt;divisor&lt;/code&gt; such that the result mentioned above is less than or equal to &lt;code&gt;threshold&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Each result of the division is rounded to the nearest integer greater than or equal to that element. (For example: &lt;code&gt;7/3 = 3&lt;/code&gt; and &lt;code&gt;10/2 = 5&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;The test cases are generated so that there will be an answer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: nums = [1,2,5,9], threshold = 6
Output: 5
Explanation: We can get a sum to 17 (1+2+5+9) if the divisor is 1. 
If the divisor is 4 we can get a sum of 7 (1+1+2+3) and if the divisor is 5 the sum will be 5 (1+1+1+2). 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: nums = [44,22,33,11,1], threshold = 5
Output: 44
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Constraints:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= nums.length &amp;lt;= 5 * 104&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= nums[i] &amp;lt;= 106&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nums.length &amp;lt;= threshold &amp;lt;= 106&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;# Note: You don&apos;t need to sort the input array here because the answer is not required to be an element of the array; we are searching within the range from 1 to max(nums).
class Solution:
    def smallestDivisor(self, nums: List[int], threshold: int) -&amp;gt; int:
        l, r = 1, max(nums)
        while l &amp;lt;= r:
            m = (l + r) // 2
            total = 0
            if sum((x + m - 1) // m for x in nums) &amp;lt;= threshold:
                r = m - 1
            else:
                l = m + 1
        return l 
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;1) When to Shrink &lt;code&gt;l&lt;/code&gt; &amp;amp; &lt;code&gt;r&lt;/code&gt;?&lt;/h4&gt;
&lt;p&gt;The direction of your search depends on the condition:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;sum(mid) &amp;lt;= threshold&lt;/code&gt;&lt;/strong&gt;: &lt;code&gt;mid&lt;/code&gt; is &lt;strong&gt;feasible&lt;/strong&gt;, but a smaller answer might exist → &lt;strong&gt;Search Left&lt;/strong&gt; → &lt;code&gt;r = mid - 1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;sum(mid) &amp;gt; threshold&lt;/code&gt;&lt;/strong&gt;: &lt;code&gt;mid&lt;/code&gt; is &lt;strong&gt;too small&lt;/strong&gt;, you must increase it to satisfy the condition → &lt;strong&gt;Search Right&lt;/strong&gt; → &lt;code&gt;l = mid + 1&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2) Why is &lt;code&gt;l&lt;/code&gt; the answer after the loop?&lt;/h4&gt;
&lt;p&gt;The loop terminates when &lt;code&gt;l &amp;gt; r&lt;/code&gt; (specifically, &lt;code&gt;l = r + 1&lt;/code&gt;).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;r&lt;/code&gt;&lt;/strong&gt; stops at the &lt;strong&gt;last position that fails the condition&lt;/strong&gt; (the last &lt;code&gt;False&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;l&lt;/code&gt;&lt;/strong&gt; stops exactly one position to the right of &lt;code&gt;r&lt;/code&gt; (the first &lt;code&gt;True&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Logic:&lt;/strong&gt; Because we only move &lt;code&gt;l&lt;/code&gt; right when &lt;code&gt;mid&lt;/code&gt; is invalid, and move &lt;code&gt;r&lt;/code&gt; left when &lt;code&gt;mid&lt;/code&gt; is valid, the search concludes with(结束于) &lt;code&gt;r&lt;/code&gt; at the last invalid value and &lt;code&gt;l&lt;/code&gt; at the first valid value. Therefore, &lt;code&gt;l&lt;/code&gt; is the &lt;strong&gt;smallest feasible answer&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Find the least&lt;/h2&gt;
&lt;h3&gt;&lt;a href=&quot;https://leetcode.com/problems/minimum-time-to-complete-trips/&quot;&gt;2187. Minimum Time to Complete Trips&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You are given an array &lt;code&gt;time&lt;/code&gt; where &lt;code&gt;time[i]&lt;/code&gt; denotes the time taken by the &lt;code&gt;ith&lt;/code&gt; bus to complete &lt;strong&gt;one trip&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Each bus can make multiple trips &lt;strong&gt;successively&lt;/strong&gt;; that is, the next trip can start &lt;strong&gt;immediately after&lt;/strong&gt; completing the current trip. Also, each bus operates &lt;strong&gt;independently&lt;/strong&gt;; that is, the trips of one bus do not influence the trips of any other bus.&lt;/p&gt;
&lt;p&gt;You are also given an integer &lt;code&gt;totalTrips&lt;/code&gt;, which denotes the number of trips all buses should make &lt;strong&gt;in total&lt;/strong&gt;. Return &lt;em&gt;the &lt;strong&gt;minimum time&lt;/strong&gt; required for all buses to complete &lt;strong&gt;at least&lt;/strong&gt;&lt;/em&gt; &lt;code&gt;totalTrips&lt;/code&gt; &lt;em&gt;trips&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: time = [1,2,3], totalTrips = 5
Output: 3
Explanation:
- At time t = 1, the number of trips completed by each bus are [1,0,0]. 
  The total number of trips completed is 1 + 0 + 0 = 1.
- At time t = 2, the number of trips completed by each bus are [2,1,0]. 
  The total number of trips completed is 2 + 1 + 0 = 3.
- At time t = 3, the number of trips completed by each bus are [3,1,1]. 
  The total number of trips completed is 3 + 1 + 1 = 5.
So the minimum time needed for all buses to complete at least 5 trips is 3.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: time = [2], totalTrips = 1
Output: 2
Explanation:
There is only one bus, and it will complete its first trip at t = 2.
So the minimum time needed to complete 1 trip is 2.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Constraints:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= time.length &amp;lt;= 105&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= time[i], totalTrips &amp;lt;= 107&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;# Ensure that the right boundary is large enough to cover totalTrips. The most reliable way is min(time) * totalTrips.
class Solution:
    def minimumTime(self, time: List[int], totalTrips: int) -&amp;gt; int:
        min_t = min(time)
        l = min_t
        r = min_t * totalTrips
        while l &amp;lt;= r:
            m = (l + r) // 2
            if sum(m // x for x in time) &amp;gt;= totalTrips:
                r = m - 1
            else:
                l = m + 1
        # After the loop, &apos;l&apos; is the smallest time that satisfies the condition
        return l
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;a href=&quot;https://leetcode.com/problems/capacity-to-ship-packages-within-d-days/&quot;&gt;1011. Capacity To Ship Packages Within D Days&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A conveyor(传送带，传送装置；传播者，传达者) belt(腰带，皮带；传送带；地带) has packages that must be shipped from one port to another within &lt;code&gt;days&lt;/code&gt; days.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;ith&lt;/code&gt; package on the conveyor belt has a weight of &lt;code&gt;weights[i]&lt;/code&gt;. Each day, we load the ship with packages on the conveyor belt (in the order given by &lt;code&gt;weights&lt;/code&gt;). We may not load more weight than the maximum weight capacity of the ship.&lt;/p&gt;
&lt;p&gt;Return the least weight capacity of the ship that will result in all the packages on the conveyor belt being shipped within &lt;code&gt;days&lt;/code&gt; days.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: weights = [1,2,3,4,5,6,7,8,9,10], days = 5
Output: 15
Explanation: A ship capacity of 15 is the minimum to ship all the packages in 5 days like this:
1st day: 1, 2, 3, 4, 5
2nd day: 6, 7
3rd day: 8
4th day: 9
5th day: 10

Note that the cargo must be shipped in the order given, so using a ship of capacity 14 and splitting the packages into parts like (2, 3, 4, 5), (1, 6, 7), (8), (9), (10) is not allowed.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: weights = [3,2,2,4,1,4], days = 3
Output: 6
Explanation: A ship capacity of 6 is the minimum to ship all the packages in 3 days like this:
1st day: 3, 2
2nd day: 2, 4
3rd day: 1, 4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 3:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: weights = [1,2,3,1,1], days = 4
Output: 3
Explanation:
1st day: 1
2nd day: 2
3rd day: 3
4th day: 1, 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Constraints:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= days &amp;lt;= weights.length &amp;lt;= 5 * 104&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= weights[i] &amp;lt;= 500&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def shipWithinDays(self, weights: List[int], days: int) -&amp;gt; int:
        # l: must be at least the heaviest package
        # r: the sum of all packages (shipping everything in 1 day)
        l, r = max(weights), sum(weights)
        
        while l &amp;lt;= r:
            mid = (l + r) // 2
            
            # Greedy check: how many days are needed with capacity &apos;mid&apos;?
            need = 1
            cur = 0
            for w in weights:
                if cur + w &amp;lt;= mid:
                    cur += w
                else:
                    need += 1
                    cur = w # Start new day with the current package
            
            if need &amp;lt;= days:
                # Valid capacity, try to find a smaller one
                r = mid - 1
            else:
                # Capacity too small, need more power
                l = mid + 1
        return l
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;a href=&quot;https://leetcode.com/problems/heaters/&quot;&gt;475. Heaters&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Winter is coming! During the contest, your first job is to design a standard heater with a fixed warm radius to warm all the houses.&lt;/p&gt;
&lt;p&gt;Every house can be warmed, as long as the house is within the heater&apos;s warm radius range.&lt;/p&gt;
&lt;p&gt;Given the positions of &lt;code&gt;houses&lt;/code&gt; and &lt;code&gt;heaters&lt;/code&gt; on a horizontal line, return &lt;em&gt;the minimum radius standard of heaters so that those heaters could cover all houses.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Notice&lt;/strong&gt; that all the &lt;code&gt;heaters&lt;/code&gt; follow your radius standard, and the warm radius will be the same.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: houses = [1,2,3], heaters = [2]
Output: 1
Explanation: The only heater was placed in the position 2, and if we use the radius 1 standard, then all the houses can be warmed.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: houses = [1,2,3,4], heaters = [1,4]
Output: 1
Explanation: The two heaters were placed at positions 1 and 4. We need to use a radius 1 standard, then all the houses can be warmed.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 3:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: houses = [1,5], heaters = [2]
Output: 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Constraints:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= houses.length, heaters.length &amp;lt;= 3 * 104&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= houses[i], heaters[i] &amp;lt;= 109&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;# Strategy: For each house, find the nearest heaters on both the left and right sides.
# The minimum radius required for a house is the distance to its closest heater.
# The global answer is the maximum of these minimum distances.
class Solution:
    def findRadius(self, houses: List[int], heaters: List[int]) -&amp;gt; int:
        ans = 0
        houses.sort()
        heaters.sort()
        n = len(heaters)
        
        for x in houses:
            # Binary Search for the first heater &amp;gt;= house (Lower Bound)
            i = bisect.bisect_left(heaters, x)
            
            # Distance to the nearest heater on the left (largest heater &amp;lt;= x)
            # If i == 0, no heater exists on the left
            ld = x - heaters[i - 1] if i &amp;gt; 0 else float(&apos;inf&apos;)
            
            # Distance to the nearest heater on the right (smallest heater &amp;gt;= x)
            # If i == n, no heater exists on the right
            rd = heaters[i] - x if i &amp;lt; n else float(&apos;inf&apos;)
            
            # The house only needs to be covered by the CLOSER of the two
            # Update global max radius to ensure this house (and all others) are covered
            ans = max(ans, min(ld, rd))
            
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;a href=&quot;https://leetcode.com/problems/koko-eating-bananas/&quot;&gt;875. Koko Eating Bananas&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Koko loves to eat bananas. There are &lt;code&gt;n&lt;/code&gt; piles(痔疮，堆) of bananas, the &lt;code&gt;ith&lt;/code&gt; pile has &lt;code&gt;piles[i]&lt;/code&gt; bananas. The guards have gone and will come back in &lt;code&gt;h&lt;/code&gt; hours.&lt;/p&gt;
&lt;p&gt;Koko can decide her bananas-per-hour eating speed of &lt;code&gt;k&lt;/code&gt;. Each hour, she chooses some pile of bananas and eats &lt;code&gt;k&lt;/code&gt; bananas from that pile. If the pile has less than &lt;code&gt;k&lt;/code&gt; bananas, she eats all of them instead and will not eat any more bananas during this hour.&lt;/p&gt;
&lt;p&gt;Koko likes to eat slowly but still wants to finish eating all the bananas before the guards return.&lt;/p&gt;
&lt;p&gt;Return &lt;em&gt;the minimum integer&lt;/em&gt; &lt;code&gt;k&lt;/code&gt; &lt;em&gt;such that she can eat all the bananas within&lt;/em&gt; &lt;code&gt;h&lt;/code&gt; &lt;em&gt;hours&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: piles = [3,6,7,11], h = 8
Output: 4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: piles = [30,11,23,4,20], h = 5
Output: 30
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 3:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: piles = [30,11,23,4,20], h = 6
Output: 23 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Constraints:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= piles.length &amp;lt;= 104&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;piles.length &amp;lt;= h &amp;lt;= 109&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= piles[i] &amp;lt;= 109&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def minEatingSpeed(self, piles: List[int], h: int) -&amp;gt; int:
        # l: Smallest possible speed (1 banana/hr)
        # r: A &quot;guaranteed feasible&quot; upper bound (sum of all bananas)
        l = 1
        r = sum(piles) # Note: max(piles) is a tighter, more efficient bound
        
        while l &amp;lt;= r:
            m = (l + r) // 2
            
            # check(m): Calculate total hours needed at speed &apos;m&apos;
            # (x + m - 1) // m is the integer version of math.ceil(x / m)
            hours_needed = sum((x + m - 1) // m for x in piles)
            
            if hours_needed &amp;lt;= h:
                # current speed &apos;m&apos; is feasible (True), try a smaller speed
                r = m - 1
            else:
                # current speed &apos;m&apos; is too slow (False), must increase speed
                l = m + 1
                
        # After the loop, l is the first speed that makes check(m) True
        return l
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Find the most&lt;/h2&gt;
&lt;h3&gt;&lt;a href=&quot;https://leetcode.com/problems/h-index-ii/&quot;&gt;275. H-Index II&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Given an array of integers &lt;code&gt;citations&lt;/code&gt; where &lt;code&gt;citations[i]&lt;/code&gt; is the number of citations a researcher received for their &lt;code&gt;ith&lt;/code&gt; paper and &lt;code&gt;citations&lt;/code&gt; is sorted in &lt;strong&gt;non-descending order&lt;/strong&gt;, return &lt;em&gt;the researcher&apos;s h-index&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;According to the &lt;a href=&quot;https://en.wikipedia.org/wiki/H-index&quot;&gt;definition of h-index on Wikipedia&lt;/a&gt;: The h-index is defined as the maximum value of &lt;code&gt;h&lt;/code&gt; such that the given researcher has published at least &lt;code&gt;h&lt;/code&gt; papers that have each been cited at least &lt;code&gt;h&lt;/code&gt; times.&lt;/p&gt;
&lt;p&gt;You must write an algorithm that runs in logarithmic time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: citations = [0,1,3,5,6]
Output: 3
Explanation: [0,1,3,5,6] means the researcher has 5 papers in total and each of them had received 0, 1, 3, 5, 6 citations respectively.
Since the researcher has 3 papers with at least 3 citations each and the remaining two with no more than 3 citations each, their h-index is 3.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: citations = [1,2,100]
Output: 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Constraints:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;n == citations.length&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= n &amp;lt;= 105&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0 &amp;lt;= citations[i] &amp;lt;= 1000&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;citations&lt;/code&gt; is sorted in &lt;strong&gt;ascending order&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def hIndex(self, citations: List[int]) -&amp;gt; int:
        n = len(citations)
        # Search Range: 0 to n (Max possible H-index is the number of papers)
        l, r = 0, n

        # Pattern: T T T...F F F (Looking for the LAST True)
        while l &amp;lt;= r:
            h = (l + r) // 2
            
            # check(h): Are there at least &apos;h&apos; papers with &amp;gt;= &apos;h&apos; citations?
            # Since sorted, citations[n-h] is the h-th largest value.
            if h == 0 or citations[n - h] &amp;gt;= h:
                # This &apos;h&apos; works! Try a larger value to the right.
                l = h + 1      
            else:
                # Too many papers requested or citations too low. Search left.
                r = h - 1      

        # Per the &quot;Last True&quot; template, &apos;r&apos; is the answer after l &amp;gt; r
        return r
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;1) The Strategy: &quot;Find the Largest Valid H&quot;&lt;/h4&gt;
&lt;p&gt;The H-Index definition states: &quot;A scientist has index $h$ if $h$ of their $n$ papers have &lt;strong&gt;at least&lt;/strong&gt; $h$ citations.&quot; Since the &lt;code&gt;citations&lt;/code&gt; array is sorted, the papers with the most citations are at the end of the array.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The Condition&lt;/strong&gt;: If we pick a value &lt;code&gt;h&lt;/code&gt;, the paper at index &lt;code&gt;n - h&lt;/code&gt; is the &quot;weakest&quot; paper in our set of $h$ papers. If &lt;code&gt;citations[n - h] &amp;gt;= h&lt;/code&gt;, then all $h$ papers have at least $h$ citations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Monotonicity&lt;/strong&gt;: If a researcher satisfies the condition for $h=5$, they might satisfy it for $h=6$. If they fail for $h=5$, they will definitely fail for $h=6$.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pattern&lt;/strong&gt;: &lt;code&gt;[T, T, T, T, F, F]&lt;/code&gt; — We want the &lt;strong&gt;Last True&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;[&lt;a href=&quot;https://www.cnblogs.com/grandyang/p/8021421.html&quot;&gt;LeetCode] 644. Maximum Average Subarray II &lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Given an array consisting of &lt;code&gt;n&lt;/code&gt; integers, find the contiguous subarray whose length is greater than or equal to &lt;code&gt;k&lt;/code&gt; that has the maximum average value. And you need to output the maximum average value.&lt;/p&gt;
&lt;p&gt;Example 1:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: [1,12,-5,-6,50,3], k = 4
Output: 12.75
Explanation:
when length is 5, maximum average value is 10.8,
when length is 6, maximum average value is 9.16667.
Thus return 12.75.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;1 &amp;lt;= &lt;code&gt;k&lt;/code&gt; &amp;lt;= &lt;code&gt;n&lt;/code&gt; &amp;lt;= 10,000.&lt;/li&gt;
&lt;li&gt;Elements of the given array will be in range [-10,000, 10,000].&lt;/li&gt;
&lt;li&gt;The answer with the calculation error less than 10-5 will be accepted.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def findMaxAverage(self, nums: List[int], k: int) -&amp;gt; float:
        n = len(nums)

        def check(mid: float) -&amp;gt; bool:
            # Transform: sum(nums[i] - mid) &amp;gt;= 0
            pre = 0.0      # sum(b[0..i])
            pre_k = 0.0    # sum(b[0..i-k])
            min_pre = 0.0  # min(pre[0...i-k])

            # Initial window of size k
            for i in range(k):
                pre += nums[i] - mid
            if pre &amp;gt;= 0: return True

            # Sliding window with variable start
            for i in range(k, n):
                pre += nums[i] - mid
                pre_k += nums[i - k] - mid
                # Greedy: keep track of the smallest prefix sum seen so far
                # that allows for a subarray length &amp;gt;= k
                min_pre = min(min_pre, pre_k)
                
                if pre - min_pre &amp;gt;= 0:
                    return True
            return False

        # Range: Between the smallest and largest possible numbers
        l, r = min(nums), max(nums)
        eps = 1e-5 # Precision threshold

        # Binary search for the maximum feasible average
        while r - l &amp;gt; eps:
            mid = (l + r) / 2
            if check(mid):
                l = mid  # mid is feasible, try to increase it
            else:
                r = mid  # mid is too high, decrease it
        
        return l
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;1) How the &lt;code&gt;check&lt;/code&gt; Function Works: Prefix Sums &amp;amp; Greedy Strategy&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;check&lt;/code&gt; function validates the condition in &lt;strong&gt;$O(n)$&lt;/strong&gt; time using a combination of prefix sums and a greedy sliding window:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pre-processing&lt;/strong&gt;: Subtract &lt;code&gt;mid&lt;/code&gt; from every element in the array ($nums[i] - mid$). This transforms the problem from finding an average to finding a &lt;strong&gt;subarray sum $\ge 0$&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sliding Window&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;Maintain the current prefix sum &lt;strong&gt;&lt;code&gt;pre&lt;/code&gt;&lt;/strong&gt; (representing the sum from $0$ to $i$).&lt;/li&gt;
&lt;li&gt;Maintain &lt;strong&gt;&lt;code&gt;min_pre&lt;/code&gt;&lt;/strong&gt; (representing the minimum prefix sum encountered between index $0$ and $i-k$).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Greedy Validation&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;We are looking for a pair $(i, j)$ such that  pre[i] - pre[j] $\ge 0$(where $i - j \ge k$)  .&lt;/li&gt;
&lt;li&gt;To maximize this difference, we simply subtract the &lt;strong&gt;smallest prefix sum (&lt;code&gt;min_pre&lt;/code&gt;)&lt;/strong&gt; that occurred at least $k$ positions ago.&lt;/li&gt;
&lt;li&gt;If &lt;strong&gt;pre - min_pre $\ge 0$&lt;/strong&gt;, we have successfully found a subarray of length $\ge k$ that satisfies the condition.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;a href=&quot;https://leetcode.com/problems/maximum-candies-allocated-to-k-children/&quot;&gt;2226. Maximum Candies Allocated to K Children&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You are given a &lt;strong&gt;0-indexed&lt;/strong&gt; integer array &lt;code&gt;candies&lt;/code&gt;. Each element in the array denotes a pile of candies of size &lt;code&gt;candies[i]&lt;/code&gt;. You can divide each pile into any number of &lt;strong&gt;sub piles&lt;/strong&gt;, but you &lt;strong&gt;cannot&lt;/strong&gt; merge two piles together.&lt;/p&gt;
&lt;p&gt;You are also given an integer &lt;code&gt;k&lt;/code&gt;. You should allocate piles of candies to &lt;code&gt;k&lt;/code&gt; children such that each child gets the &lt;strong&gt;same&lt;/strong&gt; number of candies. Each child can be allocated candies from &lt;strong&gt;only one&lt;/strong&gt; pile of candies and some piles of candies may go unused.&lt;/p&gt;
&lt;p&gt;Return &lt;em&gt;the &lt;strong&gt;maximum number of candies&lt;/strong&gt; each child can get.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: candies = [5,8,6], k = 3
Output: 5
Explanation: We can divide candies[1] into 2 piles of size 5 and 3, and candies[2] into 2 piles of size 5 and 1. We now have five piles of candies of sizes 5, 5, 3, 5, and 1. We can allocate the 3 piles of size 5 to 3 children. It can be proven that each child cannot receive more than 5 candies.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: candies = [2,5], k = 11
Output: 0
Explanation: There are 11 children but only 7 candies in total, so it is impossible to ensure each child receives at least one candy. Thus, each child gets no candy and the answer is 0.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Constraints:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= candies.length &amp;lt;= 105&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= candies[i] &amp;lt;= 107&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= k &amp;lt;= 1012&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;# In binary search for the **maximum** value (`TTTTFFFF`), we move the left boundary (`l = m + 1`) when the condition is met to seek a larger valid answer, ultimately returning **`r`** as the last &quot;True&quot; position.
class Solution:
    def maximumCandies(self, candies: List[int], k: int) -&amp;gt; int:
        def ok(m):
            cnt = 0
            for x in candies:
                cnt += x // m
                if cnt &amp;gt;= k:
                    return True
            return False
        l = 1
        r = max(candies)
        while l &amp;lt;= r:
            m = (l + r) // 2
            if ok(m):
                l = m + 1
            else:
                r = m - 1
        return r

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Binary Search on an Indirect Value&lt;/h2&gt;
&lt;h3&gt;&lt;a href=&quot;https://leetcode.com/problems/kth-largest-element-in-an-array/&quot;&gt;215. Kth Largest Element in an Array&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Given an integer array &lt;code&gt;nums&lt;/code&gt; and an integer &lt;code&gt;k&lt;/code&gt;, return &lt;em&gt;the&lt;/em&gt; &lt;code&gt;kth&lt;/code&gt; &lt;em&gt;largest element in the array&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Note that it is the &lt;code&gt;kth&lt;/code&gt; largest element in the sorted order, not the &lt;code&gt;kth&lt;/code&gt; distinct element.&lt;/p&gt;
&lt;p&gt;Can you solve it without sorting?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: nums = [3,2,1,5,6,4], k = 2
Output: 5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: nums = [3,2,3,1,2,4,5,5,6], k = 4
Output: 4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Constraints:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= k &amp;lt;= nums.length &amp;lt;= 105&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-104 &amp;lt;= nums[i] &amp;lt;= 104&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def findKthLargest(self, nums: List[int], k: int) -&amp;gt; int:
        l = min(nums)
        r = max(nums)
        while l &amp;lt;= r:
            m = (l + r) // 2
            cnt = sum(x &amp;gt;= m for x in nums)
            if cnt &amp;gt;= k:
                l = m + 1
            else:
                r = m - 1
        return r
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;// std::numeric_limits&amp;lt;int&amp;gt;::min()
// std::numeric_limits&amp;lt;int&amp;gt;::max()
class Solution {
public:
    int findKthLargest(vector&amp;lt;int&amp;gt;&amp;amp; nums, int k) {
        auto [mn, mx] = minmax_element(nums.begin(), nums.end());
        int l = *mn;
        int r = *mx;
        auto checkKLargest = [&amp;amp;](int m) {
            int cnt = 0;
            for (int x : nums) {
                if (x &amp;gt;= m) cnt++;
            }
            return cnt &amp;gt;= k; 
        };
        while (l &amp;lt;= r) {
            int m = l + (r - l) / 2;
            if (checkKLargest(m)) {
                l = m + 1;
            } else {
                r = m - 1;
            }
        }
        return r;
    }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;[&lt;a href=&quot;https://www.cnblogs.com/grandyang/p/9937770.html&quot;&gt;LeetCode] Search in a Sorted Array of Unknown Size &lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Given an integer array sorted in ascending order, write a function to search &lt;code&gt;target&lt;/code&gt; in &lt;code&gt;nums&lt;/code&gt;. If &lt;code&gt;target&lt;/code&gt; exists, then return its index, otherwise return &lt;code&gt;-1&lt;/code&gt;. However, the array size is unknown to you. You may only access the array using an &lt;code&gt;ArrayReader&lt;/code&gt; interface, where &lt;code&gt;ArrayReader.get(k)&lt;/code&gt; returns the element of the array at index &lt;code&gt;k&lt;/code&gt; (0-indexed).&lt;/p&gt;
&lt;p&gt;You may assume all integers in the array are less than &lt;code&gt;10000&lt;/code&gt;, and if you access the array out of bounds, &lt;code&gt;ArrayReader.get&lt;/code&gt; will return &lt;code&gt;2147483647&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Example 1:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;array
target
nums
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example 2:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;array
target
nums
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You may assume that all elements in the array are unique.&lt;/li&gt;
&lt;li&gt;The value of each element in the array will be in the range &lt;code&gt;[-9999, 9999]&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;# Exponential expansion is a method used to find a valid search boundary when the size of a sorted array is unknown or unbounded.
def doubling_search(nums, target):
    l = 0
    r = 1
    while True:
        try:
            if nums[r] &amp;lt; target:
                l = r
                r *= 2
            else:
                break
        except IndexError:
            break

    while l &amp;lt;= r:
        m = (l + r) // 2
        try:
            val = nums[m]
        except IndexError:
            r = m - 1
            continue

        if val &amp;gt; target:
            r = m - 1
        elif val == target:
            return m
        else:
            l = m + 1
    return -1


&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;The Kth Smallest/Biggest&lt;/h2&gt;
&lt;p&gt;Nearly everyone has used the &lt;a href=&quot;https://en.wikipedia.org/wiki/Multiplication_table&quot;&gt;Multiplication Table&lt;/a&gt;. The multiplication table of size &lt;code&gt;m x n&lt;/code&gt; is an integer matrix &lt;code&gt;mat&lt;/code&gt; where &lt;code&gt;mat[i][j] == i * j&lt;/code&gt; (&lt;strong&gt;1-indexed&lt;/strong&gt;).&lt;/p&gt;
&lt;p&gt;Given three integers &lt;code&gt;m&lt;/code&gt;, &lt;code&gt;n&lt;/code&gt;, and &lt;code&gt;k&lt;/code&gt;, return &lt;em&gt;the&lt;/em&gt; &lt;code&gt;kth&lt;/code&gt; &lt;em&gt;smallest element in the&lt;/em&gt; &lt;code&gt;m x n&lt;/code&gt; &lt;em&gt;multiplication table&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-c69d652d2a0747fab9aad1fab48ff742.r2.dev/images/multtable1-grid&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: m = 3, n = 3, k = 5
Output: 3
Explanation: The 5th smallest number is 3.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-c69d652d2a0747fab9aad1fab48ff742.r2.dev/images/multtable2-grid&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: m = 2, n = 3, k = 6
Output: 6
Explanation: The 6th smallest number is 6.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Constraints:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= m, n &amp;lt;= 3 * 104&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= k &amp;lt;= m * n&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def findKthNumber(self, m: int, n: int, k: int) -&amp;gt; int:
        def count(x):
            cnt = 0
            for i in range(1, m + 1):
                cnt += min(x // i, n)
            return cnt

        l = 0
        r = m * n
        while l &amp;lt;= r:
            mid = (l + r) // 2
            if count(mid) &amp;gt;= k:
                r = mid - 1
            else:
                l = mid + 1
        return l
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
public:
    int findKthNumber(int m, int n, int k) {
        int l = 0;
        int r = m * n;
        
        // Capture external variables by reference using &amp;amp;.
        auto count = [&amp;amp;](int x) {
            int cnt = 0;
            for (int i = 1; i &amp;lt;= m; i++) {
                cnt += min(x / i, n);
            }
            return cnt;
        }; // &amp;lt;--- IMPORTANT: Do not forget the semicolon after the lambda definition!

        while (l &amp;lt;= r) {
            int mid = l + (r - l) / 2;
            if (count(mid) &amp;gt;= k) {
                r = mid - 1;
            } else {
                l = mid + 1;
            }
        }
        return l;
    }
};
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Algorithm Study Plan</title><link>https://lxy-alexander.github.io/blog/posts/algorithm/algorithm-study-plan/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/algorithm/algorithm-study-plan/</guid><description>Algorithm</description><pubDate>Tue, 20 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Plan&lt;/h1&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-c69d652d2a0747fab9aad1fab48ff742.r2.dev/images/image-20260206003044638&quot; alt=&quot;image-20260206003044638&quot; /&gt;&lt;/p&gt;
&lt;p&gt;按照专题刷题，而不是随机刷题。同一个专题，一个套路可以解决多个题目，刷题效率高。此外，这能让你从不同的角度去观察、思考同一个算法，从而深刻地理解算法的本质。
螺旋上升式学习：先完成难度分 ≤1700 的题目。把各个题单、各个知识点的基础题刷一遍，再刷更难的题目。难度分低的题目一般只会考察一个知识点，而难度分高的题目会同时考察多个知识点。&lt;/p&gt;
&lt;p&gt;https://leetcode.cn/discuss/post/3141566/ru-he-ke-xue-shua-ti-by-endlesscheng-q3yd/&lt;/p&gt;
&lt;h1&gt;Degree of Completion&lt;/h1&gt;
&lt;h2&gt;Sliding Window: 2026.1.19 - 2026.1.23&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;At most k distinct elements (by using &lt;code&gt;len(cnt)&lt;/code&gt; to check if the window is valid)&lt;/li&gt;
&lt;li&gt;At most k occurrences of each element (by using &lt;code&gt;cnt[s[r]] &amp;gt; k&lt;/code&gt; to check if the window is valid.  If  adding s[r] makes the window not valid, we need shrink the window to make it valid again)&lt;/li&gt;
&lt;li&gt;Exactly K Distinct Elements = At most(K) - At most(K-1)&lt;/li&gt;
&lt;li&gt;Some variant questions:
&lt;ul&gt;
&lt;li&gt;Including exactly 5 vowels(other character are not vowels) need to divide and conquer firstly. and then do the at most&lt;/li&gt;
&lt;li&gt;exactly&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Sliding Window</title><link>https://lxy-alexander.github.io/blog/posts/algorithm/sliding-window/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/algorithm/sliding-window/</guid><description>Sliding Window</description><pubDate>Tue, 20 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Sliding Window&lt;/strong&gt; is a way to look at a small part of data and move it forward one step at a time, instead of starting over each time.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;Fixed-length sliding window&lt;/h1&gt;
&lt;h3&gt;&lt;a href=&quot;https://leetcode.com/problems/maximum-number-of-vowels-in-a-substring-of-given-length/&quot;&gt;1456. Maximum Number of Vowels in a Substring of Given Length&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Given a string &lt;code&gt;s&lt;/code&gt; and an integer &lt;code&gt;k&lt;/code&gt;, return &lt;em&gt;the maximum number of vowel letters in any substring of&lt;/em&gt; &lt;code&gt;s&lt;/code&gt; &lt;em&gt;with length&lt;/em&gt; &lt;code&gt;k&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Vowel letters&lt;/strong&gt; in English are &lt;code&gt;&apos;a&apos;&lt;/code&gt;, &lt;code&gt;&apos;e&apos;&lt;/code&gt;, &lt;code&gt;&apos;i&apos;&lt;/code&gt;, &lt;code&gt;&apos;o&apos;&lt;/code&gt;, and &lt;code&gt;&apos;u&apos;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: s = &quot;abciiidef&quot;, k = 3
Output: 3
Explanation: The substring &quot;iii&quot; contains 3 vowel letters.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: s = &quot;aeiou&quot;, k = 2
Output: 2
Explanation: Any substring of length 2 contains 2 vowels.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 3:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: s = &quot;leetcode&quot;, k = 3
Output: 2
Explanation: &quot;lee&quot;, &quot;eet&quot; and &quot;ode&quot; contain 2 vowels.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Constraints:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= s.length &amp;lt;= 105&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;s&lt;/code&gt; consists of lowercase English letters.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= k &amp;lt;= s.length&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
public:
    bool isVowel(char c) {
        return c == &apos;a&apos; || c == &apos;e&apos; || c == &apos;i&apos; || c == &apos;o&apos; || c == &apos;u&apos;;
    }
    
    int maxVowels(string s, int k) {
        // const std::set&amp;lt;char&amp;gt; vowelSet{&apos;a&apos;, &apos;e&apos;, &apos;i&apos;, &apos;o&apos;, &apos;u&apos;};
        int n = s.size();
        int vowelCnt = 0;
        int ans = 0;
        for (int i = 0; i &amp;lt; n; i++) {
            if (isVowel(s[i])) vowelCnt++;
            if (i &amp;lt; k - 1) continue;
            ans = std::max(ans, vowelCnt);
            if (isVowel(s[i - k + 1])) vowelCnt--;
        }
        return ans;
    }
    
};
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def maxVowels(self, s: str, k: int) -&amp;gt; int:
        ans = 0
        vowel_cnt = 0

        for i, c in enumerate(s):
            if c in &quot;aieou&quot;:
                vowel_cnt += 1
            if i &amp;lt; k - 1:
                continue
            ans = max(ans, vowel_cnt)
            if s[i - k + 1] in &quot;aieou&quot;:
                vowel_cnt -= 1
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;a href=&quot;https://leetcode.com/problems/maximum-average-subarray-i/&quot;&gt;643. Maximum Average Subarray I&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You are given an integer array &lt;code&gt;nums&lt;/code&gt; consisting of &lt;code&gt;n&lt;/code&gt; elements, and an integer &lt;code&gt;k&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Find a contiguous subarray whose &lt;strong&gt;length is equal to&lt;/strong&gt; &lt;code&gt;k&lt;/code&gt; that has the maximum average value and return &lt;em&gt;this value&lt;/em&gt;. Any answer with a calculation error less than &lt;code&gt;10-5&lt;/code&gt; will be accepted.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: nums = [1,12,-5,-6,50,3], k = 4
Output: 12.75000
Explanation: Maximum average is (12 - 5 - 6 + 50) / 4 = 51 / 4 = 12.75
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: nums = [5], k = 1
Output: 5.00000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Constraints:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;n == nums.length&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= k &amp;lt;= n &amp;lt;= 105&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-104 &amp;lt;= nums[i] &amp;lt;= 104&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
public:
    double findMaxAverage(vector&amp;lt;int&amp;gt;&amp;amp; nums, int k) {
        double ans = -1e18;
        double sum = 0;
        int n = nums.size();
        for (int i = 0; i &amp;lt; n; i++) {
            sum += nums[i];
            if (i &amp;lt; k - 1) continue;
            ans = std::max(ans, sum / k);
            sum -= nums[i - k + 1];
        }
        return ans;
    }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def findMaxAverage(self, nums: List[int], k: int) -&amp;gt; float:
        ans = float(&apos;-inf&apos;)
        sum = 0
        for i in range(len(nums)):
            sum += nums[i]
            if i &amp;lt; k - 1:
                continue
            ans = max(ans, sum / k)
            sum -= nums[i - k + 1]
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;a href=&quot;https://leetcode.com/problems/maximum-sum-of-distinct-subarrays-with-length-k/&quot;&gt;2461. Maximum Sum of Distinct Subarrays With Length K&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You are given an integer array &lt;code&gt;nums&lt;/code&gt; and an integer &lt;code&gt;k&lt;/code&gt;. Find the maximum subarray sum of all the subarrays of &lt;code&gt;nums&lt;/code&gt; that meet the following conditions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The length of the subarray is &lt;code&gt;k&lt;/code&gt;, and&lt;/li&gt;
&lt;li&gt;All the elements of the subarray are &lt;strong&gt;distinct&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Return &lt;em&gt;the maximum subarray sum of all the subarrays that meet the conditions&lt;/em&gt;*.* If no subarray meets the conditions, return &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;A &lt;strong&gt;subarray&lt;/strong&gt; is a contiguous non-empty sequence of elements within an array.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: nums = [1,5,4,2,9,9,9], k = 3
Output: 15
Explanation: The subarrays of nums with length 3 are:
- [1,5,4] which meets the requirements and has a sum of 10.
- [5,4,2] which meets the requirements and has a sum of 11.
- [4,2,9] which meets the requirements and has a sum of 15.
- [2,9,9] which does not meet the requirements because the element 9 is repeated.
- [9,9,9] which does not meet the requirements because the element 9 is repeated.
We return 15 because it is the maximum subarray sum of all the subarrays that meet the conditions
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: nums = [4,4,4], k = 3
Output: 0
Explanation: The subarrays of nums with length 3 are:
- [4,4,4] which does not meet the requirements because the element 4 is repeated.
We return 0 because no subarrays meet the conditions.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Constraints:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= k &amp;lt;= nums.length &amp;lt;= 105&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= nums[i] &amp;lt;= 105&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
public:
    long long maximumSubarraySum(vector&amp;lt;int&amp;gt;&amp;amp; nums, int k) {
        long long ans = 0;
        long long sum = 0;
        std::unordered_map&amp;lt;int, int&amp;gt; map; // stores the frequency of elements in the window
        int left = 0;
        int n = nums.size();
        for (int right = 0; right &amp;lt; n; right++) {
            int x = nums[right];
            map[x]++;
            sum += x;
            left = right - k + 1;
            if (left &amp;lt; 0) continue;
            if (map.size() == k) ans = std::max(ans, sum); // is used to check whether duplicates exist
            int o = nums[left];
            sum -= o;
            map[o]--;
            if (map[o] == 0) map.erase(o);
        }
        return ans;
    }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def maximumSubarraySum(self, nums: List[int], k: int) -&amp;gt; int:
        ans, sum, left = 0, 0, 0
        d = defaultdict(int)

        for right, x in enumerate(nums):
            d[x] += 1
            sum += x
            left = right - k + 1
            if left &amp;lt; 0:
                continue
            if len(d) == k: # d stores the frequency of elements in the window
                ans = max(ans, sum)
            
            out = nums[left]
            sum -= out
            d[out] -= 1
            if d[out] == 0:
                del d[out] 
        return ans         
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;a href=&quot;https://leetcode.com/problems/substring-with-concatenation-of-all-words/&quot;&gt;30. Substring with Concatenation of All Words&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You are given a string &lt;code&gt;s&lt;/code&gt; and an array of strings &lt;code&gt;words&lt;/code&gt;. All the strings of &lt;code&gt;words&lt;/code&gt; are of &lt;strong&gt;the same length&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;A &lt;strong&gt;concatenated string&lt;/strong&gt; is a string that exactly contains all the strings of any permutation of &lt;code&gt;words&lt;/code&gt; concatenated.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For example, if &lt;code&gt;words = [&quot;ab&quot;,&quot;cd&quot;,&quot;ef&quot;]&lt;/code&gt;, then &lt;code&gt;&quot;abcdef&quot;&lt;/code&gt;, &lt;code&gt;&quot;abefcd&quot;&lt;/code&gt;, &lt;code&gt;&quot;cdabef&quot;&lt;/code&gt;, &lt;code&gt;&quot;cdefab&quot;&lt;/code&gt;, &lt;code&gt;&quot;efabcd&quot;&lt;/code&gt;, and &lt;code&gt;&quot;efcdab&quot;&lt;/code&gt; are all concatenated strings. &lt;code&gt;&quot;acdbef&quot;&lt;/code&gt; is not a concatenated string because it is not the concatenation of any permutation of &lt;code&gt;words&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Return an array of &lt;em&gt;the starting indices&lt;/em&gt; of all the concatenated substrings in &lt;code&gt;s&lt;/code&gt;. You can return the answer in &lt;strong&gt;any order&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Input:&lt;/strong&gt; s = &quot;barfoothefoobarman&quot;, words = [&quot;foo&quot;,&quot;bar&quot;]&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt; [0,9]&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The substring starting at 0 is &lt;code&gt;&quot;barfoo&quot;&lt;/code&gt;. It is the concatenation of &lt;code&gt;[&quot;bar&quot;,&quot;foo&quot;]&lt;/code&gt; which is a permutation of &lt;code&gt;words&lt;/code&gt;.
The substring starting at 9 is &lt;code&gt;&quot;foobar&quot;&lt;/code&gt;. It is the concatenation of &lt;code&gt;[&quot;foo&quot;,&quot;bar&quot;]&lt;/code&gt; which is a permutation of &lt;code&gt;words&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Input:&lt;/strong&gt; s = &quot;wordgoodgoodgoodbestword&quot;, words = [&quot;word&quot;,&quot;good&quot;,&quot;best&quot;,&quot;word&quot;]&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt; []&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;There is no concatenated substring.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example 3:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Input:&lt;/strong&gt; s = &quot;barfoofoobarthefoobarman&quot;, words = [&quot;bar&quot;,&quot;foo&quot;,&quot;the&quot;]&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt; [6,9,12]&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The substring starting at 6 is &lt;code&gt;&quot;foobarthe&quot;&lt;/code&gt;. It is the concatenation of &lt;code&gt;[&quot;foo&quot;,&quot;bar&quot;,&quot;the&quot;]&lt;/code&gt;.
The substring starting at 9 is &lt;code&gt;&quot;barthefoo&quot;&lt;/code&gt;. It is the concatenation of &lt;code&gt;[&quot;bar&quot;,&quot;the&quot;,&quot;foo&quot;]&lt;/code&gt;.
The substring starting at 12 is &lt;code&gt;&quot;thefoobar&quot;&lt;/code&gt;. It is the concatenation of &lt;code&gt;[&quot;the&quot;,&quot;foo&quot;,&quot;bar&quot;]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Constraints:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= s.length &amp;lt;= 104&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= words.length &amp;lt;= 5000&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= words[i].length &amp;lt;= 30&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;s&lt;/code&gt; and &lt;code&gt;words[i]&lt;/code&gt; consist of lowercase English letters.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;// ❌ 不在 → [l, r) → 不加 1     ✅ 在 → [l, r] → 加 1&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
public:
    vector&amp;lt;int&amp;gt; findSubstring(string s, vector&amp;lt;string&amp;gt;&amp;amp; words) {
        vector&amp;lt;int&amp;gt; ans;
        int n = s.size();
        int m = words.size();
        int wordLen = words[0].size();
        int winLen = m * wordLen;

        map&amp;lt;string, int&amp;gt; map1;
        for (const string&amp;amp; word : words) {
            map1[word]++;
        }

      
        for (int i = 0; i &amp;lt; wordLen; i++) {
            map&amp;lt;string, int&amp;gt; map2;

            for (int j = i; j + wordLen &amp;lt;= n; j += wordLen) {
                // 1. 进窗口
                string w = s.substr(j, wordLen);
                map2[w]++;

                // 2. 窗口还没满
                if (j + wordLen - i &amp;lt; winLen) continue;

                // 3. 判断
                if (map1 == map2)
                    ans.push_back(j + wordLen - winLen);

                // 4. 出窗口
                string out = s.substr(j + wordLen - winLen, wordLen);
                map2[out]--;
                if (map2[out] == 0) map2.erase(out);
            }
        }

        return ans;
    }
};

&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def findSubstring(self, s: str, words: List[str]) -&amp;gt; List[int]:
        ans = []
        n = len(s)
        word_len = len(words[0])
        win_len = len(words) * word_len
        d1 = defaultdict(int)

        for w in words:
            d1[w] += 1

        for i in range(word_len):
            d2 = defaultdict(int)

            for j in range(i + word_len, n + 1, word_len):
                w = s[j-word_len:j]
                d2[w] += 1
                
                if j - i &amp;lt; win_len:
                    continue

                if d1 == d2:
                    ans.append(j - win_len)

                out = s[j - win_len : j - win_len + word_len]
                d2[out] -= 1
                if d2[out] == 0:
                    del d2[out]
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;a href=&quot;https://leetcode.com/problems/max-consecutive-ones-iii/&quot;&gt;1004. Max Consecutive Ones III&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Given a binary array &lt;code&gt;nums&lt;/code&gt; and an integer &lt;code&gt;k&lt;/code&gt;, return &lt;em&gt;the maximum number of consecutive&lt;/em&gt; &lt;code&gt;1&lt;/code&gt;&lt;em&gt;&apos;s in the array if you can flip at most&lt;/em&gt; &lt;code&gt;k&lt;/code&gt; &lt;code&gt;0&lt;/code&gt;&apos;s.↳&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: nums = [1,1,1,0,0,0,1,1,1,1,0], k = 2
Output: 6
Explanation: [1,1,1,0,0,1,1,1,1,1,1]
Bolded numbers were flipped from 0 to 1. The longest subarray is underlined.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: nums = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], k = 3
Output: 10
Explanation: [0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1]
Bolded numbers were flipped from 0 to 1. The longest subarray is underlined.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Constraints:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= nums.length &amp;lt;= 105&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nums[i]&lt;/code&gt; is either &lt;code&gt;0&lt;/code&gt; or &lt;code&gt;1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0 &amp;lt;= k &amp;lt;= nums.length&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def longestOnes(self, nums: List[int], k: int) -&amp;gt; int:
        ans = 0
        l = 0
        cnt = 0
        for r, x in enumerate(nums):
            cnt += int(not x)
            while cnt &amp;gt; k:
                cnt -= int(not nums[l])
                l += 1
            ans = max(ans, r - l + 1)
        return ans
        
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
public:
    int longestOnes(vector&amp;lt;int&amp;gt;&amp;amp; nums, int k) {
        // you can flip at most k 0, it means the window has at most k zero, it can be 0, 1, to k.
        int ans = 0;
        int cnt = 0;
        int l = 0;
        int n = nums.size();
        for (int r = 0; r &amp;lt; n; r++) {
            // accumulate the amount of zero
            cnt += !nums[r];
            while (cnt &amp;gt; k) {
                cnt -= !nums[l];
                l++;
            }
            ans = max(ans, r - l + 1);
        }
        return ans;
    }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Variable-length sliding window&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;Variable-length sliding windows are mainly divided into three categories: finding the longest subarray, finding the shortest subarray, and finding the number of subarrays.&lt;/p&gt;
&lt;p&gt;A sliding window is equivalent to maintaining a queue . Moving the right pointer can be seen as enqueuing , and moving the left pointer can be seen as dequeuing .&lt;/p&gt;
&lt;p&gt;&amp;lt;  ≤  → direct sliding window
==     → at most K − at most (K − 1)
≥      → total − at most (K − 1)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;At Most&lt;/h2&gt;
&lt;p&gt;:::important&lt;/p&gt;
&lt;p&gt;At Most（至多）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;≤ K&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;单调性最好&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;滑动窗口首选&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;“至多”之所以单调性最好，是因为扩张和收缩对合法性的影响方向是完全相反且确定的。&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;at most K distinct&lt;/li&gt;
&lt;li&gt;at most K occurrences&lt;/li&gt;
&lt;li&gt;sum ≤ K&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;:::&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://leetcode.com/problems/consecutive-characters/&quot;&gt;1446. Consecutive Characters&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;strong&gt;power&lt;/strong&gt; of the string is the maximum length of a non-empty substring that contains only one unique character.&lt;/p&gt;
&lt;p&gt;Given a string &lt;code&gt;s&lt;/code&gt;, return &lt;em&gt;the &lt;strong&gt;power&lt;/strong&gt; of&lt;/em&gt; &lt;code&gt;s&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: s = &quot;leetcode&quot;
Output: 2
Explanation: The substring &quot;ee&quot; is of length 2 with the character &apos;e&apos; only.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: s = &quot;abbcccddddeeeeedcba&quot;
Output: 5
Explanation: The substring &quot;eeeee&quot; is of length 5 with the character &apos;e&apos; only.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Constraints:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= s.length &amp;lt;= 500&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;s&lt;/code&gt; consists of only lowercase English letters.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def maxPower(self, s: str) -&amp;gt; int:
        ans = 0
        l = 0
        cnt = defaultdict(int)
        for r, c in enumerate(s):
            cnt[c] += 1
            while len(cnt) &amp;gt; 1:
                cnt[s[l]] -= 1
                if cnt[s[l]] == 0: #因为你的check条件是size，所以当这个map中的某个元素的个数为0，要把它移出，不移除会让这个check条件一直符合，最终会让l一直++，到最后会超过s的长度，导致溢出。
                    del cnt[s[l]]
                l += 1
            ans = max(ans, r - l + 1)
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
public:
    int maxPower(string s) {
        int ans = 0;
        int l = 0;
        int n = s.size();
        map&amp;lt;char, int&amp;gt; cnt;
        for (int r = 0; r &amp;lt; n; r++) {
            cnt[s[r]]++;
            while (cnt.size() &amp;gt; 1) {
                cnt[s[l]]--;
                if (cnt[s[l]] == 0) {
                    cnt.erase(s[l]);
                }
                l++;
            }
            ans = max(ans, r - l + 1);
        }
        return ans;
    }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;a href=&quot;https://leetcode.com/problems/max-consecutive-ones-iii/&quot;&gt;1004. Max Consecutive Ones III&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Given a binary array &lt;code&gt;nums&lt;/code&gt; and an integer &lt;code&gt;k&lt;/code&gt;, return &lt;em&gt;the maximum number of consecutive&lt;/em&gt; &lt;code&gt;1&lt;/code&gt;&lt;em&gt;&apos;s in the array if you can flip at most&lt;/em&gt; &lt;code&gt;k&lt;/code&gt; &lt;code&gt;0&lt;/code&gt;&apos;s.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: nums = [1,1,1,0,0,0,1,1,1,1,0], k = 2
Output: 6
Explanation: [1,1,1,0,0,1,1,1,1,1,1]
Bolded numbers were flipped from 0 to 1. The longest subarray is underlined.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: nums = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], k = 3
Output: 10
Explanation: [0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1]
Bolded numbers were flipped from 0 to 1. The longest subarray is underlined.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Constraints:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= nums.length &amp;lt;= 105&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nums[i]&lt;/code&gt; is either &lt;code&gt;0&lt;/code&gt; or &lt;code&gt;1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0 &amp;lt;= k &amp;lt;= nums.length&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def longestOnes(self, nums: List[int], k: int) -&amp;gt; int:
        ans = 0
        l = 0
        cnt = 0
        for r, x in enumerate(nums):
            cnt += int(not x)
            while cnt &amp;gt; k:
                cnt -= int(not nums[l])
                l += 1
            ans = max(ans, r - l + 1)
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
public:
    int longestOnes(vector&amp;lt;int&amp;gt;&amp;amp; nums, int k) {
        // you can flip at most k 0, it means the window has at most k zero, it can be 0, 1, to k.
        int ans = 0;
        int cnt = 0;
        int l = 0;
        int n = nums.size();
        for (int r = 0; r &amp;lt; n; r++) {
            // accumulate the amount of zero
            cnt += !nums[r];
            while (cnt &amp;gt; k) {
                cnt -= !nums[l];
                l++;
            }
            ans = max(ans, r - l + 1);
        }
        return ans;
    }
};

&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;[&lt;a href=&quot;https://www.cnblogs.com/grandyang/p/5351347.html&quot;&gt;LeetCode] 340. Longest Substring with At Most K Distinct Characters&lt;/a&gt; (at most k distinct elements )&lt;/h3&gt;
&lt;p&gt;Given a string, find the length of the longest substring T that contains at most &lt;em&gt;k&lt;/em&gt; distinct characters.&lt;/p&gt;
&lt;p&gt;Example 1:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: s = &quot;eceba&quot;, k = 2
Output: 3
Explanation: T is &quot;ece&quot; which its length is 3.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example 2:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: s = &quot;aa&quot;, k = 1
Output: 2
Explanation: T is &quot;aa&quot; which its length is 2.
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
public:
    int lengthOfLongestSubstringKDistinct(string s, int k) {
        int ans = 0;
        map&amp;lt;char, int&amp;gt; cnt;
        int l = 0;
        int n = s.size();
        for (int r = 0; r &amp;lt; n; r++) {
            cnt[s[r]]++;
            while (cnt.size() &amp;gt; k) {
                cnt[s[l]]--;
                if (cnt[s[l]] == 0) {
                    cnt.erase(s[l]);
                }
                l++;
            }
            ans = max(ans, r - l + 1);
        }
        return ans;
    }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def lengthOfLongestSubstringKDistinct(self, s: str, k: int) -&amp;gt; int:
        ans = 0
        cnt = defaultdict(int)
        l = 0
        for r, c in enumerate(s):
            cnt[c] += 1 # cnt[c] is an integer (the frequency of character c)
            while len(cnt) &amp;gt; k: // we need check how many distinct characters are currently in the sliding window. 
                cnt[s[l]] -= 1
                if cnt[s[l]] == 0:
                    del cnt[s[l]]
                l += 1
            ans = max(ans, r - l + 1)
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;a href=&quot;https://leetcode.com/problems/fruit-into-baskets/&quot;&gt;904. Fruit Into Baskets&lt;/a&gt;  (at most 2 distinct elements )&lt;/h3&gt;
&lt;p&gt;You are visiting a farm that has a single row of fruit trees arranged from left to right. The trees are represented by an integer array &lt;code&gt;fruits&lt;/code&gt; where &lt;code&gt;fruits[i]&lt;/code&gt; is the &lt;strong&gt;type&lt;/strong&gt; of fruit the &lt;code&gt;ith&lt;/code&gt; tree produces.&lt;/p&gt;
&lt;p&gt;You want to collect as much fruit as possible. However, the owner has some strict rules that you must follow:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You only have &lt;strong&gt;two&lt;/strong&gt; baskets, and each basket can only hold a &lt;strong&gt;single type&lt;/strong&gt; of fruit. There is no limit on the amount of fruit each basket can hold.&lt;/li&gt;
&lt;li&gt;Starting from any tree of your choice, you must pick &lt;strong&gt;exactly one fruit&lt;/strong&gt; from &lt;strong&gt;every&lt;/strong&gt; tree (including the start tree) while moving to the right. The picked fruits must fit in one of your baskets.&lt;/li&gt;
&lt;li&gt;Once you reach a tree with fruit that cannot fit in your baskets, you must stop.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Given the integer array &lt;code&gt;fruits&lt;/code&gt;, return &lt;em&gt;the &lt;strong&gt;maximum&lt;/strong&gt; number of fruits you can pick&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: fruits = [1,2,1]
Output: 3
Explanation: We can pick from all 3 trees.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: fruits = [0,1,2,2]
Output: 3
Explanation: We can pick from trees [1,2,2].
If we had started at the first tree, we would only pick from trees [0,1].
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 3:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: fruits = [1,2,3,2,2]
Output: 4
Explanation: We can pick from trees [2,3,2,2].
If we had started at the first tree, we would only pick from trees [1,2].
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Constraints:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= fruits.length &amp;lt;= 105&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0 &amp;lt;= fruits[i] &amp;lt; fruits.length&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
public:
    int totalFruit(vector&amp;lt;int&amp;gt;&amp;amp; fruits) {
        int ans = 0;
        map&amp;lt;int, int&amp;gt; cnt; // count each fruit type inside the window
        int l = 0;
        int n = fruits.size();
        for (int r = 0; r &amp;lt; n; r++) {
            cnt[fruits[r]]++; // do increment
            while (cnt.size() &amp;gt; 2) { // if the size of the fruit type exceed 2, we need shrink the window from the left
                cnt[fruits[l]]--;
                if (cnt[fruits[l]] == 0) {
                    cnt.erase(fruits[l]);
                }
                l++;
            }
            ans = max(ans, r - l + 1);
        }
        return ans;
    }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def totalFruit(self, fruits: List[int]) -&amp;gt; int:
        ans = 0
        cnt = defaultdict(int)
        l = 0
        for r, x in enumerate(fruits):
            cnt[x] += 1
            while len(cnt) &amp;gt; 2: # reflects number of fruit types
                cnt[fruits[l]] -= 1 
                if cnt[fruits[l]] == 0:
                    del cnt[fruits[l]]
                l += 1
            ans = max(ans, r - l + 1)
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;a href=&quot;https://leetcode.com/problems/longest-substring-without-repeating-characters/&quot;&gt;3. Longest Substring Without Repeating Characters&lt;/a&gt; （at most 1 occurrence）&lt;/h3&gt;
&lt;p&gt;Given a string &lt;code&gt;s&lt;/code&gt;, find the length of the &lt;strong&gt;longest&lt;/strong&gt; &lt;strong&gt;substring&lt;/strong&gt; without duplicate characters.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: s = &quot;abcabcbb&quot;
Output: 3
Explanation: The answer is &quot;abc&quot;, with the length of 3. Note that &quot;bca&quot; and &quot;cab&quot; are also correct answers.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: s = &quot;bbbbb&quot;
Output: 1
Explanation: The answer is &quot;b&quot;, with the length of 1.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 3:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: s = &quot;pwwkew&quot;
Output: 3
Explanation: The answer is &quot;wke&quot;, with the length of 3.
Notice that the answer must be a substring, &quot;pwke&quot; is a subsequence and not a substring.
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int ans = 0;
        int l = 0; // l is leftbound and r is right bound
        map&amp;lt;char, int&amp;gt; cnt;
        int n = s.size();
        for (int r = 0; r &amp;lt; n; r++) {
            cnt[s[r]]++;
            
            // if the occurrences of the current character is greater than one, it is not valid substring, we need shrink the left boundary to make it valid
            while (cnt[s[r]] &amp;gt; 1) {
                cnt[s[l]]--;
                if (cnt[s[l]] == 0) {
                    cnt.erase(s[l]);
                }
                l++;
            }
            ans = max(ans, r - l + 1); 
            
        }
        return ans;
    }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def lengthOfLongestSubstring(self, s: str) -&amp;gt; int:
        ans = 0
        l = 0
        cnt = defaultdict(int)
        for r, c in enumerate(s):
            cnt[c] += 1
            while cnt[c] &amp;gt; 1:
                cnt[s[l]] -= 1
                l += 1
            ans = max(ans, r - l + 1)
        return ans

&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;a href=&quot;https://leetcode.com/problems/maximum-length-substring-with-two-occurrences/&quot;&gt;3090. Maximum Length Substring With Two Occurrences&lt;/a&gt; （at most 2 occurrences）&lt;/h3&gt;
&lt;p&gt;Given a string &lt;code&gt;s&lt;/code&gt;, return the &lt;strong&gt;maximum&lt;/strong&gt; length of a substring such that it contains &lt;em&gt;at most two occurrences&lt;/em&gt; of each character.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Input:&lt;/strong&gt; s = &quot;bcbbbcba&quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt; 4&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The following substring has a&lt;/p&gt;
&lt;p&gt;length&lt;/p&gt;
&lt;p&gt;of 4 and contains at most two&lt;/p&gt;
&lt;p&gt;occurrences&lt;/p&gt;
&lt;p&gt;of each character: &lt;code&gt;&quot;bcbbbcba&quot;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Input:&lt;/strong&gt; s = &quot;aaaa&quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt; 2&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The following substring has a&lt;/p&gt;
&lt;p&gt;length&lt;/p&gt;
&lt;p&gt;of 2 and contains at most two&lt;/p&gt;
&lt;p&gt;occurrences&lt;/p&gt;
&lt;p&gt;of each character: &lt;code&gt;&quot;aaaa&quot;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Constraints:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;2 &amp;lt;= s.length &amp;lt;= 100&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;s&lt;/code&gt; consists only of lowercase English letters.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
public:
    int maximumLengthSubstring(string s) {
        int ans = 0;
        map&amp;lt;char, int&amp;gt; cnt;
        int l = 0;
        int n = s.size();
        for (int r = 0; r &amp;lt; n; r++) {
            cnt[s[r]]++;
            while (cnt[s[r]] &amp;gt; 2) { // if the count exceed 2, the window becomes invalid. we will shrink the window from the left
                cnt[s[l]]--; // decreses the count of s[l]
                l++; // move the left boundary to next position
            }
            ans = max(ans, r - l + 1);
        }
        return ans;
    }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def maximumLengthSubstring(self, s: str) -&amp;gt; int:
        ans = 0
        d = defaultdict(int)
        l = 0
        for r, c in enumerate(s):
            d[c] += 1
            while d[c] &amp;gt; 2:
                d[s[l]] -= 1
                l += 1
            ans = max(ans, r - l + 1)
        return ans

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Exactly K Distinct&lt;/h2&gt;
&lt;p&gt;:::important&lt;/p&gt;
&lt;p&gt;Exactly K Distinct Elements&lt;/p&gt;
&lt;p&gt;:::&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://leetcode.com/problems/count-number-of-nice-subarrays/&quot;&gt;1248. Count Number of Nice Subarrays&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Given an array of integers &lt;code&gt;nums&lt;/code&gt; and an integer &lt;code&gt;k&lt;/code&gt;. A continuous subarray is called &lt;strong&gt;nice&lt;/strong&gt; if there are &lt;code&gt;k&lt;/code&gt; odd numbers on it.&lt;/p&gt;
&lt;p&gt;Return &lt;em&gt;the number of &lt;strong&gt;nice&lt;/strong&gt; sub-arrays&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: nums = [1,1,2,1,1], k = 3
Output: 2
Explanation: The only sub-arrays with 3 odd numbers are [1,1,2,1] and [1,2,1,1].
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: nums = [2,4,6], k = 1
Output: 0
Explanation: There are no odd numbers in the array.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 3:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: nums = [2,2,2,1,2,2,1,2,2,2], k = 2
Output: 16
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Constraints:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= nums.length &amp;lt;= 50000&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= nums[i] &amp;lt;= 10^5&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= k &amp;lt;= nums.length&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
public:
    int numberOfSubarrays(vector&amp;lt;int&amp;gt;&amp;amp; nums, int k) {
        return atMostNumberOfSubarrays(nums, k)
            - atMostNumberOfSubarrays(nums, k - 1);

    }

private:
    int atMostNumberOfSubarrays(vector&amp;lt;int&amp;gt;&amp;amp; nums, int k) {
        int ans = 0;
        int oddCnt = 0;
        int left = 0;
        int n = nums.size();
        for (int right = 0; right &amp;lt; n; right++) {
            oddCnt += nums[right] &amp;amp; 1;
            while (oddCnt &amp;gt; k) {
                oddCnt -= nums[left] &amp;amp; 1;
                left++;
            }
            ans += right - left + 1;
        }
        return ans;
    }
};

&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;# Why exactly K cannot be handled directly with a sliding window
# Imagine the current window contains exactly k odd numbers.
# When the right pointer moves:
# If the new number is odd → the count becomes k + 1 (invalid)
# If the new number is even → the count stays k (still valid)
# When the left pointer moves:
# If the removed number is odd → the count becomes k − 1 (invalid)
# If the removed number is even → the count stays k (still valid)
class Solution:
    &quot;&quot;&quot;
    exactly K = at most K - at most (k - 1)
    &quot;&quot;&quot;

    def numberOfSubarrays(self, nums: List[int], k: int) -&amp;gt; int: 
        # This nested function exists only to help solve this problem.
        # share the context via closures
        def atMost(k):
            ans = l = cnt = 0
            for r, x in enumerate(nums):
                cnt += x &amp;amp; 1
                while cnt &amp;gt; k:
                    cnt -= nums[l] &amp;amp; 1
                    l += 1
                # when the while loop finishes, cnt must be less than or equal to k, it means the window [l, r] satisfies the constraint. so all subarrays ending at r and starting from any index between l and r are valid, so we add r - l + 1.
                ans += r - l + 1
            return ans
        
        # To compute the number of subarrays with exactly k odd numbers by using the numbers of subarrays with at most k odd numbers to substracting the one with at most k - 1 odd numbers.
        return atMost(k) - atMost(k - 1)
                
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;a href=&quot;https://leetcode.com/problems/count-vowel-substrings-of-a-string/&quot;&gt;2062. Count Vowel Substrings of a String&lt;/a&gt; （exactly including 5 all vowels 这个和其他exactly k 不一样）&lt;/h3&gt;
&lt;p&gt;:::note&lt;/p&gt;
&lt;p&gt;For “contains all 5 vowels”, expanding &lt;code&gt;r&lt;/code&gt; is monotonic (valid stays valid), but shrinking &lt;code&gt;l&lt;/code&gt; can break validity, so the classic “invalid → shrink” template doesn’t directly apply.&lt;/p&gt;
&lt;p&gt;那还能用滑窗吗？&lt;/p&gt;
&lt;p&gt;可以，只是换一种形式（合法时缩）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不合法 → 扩 r 去变合法&lt;/li&gt;
&lt;li&gt;合法 → 尝试缩 l，并统计答案&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;扩到合法，再缩到刚好不合法，再继续扩&lt;/p&gt;
&lt;p&gt;:::&lt;/p&gt;
&lt;p&gt;A &lt;strong&gt;substring&lt;/strong&gt; is a contiguous (non-empty) sequence of characters within a string.&lt;/p&gt;
&lt;p&gt;A &lt;strong&gt;vowel substring&lt;/strong&gt; is a substring that &lt;strong&gt;only&lt;/strong&gt; consists of vowels (&lt;code&gt;&apos;a&apos;&lt;/code&gt;, &lt;code&gt;&apos;e&apos;&lt;/code&gt;, &lt;code&gt;&apos;i&apos;&lt;/code&gt;, &lt;code&gt;&apos;o&apos;&lt;/code&gt;, and &lt;code&gt;&apos;u&apos;&lt;/code&gt;) and has &lt;strong&gt;all five&lt;/strong&gt; vowels present in it.&lt;/p&gt;
&lt;p&gt;Given a string &lt;code&gt;word&lt;/code&gt;, return &lt;em&gt;the number of &lt;strong&gt;vowel substrings&lt;/strong&gt; in&lt;/em&gt; &lt;code&gt;word&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: word = &quot;aeiouu&quot;
Output: 2
Explanation: The vowel substrings of word are as follows (underlined):
- &quot;aeiouu&quot;
- &quot;aeiouu&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: word = &quot;unicornarihan&quot;
Output: 0
Explanation: Not all 5 vowels are present, so there are no vowel substrings.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 3:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: word = &quot;cuaieuouac&quot;
Output: 7
Explanation: The vowel substrings of word are as follows (underlined):
- &quot;cuaieuouac&quot;
- &quot;cuaieuouac&quot;
- &quot;cuaieuouac&quot;
- &quot;cuaieuouac&quot;
- &quot;cuaieuouac&quot;
- &quot;cuaieuouac&quot;
- &quot;cuaieuouac&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Constraints:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= word.length &amp;lt;= 100&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;word&lt;/code&gt; consists of lowercase English letters only.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
public:
    bool isVowel(char c) {
        return c == &apos;a&apos; || c == &apos;e&apos; || c == &apos;i&apos;
            || c == &apos;o&apos; || c == &apos;u&apos;;
    }

    int countVowelSubstrings(string word) {
        int ans = 0;
        int n = word.size();
        
        int i = 0;
        while (i &amp;lt; n) {
            if (!isVowel(word[i])) {
                i++;
                continue;
            }

            int j = i;
            while (j &amp;lt; n &amp;amp;&amp;amp; isVowel(word[j])) j++;

            if (j - i &amp;gt;= 5) {
                unordered_map&amp;lt;char, int&amp;gt; cnt;
                int l = i;
                for (int r = i; r &amp;lt; j; r++) {
                    cnt[word[r]]++;
                    while (cnt.size() == 5) {
                        ans += j - r; // [l, r] is a valid substring, so [l, r], [l, r+1], [l, r+2], ..., [l, j-1] are also valid.

                        cnt[word[l]]--;
                        if (cnt[word[l]] == 0)
                            cnt.erase(word[l]);
                        l++;
                    }
                }
            }
            i = j;
        }

        return ans;
    }
};

&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def countVowelSubstrings(self, word: str) -&amp;gt; int:
        ans = 0
        vowels = set(&apos;aeiou&apos;)
        i = 0
        while i &amp;lt; len(word):
            if word[i] not in vowels:
                i += 1
                continue
            j = i
            while j &amp;lt; len(word) and word[j] in vowels:
                j += 1
            if j - i &amp;gt;= len(vowels):
                cnt = defaultdict(int)
                l = i
                for r in range(i, j):
                    cnt[word[r]] += 1
                    while len(cnt) == len(vowels):
                        ans += j - r
                        cnt[word[l]] -= 1
                        if cnt[word[l]] == 0:
                            del cnt[word[l]]
                        l += 1
            i = j

        return ans 

&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def countVowelSubstrings(self, word: str) -&amp;gt; int:
        &quot;&quot;&quot;
        当窗口 [l, r] 已经包含 a e i o u：
        [l, r]
        [l, r+1]
        [l, r+2]
        ……
        全部都是合法子串
        所以是 len(s) - r
        &quot;&quot;&quot;
        ans = 0
        for s in re.findall(r&apos;[aeiou]+&apos;, word):
            if len(s) &amp;lt; 5:
                continue
            cnt = defaultdict(int)
            l = 0
            for r, c in enumerate(s):
                cnt[c] += 1
                while len(cnt) == 5:
                    ans += len(s) - r
                    cnt[s[l]] -= 1
                    if cnt[s[l]] == 0:
                        del cnt[s[l]]
                    l += 1
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;a href=&quot;https://leetcode.com/problems/subarrays-with-k-different-integers/&quot;&gt;992. Subarrays with K Different Integers&lt;/a&gt; （Exactly K Different Integers）&lt;/h3&gt;
&lt;p&gt;Given an integer array &lt;code&gt;nums&lt;/code&gt; and an integer &lt;code&gt;k&lt;/code&gt;, return &lt;em&gt;the number of &lt;strong&gt;good subarrays&lt;/strong&gt; of&lt;/em&gt; &lt;code&gt;nums&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A &lt;strong&gt;good array&lt;/strong&gt; is an array where the number of different integers in that array is exactly &lt;code&gt;k&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For example, &lt;code&gt;[1,2,3,1,2]&lt;/code&gt; has &lt;code&gt;3&lt;/code&gt; different integers: &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;2&lt;/code&gt;, and &lt;code&gt;3&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A &lt;strong&gt;subarray&lt;/strong&gt; is a &lt;strong&gt;contiguous&lt;/strong&gt; part of an array.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: nums = [1,2,1,2,3], k = 2
Output: 7
Explanation: Subarrays formed with exactly 2 different integers: [1,2], [2,1], [1,2], [2,3], [1,2,1], [2,1,2], [1,2,1,2]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: nums = [1,2,1,3,4], k = 3
Output: 3
Explanation: Subarrays formed with exactly 3 different integers: [1,2,1,3], [2,1,3], [1,3,4].
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Constraints:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= nums.length &amp;lt;= 2 * 104&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= nums[i], k &amp;lt;= nums.length&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
public:
    int subarrayAtMostKDistinct(vector&amp;lt;int&amp;gt;&amp;amp; nums, int k) {
        int n = nums.size();
        int ans = 0;
        map&amp;lt;int, int&amp;gt;cnt;
        int l = 0;
        for (int r = 0; r &amp;lt; n; r++) {
            cnt[nums[r]]++;
            while (cnt.size() &amp;gt; k) { // distinc elements is greater than k
                cnt[nums[l]]--;
                if (cnt[nums[l]] == 0) {
                    cnt.erase(nums[l]);
                }
                l++;
            }
            /**
            [l, r] 中最多有 k 个不同元素

            [l, r]
            [l+1, r]
            [l+2, r]
            ...
            [r, r]

            r - l + 1 个
            **/
            ans += r - l + 1;
        }
        return ans;

    }
    int subarraysWithKDistinct(vector&amp;lt;int&amp;gt;&amp;amp; nums, int k) {
        return subarrayAtMostKDistinct(nums, k) - subarrayAtMostKDistinct(nums, k - 1);
    }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def subarraysWithKDistinct(self, nums: List[int], k: int) -&amp;gt; int:
        def atMost(K):
            cnt = defaultdict(int)
            l = 0
            res = 0

            for r, x in enumerate(nums):
                cnt[x] += 1

                while len(cnt) &amp;gt; K:
                    cnt[nums[l]] -= 1
                    if cnt[nums[l]] == 0:
                        del cnt[nums[l]]
                    l += 1

                res += (r - l + 1)
            return res
        
        return atMost(k) - atMost(k - 1)

&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;a href=&quot;https://leetcode.com/problems/subarray-product-less-than-k/&quot;&gt;713. Subarray Product Less Than K&lt;/a&gt;  (less than k (belong to at most))&lt;/h3&gt;
&lt;p&gt;Given an array of integers &lt;code&gt;nums&lt;/code&gt; and an integer &lt;code&gt;k&lt;/code&gt;, return &lt;em&gt;the number of contiguous subarrays where the product of all the elements in the subarray is strictly less than&lt;/em&gt; &lt;code&gt;k&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: nums = [10,5,2,6], k = 100
Output: 8
Explanation: The 8 subarrays that have product less than 100 are:
[10], [5], [2], [6], [10, 5], [5, 2], [2, 6], [5, 2, 6]
Note that [10, 5, 2] is not included as the product of 100 is not strictly less than k.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: nums = [1,2,3], k = 0
Output: 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Constraints:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= nums.length &amp;lt;= 3 * 104&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= nums[i] &amp;lt;= 1000&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0 &amp;lt;= k &amp;lt;= 106&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def numSubarrayProductLessThanK(self, nums: List[int], k: int) -&amp;gt; int:
        # 当 k ≤ 1 时，不可能存在 product &amp;lt; k 的子数组，所以答案必为 0。
        if k &amp;lt;= 1:
            return 0
        ans, product, l = 0, 1, 0
        for r, x in enumerate(nums):
            product *= x
            while product &amp;gt;= k:
                product //= nums[l] # /会使变成float
                l += 1
            ans += r - l + 1
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;At Least&lt;/h2&gt;
&lt;p&gt;:::important&lt;/p&gt;
&lt;p&gt;At Least（至少）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;≥ K&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不稳定&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;通常转化为 at most&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;满足 ≥ K 的数量  = 所有可能的数量 − 不满足 ≥ K（也就是 ≤ K−1）的数量&lt;/p&gt;
&lt;p&gt;&lt;code&gt;at least K = total − at most (K − 1) &lt;/code&gt; 这种还没遇到这样的题目&lt;/p&gt;
&lt;p&gt;:::&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://leetcode.com/problems/count-subarrays-where-max-element-appears-at-least-k-times/&quot;&gt;2962. Count Subarrays Where Max Element Appears at Least K Times&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You are given an integer array &lt;code&gt;nums&lt;/code&gt; and a &lt;strong&gt;positive&lt;/strong&gt; integer &lt;code&gt;k&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Return &lt;em&gt;the number of subarrays where the &lt;strong&gt;maximum&lt;/strong&gt; element of&lt;/em&gt; &lt;code&gt;nums&lt;/code&gt; &lt;em&gt;appears &lt;strong&gt;at least&lt;/strong&gt;&lt;/em&gt; &lt;code&gt;k&lt;/code&gt; &lt;em&gt;times in that subarray.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;A &lt;strong&gt;subarray&lt;/strong&gt; is a contiguous sequence of elements within an array.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: nums = [1,3,2,3,3], k = 2
Output: 6
Explanation: The subarrays that contain the element 3 at least 2 times are: [1,3,2,3], [1,3,2,3,3], [3,2,3], [3,2,3,3], [2,3,3] and [3,3].
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: nums = [1,4,2,1], k = 3
Output: 0
Explanation: No subarray contains the element 4 at least 3 times.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Constraints:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= nums.length &amp;lt;= 105&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= nums[i] &amp;lt;= 106&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= k &amp;lt;= 105&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def countSubarrays(self, nums: List[int], k: int) -&amp;gt; int:
        ans = 0
        l = 0
        cnt = defaultdict(int)
        mx = max(nums)
        n = len(nums)
        for r, x in enumerate(nums):
            cnt[x] += 1
            while cnt[mx] &amp;gt;= k:
                ans += n - r
                cnt[nums[l]] -= 1
                l += 1
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Greedy + Two Pointers + run-based scanning&lt;/h2&gt;
&lt;h3&gt;&lt;a href=&quot;https://leetcode.com/problems/longest-substring-of-all-vowels-in-order/&quot;&gt;1839. Longest Substring Of All Vowels in Order&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A string is considered &lt;strong&gt;beautiful&lt;/strong&gt; if it satisfies the following conditions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Each of the 5 English vowels (&lt;code&gt;&apos;a&apos;&lt;/code&gt;, &lt;code&gt;&apos;e&apos;&lt;/code&gt;, &lt;code&gt;&apos;i&apos;&lt;/code&gt;, &lt;code&gt;&apos;o&apos;&lt;/code&gt;, &lt;code&gt;&apos;u&apos;&lt;/code&gt;) must appear &lt;strong&gt;at least once&lt;/strong&gt; in it.&lt;/li&gt;
&lt;li&gt;The letters must be sorted in &lt;strong&gt;alphabetical order&lt;/strong&gt; (i.e. all &lt;code&gt;&apos;a&apos;&lt;/code&gt;s before &lt;code&gt;&apos;e&apos;&lt;/code&gt;s, all &lt;code&gt;&apos;e&apos;&lt;/code&gt;s before &lt;code&gt;&apos;i&apos;&lt;/code&gt;s, etc.).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example, strings &lt;code&gt;&quot;aeiou&quot;&lt;/code&gt; and &lt;code&gt;&quot;aaaaaaeiiiioou&quot;&lt;/code&gt; are considered &lt;strong&gt;beautiful&lt;/strong&gt;, but &lt;code&gt;&quot;uaeio&quot;&lt;/code&gt;, &lt;code&gt;&quot;aeoiu&quot;&lt;/code&gt;, and &lt;code&gt;&quot;aaaeeeooo&quot;&lt;/code&gt; are &lt;strong&gt;not beautiful&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Given a string &lt;code&gt;word&lt;/code&gt; consisting of English vowels, return &lt;em&gt;the &lt;strong&gt;length of the longest beautiful substring&lt;/strong&gt; of&lt;/em&gt; &lt;code&gt;word&lt;/code&gt;&lt;em&gt;. If no such substring exists, return&lt;/em&gt; &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A &lt;strong&gt;substring&lt;/strong&gt; is a contiguous sequence of characters in a string.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: word = &quot;aeiaaioaaaaeiiiiouuuooaauuaeiu&quot;
Output: 13
Explanation: The longest beautiful substring in word is &quot;aaaaeiiiiouuu&quot; of length 13.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: word = &quot;aeeeiiiioooauuuaeiou&quot;
Output: 5
Explanation: The longest beautiful substring in word is &quot;aeiou&quot; of length 5.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 3:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: word = &quot;a&quot;
Output: 0
Explanation: There is no beautiful substring, so return 0.
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
public:
    int longestBeautifulSubstring(string word) {
        int ans = 0;
        int l = 0, r = 0;
        int n = word.size();
        while (r &amp;lt; n) {
            if (word[r] != &apos;a&apos;) {
                r++;
                continue;
            }
            int l = r;
            r += 1;
            int type = 1;
            while (r &amp;lt; n &amp;amp;&amp;amp; word[r] &amp;gt;= word[r - 1]) {
                if (word[r] &amp;gt; word[r - 1]) {
                    type++;
                }
                r++;
            }
            if (type == 5)
                ans = max(ans, r - l);
        }
        return ans;
    }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def longestBeautifulSubstring(self, word: str) -&amp;gt; int:
        ans, l, r = 0, 0, 0
        n = len(word)
        while r &amp;lt; n:
            if word[r] != &apos;a&apos;:
                r += 1
                continue
            l = r
            r += 1
            type = 1 # initially we set the type to be 1, because the first char is &apos;a&apos;
            while r &amp;lt; n and word[r] &amp;gt;= word[r - 1]: # if next char is greater than or equal to previous one, it means it is sorted in alphabetical order
                if word[r] != word[r - 1]:
                    type += 1
                r += 1

            if type == 5:
                ans = max(ans, r - l)
        return ans

&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Leetcode150</title><link>https://lxy-alexander.github.io/blog/posts/algorithm/leetcode150/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/algorithm/leetcode150/</guid><description>Leetcode150</description><pubDate>Sun, 18 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Array / String&lt;/h1&gt;
&lt;p&gt;You are given two integer arrays &lt;code&gt;nums1&lt;/code&gt; and &lt;code&gt;nums2&lt;/code&gt;, sorted in &lt;strong&gt;non-decreasing order&lt;/strong&gt;, and two integers &lt;code&gt;m&lt;/code&gt; and &lt;code&gt;n&lt;/code&gt;, representing the number of elements in &lt;code&gt;nums1&lt;/code&gt; and &lt;code&gt;nums2&lt;/code&gt; respectively.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Merge&lt;/strong&gt; &lt;code&gt;nums1&lt;/code&gt; and &lt;code&gt;nums2&lt;/code&gt; into a single array sorted in &lt;strong&gt;non-decreasing order&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The final sorted array should not be returned by the function, but instead be &lt;em&gt;stored inside the array&lt;/em&gt; &lt;code&gt;nums1&lt;/code&gt;. To accommodate this, &lt;code&gt;nums1&lt;/code&gt; has a length of &lt;code&gt;m + n&lt;/code&gt;, where the first &lt;code&gt;m&lt;/code&gt; elements denote the elements that should be merged, and the last &lt;code&gt;n&lt;/code&gt; elements are set to &lt;code&gt;0&lt;/code&gt; and should be ignored. &lt;code&gt;nums2&lt;/code&gt; has a length of &lt;code&gt;n&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
Output: [1,2,2,3,5,6]
Explanation: The arrays we are merging are [1,2,3] and [2,5,6].
The result of the merge is [1,2,2,3,5,6] with the underlined elements coming from nums1.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: nums1 = [1], m = 1, nums2 = [], n = 0
Output: [1]
Explanation: The arrays we are merging are [1] and [].
The result of the merge is [1].
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 3:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: nums1 = [0], m = 0, nums2 = [1], n = 1
Output: [1]
Explanation: The arrays we are merging are [] and [1].
The result of the merge is [1].
Note that because m = 0, there are no elements in nums1. The 0 is only there to ensure the merge result can fit in nums1.
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -&amp;gt; None:
        &quot;&quot;&quot;
        Do not return anything, modify nums1 in-place instead.
        &quot;&quot;&quot;
        i, j, k = m - 1, n - 1, m + n - 1

        while i &amp;gt;= 0 and j &amp;gt;= 0:
            if nums1[i] &amp;lt;= nums2[j]:
                nums1[k] = nums2[j]
                j -= 1
            else:
                nums1[k] = nums1[i]
                i -= 1
            k -= 1

        if j &amp;gt;= 0:
            nums1[: k + 1] = nums2[: j + 1]
            
            

class Solution {
public:
    void merge(vector&amp;lt;int&amp;gt;&amp;amp; nums1, int m, vector&amp;lt;int&amp;gt;&amp;amp; nums2, int n) {
        int i = m - 1;
        int j = n - 1;
        int k = m + n - 1;
        while (i &amp;gt;= 0 &amp;amp;&amp;amp; j &amp;gt;= 0) {
            if (nums1[i] &amp;lt;= nums2[j]) {
                nums1[k--] = nums2[j--]; 
            } else {
                nums1[k--] = nums1[i--];
            }
        }
        while (j &amp;gt;= 0) {
            nums1[k--] = nums2[j--];
        }
    }
};

&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>BQs</title><link>https://lxy-alexander.github.io/blog/posts/interview/bqs/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/interview/bqs/</guid><description>BQs</description><pubDate>Thu, 15 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;https://www.mockquestions.com/position/Engineer/&lt;/p&gt;
&lt;h1&gt;Template&lt;/h1&gt;
&lt;h2&gt;Why our company?&lt;/h2&gt;
&lt;p&gt;1）强调技术栈匹配&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I’m excited about your tech stack, especially ___, and I’d love to work on challenging engineering problems.&lt;/strong&gt;
我对你们的技术栈很感兴趣，尤其是___，我很想解决有挑战的工程问题。&lt;/p&gt;
&lt;p&gt;2）强调成长 + 学习&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I’m looking for a place where I can keep learning, improve my system design skills, and work with strong engineers.&lt;/strong&gt;
我希望在一个能持续学习、提升系统设计能力、并和优秀工程师合作的环境里成长。&lt;/p&gt;
&lt;p&gt;3）强调产品影响力（科技岗很爱）&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Your product solves a real problem for users, and I want my work to create meaningful value.&lt;/strong&gt;
你们的产品解决真实用户问题，我希望我的工作能创造有意义的价值。&lt;/p&gt;
&lt;h1&gt;MockQuestions&lt;/h1&gt;
&lt;h2&gt;Describe a difficult project and how you overcame it.&lt;/h2&gt;
&lt;p&gt;The GPU x project was a low-level and highly complex project.==One major challenge for me was switching from &lt;strong&gt;Java to C++&lt;/strong&gt;==, while also learning &lt;strong&gt;OpenGL&lt;/strong&gt; and GPU-related concepts at the same time&lt;/p&gt;
&lt;p&gt;GPU x 项目是一个非常底层、技术复杂度很高的项目。我面临的主要挑战是需要从 &lt;strong&gt;Java 切换到 C++&lt;/strong&gt;，同时还要学习 &lt;strong&gt;OpenGL&lt;/strong&gt; 以及 GPU 相关的底层知识。.&lt;/p&gt;
&lt;p&gt;Since the project involved low-level graphics and performance optimization, the learning curve was steep. ==To overcome this, I actively asked experienced teammates for guidance and collected high-quality internal technical documents within the company.== I combined these resources with official documentation and hands-on experiments to gradually build my  understanding.&lt;/p&gt;
&lt;p&gt;为了克服学习成本高的问题，我主动向周围有经验的同事请教，并整理和学习了公司内部已有的优质技术资料，同时结合官方文档和实践不断验证理解。&lt;/p&gt;
&lt;p&gt;Through this process, I successfully adapted to C++ development and gained a much deeper understanding of low-level graphics systems and GPU optimization.&lt;/p&gt;
&lt;p&gt;通过这个过程，我顺利完成了语言和技术栈的切换，也对底层图形系统和 GPU 优化有了更深入的认识。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;What was your greatest accomplishment as an Engineer?&lt;/h2&gt;
</content:encoded></item><item><title>Build Guides</title><link>https://lxy-alexander.github.io/blog/posts/guide/guides/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/guide/guides/</guid><description>How to buid the blog website</description><pubDate>Thu, 01 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;🚀  博客构建全流程指南&lt;/h2&gt;
&lt;p&gt;这是一份基于 &lt;strong&gt;Fuwari&lt;/strong&gt; 模板与 &lt;strong&gt;Astro&lt;/strong&gt; 框架的博客构建全流程指南。本指南整合了从本地环境搭建到 GitHub Pages 自动化部署的完整步骤。&lt;/p&gt;
&lt;h3&gt;1. 环境准备&lt;/h3&gt;
&lt;p&gt;在开始之前，请确保你的设备已安装以下工具：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Node.js&lt;/strong&gt;: 版本需  18。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;pnpm&lt;/strong&gt;: 推荐的包管理器。安装命令：&lt;code&gt;npm install -g pnpm&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Git&lt;/strong&gt;: 用于代码版本管理。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;2. 初始化项目&lt;/h3&gt;
&lt;p&gt;你可以通过以下两种方式之一创建博客：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;方式 A（推荐）&lt;/strong&gt;: 访问 &lt;a href=&quot;https://github.com/saicaca/fuwari/generate&quot;&gt;Fuwari 模板页&lt;/a&gt; 直接生成新仓库。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;方式 B (命令行)&lt;/strong&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;pnpm create fuwari@latest

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;创建完成后，克隆仓库至本地并安装依赖：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pnpm install

&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;3. 本地开发与配置&lt;/h3&gt;
&lt;p&gt;在正式发布前，你需要进行个性化设置。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;基础配置&lt;/strong&gt;: 编辑 &lt;code&gt;src/config.ts&lt;/code&gt; 修改站点标题、作者、社交链接等。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;运行预览&lt;/strong&gt;: 执行 &lt;code&gt;pnpm dev&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;访问 &lt;code&gt;http://localhost:4321&lt;/code&gt; 查看实时效果。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;该模式支持&lt;strong&gt;热更新&lt;/strong&gt;，修改文件后浏览器会自动刷新。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;清理图标&lt;/strong&gt;: 若需删除默认图标，可在 &lt;code&gt;public/&lt;/code&gt; 目录下替换 &lt;code&gt;favicon.svg&lt;/code&gt;，或在 &lt;code&gt;src/components&lt;/code&gt; 中移除相关的 &lt;code&gt;&amp;lt;Icon /&amp;gt;&lt;/code&gt; 标签。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h3&gt;4. 撰写内容&lt;/h3&gt;
&lt;p&gt;Fuwari 使用 Markdown 存储文章，位置在 &lt;code&gt;src/content/posts/&lt;/code&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;创建新文章&lt;/strong&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;pnpm new-post &amp;lt;文件名&amp;gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;配置 Frontmatter&lt;/strong&gt;: 在 &lt;code&gt;.md&lt;/code&gt; 文件顶部配置元数据：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;---
title: 我的第一篇文章
published: 2026-01-22
description: 文章描述
image: ./cover.jpg
tags: [教程, Astro]
category: 技术
draft: false
---

&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h3&gt;5. GitHub Pages 部署配置&lt;/h3&gt;
&lt;p&gt;为了让全球用户都能访问，我们需要配置自动化部署。&lt;/p&gt;
&lt;h4&gt;A. 修改 Astro 配置&lt;/h4&gt;
&lt;p&gt;编辑 &lt;code&gt;astro.config.mjs&lt;/code&gt;，确保路径正确：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export default defineConfig({
  site: &quot;https://&amp;lt;你的用户名&amp;gt;.github.io&quot;,
  base: &quot;/&amp;lt;仓库名&amp;gt;/&quot;, // 如果仓库名是 username.github.io，则此项留空或填 &quot;/&quot;
});

&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;B. 创建部署脚本 (GitHub Actions)&lt;/h4&gt;
&lt;p&gt;在项目根目录创建 &lt;code&gt;.github/workflows/deploy.yml&lt;/code&gt;，并粘贴以下核心配置：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;name: Deploy to GitHub Pages

on:
  # 每次推送到 `main` 分支时触发这个“工作流程”
  # 如果你使用了别的分支名，请按需将 `main` 替换成你的分支名
  push:
    branches: [ main ]
  # 允许你在 GitHub 上的 Actions 标签中手动触发此“工作流程”
  workflow_dispatch:

# 允许 job 克隆 repo 并创建一个 page deployment
permissions:
  contents: read
  pages: write
  id-token: write

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout your repository using git
        uses: actions/checkout@v5
      - name: Install, build, and upload your site
        uses: withastro/action@v5
        # with:
          # path: . # 存储库中 Astro 项目的根位置。（可选）
          # node-version: 20 # 用于构建站点的特定 Node.js 版本，默认为 20。（可选）
          # package-manager: pnpm@latest # 应使用哪个 Node.js 包管理器来安装依赖项和构建站点。会根据存储库中的 lockfile 自动检测。（可选）
          # build-cmd: pnpm run build # 用于构建你的网站的命令。默认运行软件包的构建脚本或任务。（可选）
        # env:
          # PUBLIC_POKEAPI: &apos;https://pokeapi.co/api/v2&apos; # 对变量值使用单引号。（可选）

  deploy:
    needs: build
    runs-on: ubuntu-latest
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    steps:
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;该脚本会在你每次 &lt;code&gt;git push&lt;/code&gt; 时自动完成安装、构建并发布。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;C. 开启 GitHub 设置&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;进入 GitHub 仓库 &lt;strong&gt;Settings&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Pages&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;在 &lt;strong&gt;Build and deployment&lt;/strong&gt; 下的 &lt;strong&gt;Source&lt;/strong&gt; 选项中，选择 &lt;strong&gt;GitHub Actions&lt;/strong&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h3&gt;6. 发布上线&lt;/h3&gt;
&lt;p&gt;执行以下命令将代码推送到 GitHub：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git add .
git commit -m &quot;Initial blog setup&quot;
git push origin main
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;检查进度&lt;/strong&gt;: 在 GitHub 仓库的 &lt;strong&gt;Actions&lt;/strong&gt; 选项卡中可以看到部署进度。完成后，你的博客将运行在 &lt;code&gt;https://&amp;lt;用户名&amp;gt;.github.io/&amp;lt;仓库名&amp;gt;/&lt;/code&gt;。&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;💡 常用命令速查表&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;命令&lt;/th&gt;
&lt;th&gt;作用&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm dev&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;启动本地开发服务器&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm build&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;执行本地构建（生成 &lt;code&gt;dist&lt;/code&gt; 静态文件夹）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm new-post &amp;lt;name&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;快速创建新文章模板&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm format&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;自动格式化美化代码&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;💡 常用Markdown语法速查表&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;语法&lt;/th&gt;
&lt;th&gt;作用&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;==content==&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;高亮&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
</content:encoded></item><item><title>Astro</title><link>https://lxy-alexander.github.io/blog/posts/guide/astro/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/guide/astro/</guid><description>Astro</description><pubDate>Thu, 01 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;指令改造&lt;/h2&gt;
&lt;p&gt;做一个全局命令，比如叫 &lt;code&gt;np&lt;/code&gt;，让它支持：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;np &quot;Double&amp;amp;Triple Pointers&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;① 创建脚本&lt;/p&gt;
&lt;p&gt;新建文件：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mkdir -p ~/.local/bin
~/.local/bin/np
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;内容：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env bash
npm run -- new &quot;$@&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;② 加执行权限&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;chmod +x ~/.local/bin/np
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;③ 确保 PATH 包含它&lt;/p&gt;
&lt;p&gt;把下面加入 &lt;code&gt;~/.bashrc&lt;/code&gt; 或 &lt;code&gt;~/.zshrc&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export PATH=&quot;$HOME/.local/bin:$PATH&quot;
source ~/.zshrc
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;✅ 以后就能用：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;np &quot;Double&amp;amp;Triple Pointers&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;mark/highlight&lt;/h2&gt;
&lt;p&gt;1）安装我们自己插件需要的依赖&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm i unist-util-visit
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2）在你的项目里创建一个文件（路径一定要对）
&lt;code&gt;src/plugins/remark-mark.mjs&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;文件内容完整复制进去：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { visit } from &quot;unist-util-visit&quot;;

export default function remarkMark() {
  return (tree, file) =&amp;gt; {
    visit(tree, &quot;text&quot;, (node, index, parent) =&amp;gt; {
      if (!node.value.includes(&quot;==&quot;)) return;

      const parts = node.value.split(/(==)/g);
      const newNodes = [];
      let inMark = false;

      for (const part of parts) {
        if (part === &quot;==&quot;) {
          if (inMark) {
            newNodes.push({ type: &quot;html&quot;, value: &quot;&amp;lt;/mark&amp;gt;&quot; });
          } else {
            newNodes.push({ type: &quot;html&quot;, value: &quot;&amp;lt;mark&amp;gt;&quot; });
          }
          inMark = !inMark;
        } else if (part.length &amp;gt; 0) {
          newNodes.push({ type: &quot;text&quot;, value: part });
        }
      }

      // 替换当前节点
      parent.children.splice(index, 1, ...newNodes);
      
      // 返回索引，让 visit 继续处理新插入的节点
      return index + newNodes.length;
    });
  };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;3）打开项目根目录的 &lt;code&gt;astro.config.mjs&lt;/code&gt;，在 import 区域加上这一行（一定要放在 &lt;code&gt;export default defineConfig(...)&lt;/code&gt; 之前）&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import remarkMark from &quot;./src/plugins/remark-mark.mjs&quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;4）把 &lt;code&gt;remarkMark&lt;/code&gt; 加进 &lt;code&gt;remarkPlugins&lt;/code&gt;（建议放第一个）&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;markdown: {
  remarkPlugins: [
    remarkMark,
    remarkMath,
    remarkReadingTime,
    remarkExcerpt,
    remarkGithubAdmonitionsToDirectives,
    remarkDirective,
    remarkSectionize,
    parseDirectiveNode,
  ],
  rehypePlugins: [
    // ...
  ],
},
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Typora</title><link>https://lxy-alexander.github.io/blog/posts/guide/typora/</link><guid isPermaLink="true">https://lxy-alexander.github.io/blog/posts/guide/typora/</guid><description>Typora</description><pubDate>Thu, 01 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;typora-0.11.18 last free version&lt;/h2&gt;
&lt;p&gt;https://github.com/wyf9661/typora-free&lt;/p&gt;
&lt;h2&gt;HighLight&lt;/h2&gt;
&lt;p&gt;Setting - Markdown - 扩展语法 - 勾选高亮 - 重启(渲染失效的)&lt;/p&gt;
</content:encoded></item></channel></rss>