<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh-CN">
  <id>https://blog.chuwu.top/</id>
  <title>初五的小窝</title>
  <updated>2026-09-02T03:10:48.648Z</updated>
  <subtitle>ChuwuYo的小窝, 这里有我的日常、思考以及一些开发分享, 欢迎你的到来</subtitle>
  <generator uri="https://github.com/flcdrg/astrojs-atom" version="1.0.162">astrojs-atom</generator>
  <link href="https://blog.chuwu.top/atom.xml" rel="self" type="application/atom+xml"/>
  <link href="https://blog.chuwu.top/" rel="alternate" type="text/html"/>
  <entry>
    <id>https://blog.chuwu.top/posts/sql/mysql8/</id>
    <updated>2026-09-01T00:00:00.000Z</updated>
    <title>MySQL学习笔记 (8)</title>
    <author>
      <name>ChuwuYo</name>
    </author>
    <link href="https://blog.chuwu.top/posts/sql/mysql8/" rel="alternate"/>
    <published>2026-09-01T00:00:00.000Z</published>
    <summary>INSERT、UPDATE、DELETE、视图、事务</summary>
    <content type="html" xml:base="https://blog.chuwu.top/posts/sql/mysql8/">
      <![CDATA[<p>:::note
基于 MySQL 26.7。<a href="https://dev.mysql.com/doc/refman/26.7/en/sql-data-manipulation-statements.html">数据操作</a>
:::</p>
<h2>INSERT INTO</h2>
<p><code>INSERT INTO</code> 语句用于向表中插入新记录。</p>
<pre><code>INSERT INTO employee (emp_id, last_name, dept_no)
VALUES (201, '周七', 'D01');
</code></pre>
<p>一次插入多行：</p>
<pre><code>INSERT INTO employee (emp_id, last_name, dept_no)
VALUES (202, '吴八', 'D01'),
       (203, '郑九', 'D02');
</code></pre>
<p>没有出现在列表里的列会使用默认值，或者是 NULL。</p>
<p>也可以把查询结果插入另一张表：</p>
<pre><code>CREATE TABLE employee_archive (
    emp_id    INTEGER     NOT NULL,
    last_name VARCHAR(40) NOT NULL
);

INSERT INTO employee_archive (emp_id, last_name)
SELECT emp_id, last_name
FROM   employee
WHERE  hire_date &lt; DATE '2020-01-01';
</code></pre>
<h2>UPDATE</h2>
<p><code>UPDATE</code> 语句用于更新表中的记录。</p>
<pre><code>UPDATE employee
SET    salary = salary * 1.05,
       dept_no = 'D02'
WHERE  emp_id = 101;
</code></pre>
<p>注意：省略 <code>WHERE</code> 会更新表中的所有行。</p>
<h2>DELETE</h2>
<p><code>DELETE</code> 语句用于删除表中的记录。</p>
<pre><code>DELETE FROM employee
WHERE  emp_id = 103;
</code></pre>
<p>注意：省略 <code>WHERE</code> 会删除表中的所有行。</p>
<p><code>TRUNCATE TABLE</code> 也会清空表，和逐行 <code>DELETE</code> 不完全一样。</p>
<h2>视图</h2>
<p>视图是基于 SQL 语句的结果集的可视化表。</p>
<pre><code>CREATE VIEW employee_public AS
SELECT emp_id, last_name, dept_no
FROM   employee;

SELECT * FROM employee_public
WHERE  dept_no = 'D01';
</code></pre>
<p>删除视图：</p>
<pre><code>DROP VIEW employee_public;
</code></pre>
<p><code>WITH CHECK OPTION</code> 要求通过视图插入或更新的行，仍然能被该视图查到。带聚合、<code>DISTINCT</code> 或多表的视图往往不能更新。</p>
<h2>事务</h2>
<p>事务把多条语句绑在一起：要么全部成功，要么全部取消。</p>
<pre><code>CREATE TABLE account (
    id      INTEGER        NOT NULL,
    balance NUMERIC(12, 2) NOT NULL,
    CONSTRAINT account_pk PRIMARY KEY (id)
);

INSERT INTO account (id, balance)
VALUES (1, 500), (2, 200);

START TRANSACTION;

UPDATE account SET balance = balance - 100 WHERE id = 1;
UPDATE account SET balance = balance + 100 WHERE id = 2;

COMMIT;
</code></pre>
<ul>
<li><code>START TRANSACTION</code>：开始事务</li>
<li><code>COMMIT</code>：提交</li>
<li><code>ROLLBACK</code>：回滚，撤销还没提交的修改</li>
</ul>
<p>许多客户端默认每条语句自动提交。需要多条语句一起提交时，先执行 <code>START TRANSACTION</code>。</p>
<hr />
<h2>参考</h2>
<ol>
<li><a href="https://dev.mysql.com/doc/refman/26.7/en/insert.html">MySQL 26.7 · INSERT</a></li>
<li><a href="https://dev.mysql.com/doc/refman/26.7/en/update.html">MySQL 26.7 · UPDATE</a></li>
<li><a href="https://dev.mysql.com/doc/refman/26.7/en/delete.html">MySQL 26.7 · DELETE</a></li>
<li><a href="https://dev.mysql.com/doc/refman/26.7/en/create-view.html">MySQL 26.7 · CREATE VIEW</a></li>
<li><a href="https://dev.mysql.com/doc/refman/26.7/en/commit.html">MySQL 26.7 · START TRANSACTION</a></li>
<li><a href="https://www.iso.org/standard/76583.html">ISO/IEC 9075（SQL:2023）</a></li>
</ol>
]]>
    </content>
  </entry>
  <entry>
    <id>https://blog.chuwu.top/posts/python/python8/</id>
    <updated>2026-08-31T00:00:00.000Z</updated>
    <title>Python 札记 (8)</title>
    <author>
      <name>ChuwuYo</name>
    </author>
    <link href="https://blog.chuwu.top/posts/python/python8/" rel="alternate"/>
    <published>2026-08-31T00:00:00.000Z</published>
    <summary>模块与包、输入输出与文件、pathlib、类、类型注解、venv 与标准库地图</summary>
    <content type="html" xml:base="https://blog.chuwu.top/posts/python/python8/">
      <![CDATA[<p>:::note
基于 Python 3。
:::</p>
<h2>import</h2>
<pre><code>import math
print(math.sqrt(2))

from math import sqrt, pi
print(sqrt(2), pi)

import datetime as dt
</code></pre>
<p>模块是一个 <code>.py</code> 文件。<code>import math</code> 之后通过 <code>math.sqrt</code> 访问。<code>from math import sqrt</code> 将 <code>sqrt</code> 导入当前命名空间。<code>as</code> 用于指定别名。</p>
<p>同一个模块只执行顶层代码一次，结果缓存在 <code>sys.modules</code> 里。</p>
<p><code>from module import *</code> 会导入模块中的全部名字，不建议使用。</p>
<p>命令行参数在 <code>sys.argv</code>：</p>
<pre><code>import sys
print(sys.argv)  # [脚本路径, 后续参数...]
</code></pre>
<h2>包与 <strong>init</strong>.py</h2>
<p>含 <a href="https://docs.python.org/3/reference/import.html#regular-packages"><code>__init__.py</code></a> 的目录是包，可以 <code>import pkg.mod</code>（<a href="https://docs.python.org/3/tutorial/modules.html#packages">Packages</a>）。<code>__init__.py</code> 可以为空。包内可用相对导入：<code>from . import echo</code>。</p>
<p>没有 <code>__init__.py</code> 的目录也可能作为 <a href="https://peps.python.org/pep-0420/">namespace package</a>（PEP 420）。</p>
<pre><code>sound/
    __init__.py
    effects/
        __init__.py
        echo.py
</code></pre>
<p><code>from sound.effects import echo</code> 后使用 <code>echo.surround()</code>。包内子模块也是完整模块名：<code>sound.effects.echo</code>。</p>
<p>打包发布见 <a href="https://packaging.python.org/"><code>pyproject.toml</code></a>。</p>
<h2>if <strong>name</strong> == '<strong>main</strong>'</h2>
<p>脚本直接运行时，<a href="https://docs.python.org/3/library/__main__.html"><code>__name__</code></a> 为 <code>'__main__'</code>；被 <code>import</code> 时为模块名（如 <code>'mymodule'</code>）。常见写法：</p>
<pre><code>def main():
    ...

if __name__ == "__main__":
    main()
</code></pre>
<p><code>import mymodule</code> 不执行 <code>main()</code>；<code>python mymodule.py</code> 会执行。也可用 <code>python -m pkg.module</code>（<a href="https://docs.python.org/3/library/runpy.html"><code>-m</code> 搜索</a>）。</p>
<h2>print 与 input</h2>
<p><code>print</code> 可以把对象写到文件：<code>print(x, file=f)</code>。<code>input(prompt='')</code> 从标准输入读一行，<strong>返回 str</strong>（去掉末尾换行），数值运算再 <code>int()</code> / <code>float()</code>。</p>
<p><a href="https://docs.python.org/3/library/pprint.html"><code>pprint</code></a> 用于格式化输出嵌套结构。<a href="https://docs.python.org/3/library/json.html#json.dumps"><code>json.dumps</code></a> 用于输出 JSON。字符串格式化见 f-string / <code>str.format</code>。</p>
<h2>open 读写与 with</h2>
<p><a href="https://docs.python.org/3/library/functions.html#open"><code>open(file, mode='r', encoding=None, ... )</code></a> 返回 <a href="https://docs.python.org/3/library/io.html">text 或 binary 流</a>。文本模式默认编码依平台，通常显式传 <code>encoding='utf-8'</code>（<a href="https://docs.python.org/3/library/functions.html#open">UTF-8 模式</a> 在 3.7+ 可通过 <code>-X utf8</code> 或 <code>PYTHONUTF8</code> 影响默认行为）。</p>
<p>常用模式：<code>'r'</code> 读、<code>'w'</code> 写（清空）、<code>'a'</code> 追加、<code>'x'</code> 独占创建、<code>'r+'</code> 读写；加 <code>'b'</code> 为二进制（<code>encoding</code> 无效）。</p>
<pre><code>with open("notes.txt", "w", encoding="utf-8") as f:
    f.write("第一行\n")
    f.writelines(["第二行\n", "第三行\n"])

with open("notes.txt", "r", encoding="utf-8") as f:
    whole = f.read()       # 整个文件 str
    # 或 for line in f:   # 逐行，省内存
    # 或 lines = f.readlines()
</code></pre>
<p>文件对象实现上下文管理器，离开 <code>with</code> 时会调用 <code>close()</code>。不使用 <code>with</code> 时需要调用 <code>f.close()</code>。</p>
<p>二进制：<code>with open("img.bin", "rb") as f: data = f.read()</code>。CSV、JSON 见 <a href="https://docs.python.org/3/library/csv.html"><code>csv</code></a>、<a href="https://docs.python.org/3/library/json.html"><code>json</code></a>。</p>
<p>文本流常用方法见 <a href="https://docs.python.org/3/library/io.html#io.IOBase">io.IOBase</a>：</p>
<table>
<thead>
<tr>
<th>方法</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>read</code> / <code>readline</code> / <code>readlines</code></td>
<td>读</td>
</tr>
<tr>
<td><code>write</code> / <code>writelines</code></td>
<td>写</td>
</tr>
<tr>
<td><code>seek(offset, whence=0)</code></td>
<td>移动文件指针；<code>whence</code> 0 开头、1 当前、2 末尾</td>
</tr>
<tr>
<td><code>tell()</code></td>
<td>当前指针位置</td>
</tr>
<tr>
<td><code>flush()</code></td>
<td>刷缓冲区</td>
</tr>
<tr>
<td><code>close()</code></td>
<td>关闭（<code>with</code> 会自动调）</td>
</tr>
</tbody>
</table>
<p><code>'w+'</code> 写完后若要读回去，须先 <code>seek(0)</code>。</p>
<h2>路径：pathlib 与 os.path</h2>
<p><a href="https://docs.python.org/3/library/pathlib.html">pathlib</a>（3.4+）用面向对象路径替代字符串拼接。常用 <a href="https://docs.python.org/3/library/pathlib.html#pathlib.Path"><code>Path</code></a>：</p>
<pre><code>from pathlib import Path

p = Path("data") / "logs" / "app.log"  # 跨平台拼接
p.parent.mkdir(parents=True, exist_ok=True)
p.write_text("hello\n", encoding="utf-8")
text = p.read_text(encoding="utf-8")
print(p.suffix, p.stem, p.resolve())
</code></pre>
<p>常用：<code>exists()</code>、<code>is_file()</code>、<code>is_dir()</code>、<code>glob()</code>、<code>iterdir()</code>、<code>rename()</code>、<code>unlink()</code>（删文件）。<a href="https://docs.python.org/3/library/pathlib.html#pathlib.PurePath"><code>PurePath</code></a> 不做 I/O，只算路径。</p>
<p><a href="https://docs.python.org/3/library/os.path.html"><code>os.path</code></a> 提供基于字符串的路径操作：<code>os.path.join</code>、<code>dirname</code>、<code>basename</code>、<code>abspath</code>、<code>splitext</code>。可与 pathlib 互转：<code>Path(p)</code>、<code>str(path)</code>。获取文件状态可使用 <code>Path.stat()</code> 或 <a href="https://docs.python.org/3/library/os.html#os.stat"><code>os.stat</code></a>。</p>
<p>当前工作目录：<a href="https://docs.python.org/3/library/pathlib.html#pathlib.Path.cwd"><code>Path.cwd()</code></a>；改变用 <a href="https://docs.python.org/3/library/os.html#os.chdir"><code>os.chdir</code></a>（改变进程全局 cwd）。</p>
<h2>os 常用</h2>
<p><a href="https://docs.python.org/3/library/os.html"><code>os</code></a> 提供操作系统接口；与路径无关的常见调用：</p>
<ul>
<li><strong><code>os.environ</code></strong>：环境变量映射，可读可写；取值也可用 <a href="https://docs.python.org/3/library/os.html#os.getenv"><code>os.getenv</code></a>。</li>
<li><strong><code>os.listdir(path)</code></strong> / <strong><code>os.scandir(path)</code></strong>：列目录（后者更高效，可拿类型信息）。</li>
<li><strong><code>os.mkdir</code> / <code>os.makedirs</code></strong>：建目录；<code>makedirs(..., exist_ok=True)</code> 类似 <code>Path.mkdir</code>。</li>
<li><strong><code>os.walk(top)</code></strong>：递归遍历目录树（<a href="https://docs.python.org/3/library/os.html#os.walk">os.walk</a>）。</li>
<li><strong><code>os.replace</code></strong>：覆盖式重命名，跨平台比 <code>rename</code> 稳。</li>
<li><strong><code>os.getpid()</code></strong>、<strong><code>os.cpu_count()</code></strong>：进程 id、CPU 数。</li>
</ul>
<p><a href="https://docs.python.org/3/library/pathlib.html"><code>pathlib</code></a> 与 <code>os.environ</code> 常一起使用。子进程见 <a href="https://docs.python.org/3/library/subprocess.html"><code>subprocess</code></a>。</p>
<h2>class</h2>
<p><a href="https://docs.python.org/3/tutorial/classes.html">Classes</a>：使用 <code>class</code> 定义类。实例属性写在 <code>self</code> 上。方法的第一个参数约定为 <code>self</code>，调用时自动传入。</p>
<pre><code>class Dog:
    kind = "canine"  # 类属性，共享

    def __init__(self, name):
        self.name = name  # 实例属性

    def bark(self):
        return f"{self.name}: woof!"

d = Dog("Rex")
print(d.bark(), Dog.kind)
</code></pre>
<p><a href="https://docs.python.org/3/reference/datamodel.html#object.__init__"><code>__init__</code></a> 在实例创建后初始化，不是分配内存的构造器（那是 <code>__new__</code>）。<a href="https://docs.python.org/3/reference/datamodel.html#object.__repr__"><code>__repr__</code></a> / <a href="https://docs.python.org/3/reference/datamodel.html#object.__str__"><code>__str__</code></a> 控制 <code>repr(obj)</code> 与 <code>str(obj)</code> / <code>print(obj)</code>。</p>
<p><strong>继承</strong>：</p>
<pre><code>class Animal:
    def __init__(self, name):
        self.name = name

class Cat(Animal):
    def __init__(self, name, indoor=True):
        super().__init__(name)  # 调父类 __init__
        self.indoor = indoor
</code></pre>
<p><a href="https://docs.python.org/3/library/functions.html#super"><code>super()</code></a> 在单继承里找父类实现；多继承按 <a href="https://docs.python.org/3/tutorial/classes.html#multiple-inheritance">MRO</a>（方法解析顺序）线性化。</p>
<p><strong>数据 vs 方法</strong>：Python 不强制访问控制。约定 <code>_name</code> 表示内部使用；<a href="https://docs.python.org/3/tutorial/classes.html#private-variables"><code>__name</code></a> 触发名称改写（name mangling），改写后仍可通过 <code>_类名__属性</code> 访问。<a href="https://docs.python.org/3/library/functions.html#property"><code>@property</code></a> 把方法当属性读；<a href="https://docs.python.org/3/library/functions.html#classmethod"><code>@classmethod</code></a> / <a href="https://docs.python.org/3/library/functions.html#staticmethod"><code>@staticmethod</code></a> 见 <a href="https://docs.python.org/3/tutorial/classes.html#class-and-instance-variables">Class and Instance Variables</a>。</p>
<p>类本身也是对象，可传参、可放入容器，见 <a href="https://docs.python.org/3/tutorial/classes.html#a-word-about-names-and-objects">Odds and Ends</a>。</p>
<h2>类型注解</h2>
<p><a href="https://peps.python.org/pep-0484/">PEP 484</a> 函数注解、<a href="https://peps.python.org/pep-0526/">PEP 526</a> 变量注解、<a href="https://peps.python.org/pep-0585/">PEP 585</a> 内置泛型 <code>list[int]</code>、<a href="https://peps.python.org/pep-0604/">PEP 604</a> 的 <code>X | Y</code>（3.10+）。运行时默认不强制检查；静态检查可用 <a href="https://mypy.readthedocs.io/">mypy</a>。</p>
<pre><code>def greet(name: str, times: int = 1) -&gt; str:
    return (name + "! ") * times

def sum_all(nums: list[float]) -&gt; float:
    return sum(nums)

from typing import Optional

def find(items: list[str], key: str) -&gt; Optional[str]:
    return key if key in items else None
</code></pre>
<p>常用 <a href="https://docs.python.org/3/library/typing.html"><code>typing</code></a> 名字：</p>
<ul>
<li><strong><code>Optional[T]</code></strong> — 等价于 <code>T | None</code>（3.10+ 也可写 <a href="https://docs.python.org/3/library/typing.html#typing.Union"><code>Union</code></a> 或 <code>|</code>）。</li>
<li><strong><code>Union[A, B]</code></strong> / <strong><code>A | B</code></strong> — 多种类型之一。</li>
<li><strong><code>list[int]</code>、<code>dict[str, int]</code></strong> — 泛型容器（3.9+ 内置泛型；旧代码见 <code>List</code>、<code>Dict</code> from typing）。</li>
<li><strong><code>Callable[[int, str], bool]</code></strong> — 可调用签名。</li>
<li><strong><code>Iterable[T]</code>、<code>Sequence[T]</code></strong> — 只读迭代 / 序列抽象。</li>
<li><strong><code>TypedDict</code>、<code>Protocol</code>、<code>NamedTuple</code></strong> — 结构化数据。</li>
</ul>
<p>变量也可注解：<code>count: int = 0</code>。类属性：<code>class User: id: int; name: str</code>。延迟解析字符串注解：<a href="https://docs.python.org/3/__future__.html"><code>from __future__ import annotations</code></a>（3.7+；3.11+ 部分行为默认变化见 <a href="https://docs.python.org/3/whatsnew/3.11.html">What's New</a>）。</p>
<p><a href="https://docs.python.org/3/library/typing.html#typing.get_type_hints"><code>typing.get_type_hints()</code></a> 可在运行时取注解。</p>
<h2>venv：虚拟环境</h2>
<p><a href="https://docs.python.org/3/library/venv.html"><code>venv</code></a> 创建轻量隔离环境，各自拥有 <code>python</code> 与 <code>pip</code>（或 <a href="https://docs.python.org/3/library/ensurepip.html"><code>ensurepip</code></a> 引导），包安装不污染系统 Python。</p>
<pre><code>python -m venv .venv
# Windows PowerShell:
.\.venv\Scripts\Activate.ps1
# Unix / macOS:
source .venv/bin/activate
</code></pre>
<p>激活后 <code>which python</code> / <code>where python</code> 应指向 <code>.venv</code>。安装依赖：<code>python -m pip install requests</code>。冻结清单：<code>python -m pip freeze &gt; requirements.txt</code>，再 <code>python -m pip install -r requirements.txt</code>。退出：<code>deactivate</code>。指定解释器版本：<code>python3.12 -m venv .venv</code>。</p>
<p>项目根常见 <code>.venv/</code> 加入 <code>.gitignore</code>；依赖列表用 <code>requirements.txt</code> 或 <code>pyproject.toml</code>。</p>
<p><a href="https://virtualenv.pypa.io/"><code>virtualenv</code></a> 是第三方替代；<a href="https://docs.conda.io/"><code>conda</code></a> 是另一套生态。教程：<a href="https://docs.python.org/3/tutorial/venv.html">Virtual Environments and Packages</a>。</p>
<h2>标准库地图</h2>
<p><a href="https://docs.python.org/3/tutorial/stdlib.html">Brief Tour of the Standard Library</a> 与 <a href="https://docs.python.org/3/library/index.html">stdlib index</a> 是总入口。常用模块：</p>
<table>
<thead>
<tr>
<th>模块</th>
<th>一句话</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>os</strong></td>
<td>操作系统接口：环境变量、进程 id、目录项、部分路径与权限原语。</td>
</tr>
<tr>
<td><strong>sys</strong></td>
<td>解释器相关：<code>argv</code>、<code>path</code>、<code>stdin/stdout/stderr</code>、版本与退出。</td>
</tr>
<tr>
<td><strong>json</strong></td>
<td>JSON 与 Python 对象互转（<code>dumps</code> / <code>loads</code> / <code>dump</code> / <code>load</code>），非 Python 专有类型需默认处理。</td>
</tr>
<tr>
<td><strong>datetime</strong></td>
<td>日期与时间：<code>date</code>、<code>time</code>、<code>datetime</code>、<code>timedelta</code>，时区见 <a href="https://docs.python.org/3/library/zoneinfo.html"><code>zoneinfo</code></a>（3.9+）。</td>
</tr>
<tr>
<td><strong>pathlib</strong></td>
<td>面向对象的文件系统路径与常见 I/O 快捷方法。</td>
</tr>
<tr>
<td><strong>collections</strong></td>
<td>专用容器：<code>Counter</code>、<code>deque</code>、<code>defaultdict</code>、<code>namedtuple</code> 等。</td>
</tr>
<tr>
<td><strong>itertools</strong></td>
<td>迭代器代数：无穷序列、组合、链式、分组（惰性、省内存）。</td>
</tr>
<tr>
<td><strong>functools</strong></td>
<td>高阶函数：<code>partial</code>、<code>reduce</code>、<code>lru_cache</code>、<code>wraps</code>（装饰器元数据）。</td>
</tr>
<tr>
<td><strong>shutil</strong></td>
<td>高层文件操作：<code>copyfile</code>、<code>move</code>、<code>rmtree</code>。</td>
</tr>
<tr>
<td><strong>glob</strong></td>
<td><code>glob.glob('*.py')</code> 按通配符列文件。</td>
</tr>
<tr>
<td><strong>argparse</strong></td>
<td>命令行参数解析。</td>
</tr>
</tbody>
</table>
<p>还常会用到：<code>re</code>、<code>urllib</code> / <code>http</code>、<code>sqlite3</code>、<code>logging</code>、<code>unittest</code>。</p>
<hr />
<h2>附录：import 形式速查</h2>
<table>
<thead>
<tr>
<th>语句</th>
<th>效果</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>import mod</code></td>
<td>绑定 <code>mod</code> 模块对象</td>
</tr>
<tr>
<td><code>import mod as m</code></td>
<td>绑定别名 <code>m</code></td>
</tr>
<tr>
<td><code>from mod import a, b</code></td>
<td>把 <code>a</code>、<code>b</code> 放入当前命名空间</td>
</tr>
<tr>
<td><code>from mod import a as x</code></td>
<td>别名导入</td>
</tr>
<tr>
<td><code>from mod import *</code></td>
<td>导入 <code>__all__</code> 或公开名字（不推荐）</td>
</tr>
<tr>
<td><code>from pkg.sub import obj</code></td>
<td>导入子模块或子模块中的名字</td>
</tr>
<tr>
<td><code>from . import sibling</code></td>
<td>包内相对导入</td>
</tr>
<tr>
<td><code>importlib.import_module('mod')</code></td>
<td>动态导入（<a href="https://docs.python.org/3/library/importlib.html"><code>importlib</code></a>）</td>
</tr>
</tbody>
</table>
<hr />
<h2>附录：open 模式与 pathlib 对照</h2>
<table>
<thead>
<tr>
<th>需求</th>
<th>open</th>
<th>pathlib（3.11+ 部分 API 更早）</th>
</tr>
</thead>
<tbody>
<tr>
<td>读文本</td>
<td><code>open(p, encoding='utf-8').read()</code></td>
<td><code>Path(p).read_text(encoding='utf-8')</code></td>
</tr>
<tr>
<td>写文本</td>
<td><code>open(p, 'w', encoding='utf-8').write(s)</code></td>
<td><code>Path(p).write_text(s, encoding='utf-8')</code></td>
</tr>
<tr>
<td>读字节</td>
<td><code>open(p, 'rb').read()</code></td>
<td><code>Path(p).read_bytes()</code></td>
</tr>
<tr>
<td>拼接路径</td>
<td><code>os.path.join(a, b)</code></td>
<td><code>Path(a) / b</code></td>
</tr>
<tr>
<td>是否存在</td>
<td><code>os.path.exists(p)</code></td>
<td><code>Path(p).exists()</code></td>
</tr>
<tr>
<td>列目录</td>
<td><code>os.listdir(d)</code></td>
<td><code>Path(d).iterdir()</code></td>
</tr>
</tbody>
</table>
<hr />
<h2>附录：typing 常用符号（3.10+ 风格）</h2>
<table>
<thead>
<tr>
<th>写法</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>x: int</code></td>
<td>变量为 int</td>
</tr>
<tr>
<td><code>def f() -&gt; None</code></td>
<td>无返回值</td>
</tr>
<tr>
<td><code>list[str]</code></td>
<td>字符串列表</td>
</tr>
<tr>
<td><code>dict[str, int]</code></td>
<td>键 str、值 int</td>
</tr>
<tr>
<td><code>tuple[int, ...]</code></td>
<td>整数可变长元组</td>
</tr>
<tr>
<td><code>str | None</code> / <code>Optional[str]</code></td>
<td>可为 None</td>
</tr>
<tr>
<td><code>int | str</code></td>
<td>联合类型</td>
</tr>
<tr>
<td><code>Callable[[int], str]</code></td>
<td>接受 int 返回 str 的可调用</td>
</tr>
<tr>
<td><code>Literal["r", "w"]</code></td>
<td>只能是列出的字面量</td>
</tr>
<tr>
<td><code>TypeVar('T')</code></td>
<td>泛型类型变量</td>
</tr>
</tbody>
</table>
<p>运行时默认不按注解校验对象。</p>
<hr />
<h2>参考</h2>
<p>官方文档（<a href="https://docs.python.org/zh-cn/3/">中文版</a>）：</p>
<ol>
<li><a href="https://docs.python.org/3/tutorial/modules.html">Modules</a> — import、模块搜索路径、包、<code>__init__.py</code>、<code>__name__</code></li>
<li><a href="https://docs.python.org/3/tutorial/inputoutput.html">Input and Output</a> — <code>print</code>、<code>input</code>、格式化、f-string、读写文件 intro</li>
<li><a href="https://docs.python.org/3/tutorial/classes.html">Classes</a> — 属性、方法、继承、<code>super</code>、私有约定</li>
<li><a href="https://docs.python.org/3/tutorial/venv.html">Virtual Environments and Packages</a> — venv 与 pip 工作流</li>
<li><a href="https://docs.python.org/3/tutorial/stdlib.html">Brief Tour of the Standard Library</a> — 标准库导览</li>
<li><a href="https://docs.python.org/3/reference/import.html">The import system</a> — 导入语义、相对导入、namespace package</li>
<li><a href="https://docs.python.org/3/library/pathlib.html">pathlib</a> — <code>Path</code> / <code>PurePath</code></li>
<li><a href="https://docs.python.org/3/library/os.html">os</a> — 操作系统接口</li>
<li><a href="https://docs.python.org/3/library/os.path.html">os.path</a> — 路径字符串函数</li>
<li><a href="https://docs.python.org/3/library/functions.html#open">open()</a> — 文件打开</li>
<li><a href="https://docs.python.org/3/library/io.html">io</a> — 流基类</li>
<li><a href="https://docs.python.org/3/library/typing.html">typing</a> — 类型注解构造</li>
<li><a href="https://docs.python.org/3/library/venv.html">venv</a> — 创建虚拟环境</li>
<li><a href="https://docs.python.org/3/library/json.html">json</a> — JSON</li>
<li><a href="https://docs.python.org/3/library/datetime.html">datetime</a> — 日期时间</li>
<li><a href="https://docs.python.org/3/library/collections.html">collections</a> — 容器</li>
<li><a href="https://docs.python.org/3/library/itertools.html">itertools</a> — 迭代工具</li>
<li><a href="https://docs.python.org/3/library/functools.html">functools</a> — 高阶函数工具</li>
<li><a href="https://docs.python.org/3/library/sys.html">sys</a> — 解释器</li>
<li><a href="https://docs.python.org/3/library/__main__.html"><strong>main</strong></a> — 主模块</li>
</ol>
<p>PEP：</p>
<ol>
<li><a href="https://peps.python.org/pep-0484/">PEP 484 — Type Hints</a></li>
<li><a href="https://peps.python.org/pep-0526/">PEP 526 — Syntax for Variable Annotations</a></li>
<li><a href="https://peps.python.org/pep-0585/">PEP 585 — Type Hinting Generics In Standard Collections</a></li>
<li><a href="https://peps.python.org/pep-0604/">PEP 604 — Allow writing union types as X | Y</a></li>
<li><a href="https://peps.python.org/pep-0420/">PEP 420 — Implicit Namespace Packages</a></li>
<li><a href="https://peps.python.org/pep-0008/">PEP 8 — Style Guide</a> — import 顺序等</li>
</ol>
<p>菜鸟教程：</p>
<ol>
<li><a href="https://www.runoob.com/python3/python3-module.html">菜鸟教程 · Python3 模块</a></li>
<li><a href="https://www.runoob.com/python3/python3-inputoutput.html">菜鸟教程 · Python3 输入和输出</a></li>
<li><a href="https://www.runoob.com/python3/python3-file-methods.html">菜鸟教程 · Python3 文件方法</a></li>
<li><a href="https://www.runoob.com/python3/python3-os-file-methods.html">菜鸟教程 · OS 文件/目录</a></li>
<li><a href="https://www.runoob.com/python3/python3-class.html">菜鸟教程 · Python3 面向对象</a></li>
<li><a href="https://www.runoob.com/python/python-venv.html">菜鸟教程 · 虚拟环境</a></li>
<li><a href="https://www.runoob.com/python3/python-type-hints.html">菜鸟教程 · 类型注解</a></li>
<li><a href="https://www.runoob.com/python3/python3-stdlib.html">菜鸟教程 · 标准库概览</a></li>
</ol>
<hr />
]]>
    </content>
  </entry>
  <entry>
    <id>https://blog.chuwu.top/posts/sql/mysql7/</id>
    <updated>2026-08-31T00:00:00.000Z</updated>
    <title>MySQL学习笔记 (7)</title>
    <author>
      <name>ChuwuYo</name>
    </author>
    <link href="https://blog.chuwu.top/posts/sql/mysql7/" rel="alternate"/>
    <published>2026-08-31T00:00:00.000Z</published>
    <summary>JOIN、UNION、子查询、EXISTS</summary>
    <content type="html" xml:base="https://blog.chuwu.top/posts/sql/mysql7/">
      <![CDATA[<p>:::note
基于 MySQL 26.7。<a href="https://dev.mysql.com/doc/refman/26.7/en/join.html">JOIN</a>
:::</p>
<h2>JOIN</h2>
<p><code>JOIN</code> 用于把两个或多个表的行结合起来。</p>
<p>最常见的是 <code>INNER JOIN</code>：只返回两个表中匹配的行。</p>
<pre><code>SELECT e.last_name, d.dept_name
FROM   employee AS e
INNER  JOIN department AS d
       ON e.dept_no = d.dept_no;
</code></pre>
<p>连接条件写在 <code>ON</code> 后面。两表列名相同时，也可以用 <code>USING (dept_no)</code>。</p>
<p>列名冲突时要写成 <code>表名.列名</code> 或 <code>别名.列名</code>。</p>
<h2>LEFT JOIN / RIGHT JOIN</h2>
<p><code>LEFT JOIN</code> 返回左表的全部行。右表没有匹配时，右表的列是 NULL。</p>
<pre><code>SELECT e.last_name, d.dept_name
FROM   employee AS e
LEFT   JOIN department AS d
       ON e.dept_no = d.dept_no;
</code></pre>
<p><code>RIGHT JOIN</code> 返回右表的全部行。左表没有匹配时，左表的列是 NULL。</p>
<p><code>OUTER</code> 可以省略：<code>LEFT OUTER JOIN</code> 和 <code>LEFT JOIN</code> 相同。</p>
<h2>CROSS JOIN</h2>
<p><code>CROSS JOIN</code> 返回两个表的笛卡尔积，也就是每行与另一表的每一行组合。</p>
<pre><code>SELECT *
FROM employee
CROSS JOIN department;
</code></pre>
<p><code>CROSS JOIN</code> 不写 <code>ON</code>。</p>
<h2>UNION</h2>
<p><code>UNION</code> 用于合并两个 <code>SELECT</code> 的结果。</p>
<pre><code>SELECT dept_no FROM employee
UNION
SELECT dept_no FROM department;
</code></pre>
<ul>
<li><code>UNION</code>：去掉重复行</li>
<li><code>UNION ALL</code>：保留重复行</li>
</ul>
<p>两边选出的列数要相同。</p>
<p><code>ORDER BY</code> 写在整个 <code>UNION</code> 的最后。</p>
<p>还可以用 <code>INTERSECT</code>（交集）和 <code>EXCEPT</code>（差集）：</p>
<pre><code>SELECT dept_no FROM employee
INTERSECT
SELECT dept_no FROM department;

SELECT dept_no FROM employee
EXCEPT
SELECT dept_no FROM department;
</code></pre>
<h2>子查询</h2>
<p>子查询是嵌套在另一条语句里的查询。</p>
<p>下面找出工资高于平均工资的员工：</p>
<pre><code>SELECT last_name
FROM   employee
WHERE  salary &gt; (SELECT AVG(salary) FROM employee);
</code></pre>
<p><code>EXISTS</code> 判断子查询是否返回了行：</p>
<pre><code>SELECT last_name
FROM   employee AS e
WHERE  EXISTS (
    SELECT *
    FROM   department AS d
    WHERE  d.dept_no = e.dept_no
    AND    d.director = '院长'
);
</code></pre>
<p><code>FROM</code> 后面的子查询必须起别名：</p>
<pre><code>SELECT dept_no, n
FROM (
    SELECT dept_no, COUNT(*) AS n
    FROM   employee
    GROUP  BY dept_no
) AS t
WHERE n &gt;= 2;
</code></pre>
<hr />
<h2>参考</h2>
<ol>
<li><a href="https://dev.mysql.com/doc/refman/26.7/en/join.html">MySQL 26.7 · JOIN</a></li>
<li><a href="https://dev.mysql.com/doc/refman/26.7/en/set-operations.html">MySQL 26.7 · Set Operations</a></li>
<li><a href="https://dev.mysql.com/doc/refman/26.7/en/subqueries.html">MySQL 26.7 · Subqueries</a></li>
<li><a href="https://dev.mysql.com/doc/refman/26.7/en/exists-and-not-exists-subqueries.html">MySQL 26.7 · EXISTS</a></li>
<li><a href="https://www.iso.org/standard/76583.html">ISO/IEC 9075（SQL:2023）</a></li>
</ol>
]]>
    </content>
  </entry>
  <entry>
    <id>https://blog.chuwu.top/posts/python/python7/</id>
    <updated>2026-08-30T00:00:00.000Z</updated>
    <title>Python 札记 (7)</title>
    <author>
      <name>ChuwuYo</name>
    </author>
    <link href="https://blog.chuwu.top/posts/python/python7/" rel="alternate"/>
    <published>2026-08-30T00:00:00.000Z</published>
    <summary>函数、参数、作用域、异常、with、迭代器与生成器、装饰器</summary>
    <content type="html" xml:base="https://blog.chuwu.top/posts/python/python7/">
      <![CDATA[<p>:::note
基于 Python 3。
:::</p>
<h2>定义函数 def</h2>
<pre><code>def greet(name):
    """向 name 问好。"""
    return f"Hello, {name}!"

print(greet("Alice"))
print(greet.__name__)  # greet
</code></pre>
<p><a href="https://docs.python.org/3/reference/compound_stmts.html#function-definitions"><code>def</code></a> 用于定义函数。函数体在调用时执行。函数可以作为变量、参数和返回值。</p>
<p>函数可以 <code>return</code> 多个值（实际返回一个元组），也可以没有 <code>return</code>（等价于 <code>return None</code>）。<a href="https://docs.python.org/3/tutorial/controlflow.html#defining-functions">Defining Functions</a></p>
<h2>参数</h2>
<p>位置参数按定义顺序传入，也可以按名字传：<code>greet(name="Alice")</code>。带默认值的可以省略（<a href="https://docs.python.org/3/tutorial/controlflow.html#defining-functions">Defining Functions</a>）：</p>
<pre><code>def greet(name, greeting="Hello"):
    return f"{greeting}, {name}!"

print(greet("Alice"))
print(greet("Bob", greeting="Hi"))
</code></pre>
<p>默认值在<strong>定义时</strong>只求值一次。默认值不要使用可变对象（<code>[]</code>、<code>{}</code>），多次调用会共享同一个对象：</p>
<pre><code>def good(a, items=None):
    if items is None:
        items = []
    items.append(a)
    return items
</code></pre>
<p><code>*args</code> 收集多余的位置参数为元组，<code>**kwargs</code> 收集多余的关键字参数为字典：</p>
<pre><code>def demo(a, *args, **kwargs):
    print(a, args, kwargs)

demo(1, 2, 3, x=4)  # 1 (2, 3) {'x': 4}
print(*(1, 2, 3))   # 等同于 print(1, 2, 3)
</code></pre>
<p><code>/</code> 之前的参数只能按位置传入（3.8+），<code>*</code> 之后的只能按关键字传入，例如 <code>def f(pos, /, both, *, kwonly)</code>。</p>
<h2>return</h2>
<p><a href="https://docs.python.org/3/reference/simple_stmts.html#return"><code>return</code></a> 结束当前函数调用，并返回表达式的值。没有表达式时返回 <code>None</code>。</p>
<pre><code>def find_first(pred, items):
    for x in items:
        if pred(x):
            return x
    return None
</code></pre>
<h2>lambda</h2>
<p><a href="https://docs.python.org/3/reference/expressions.html#lambda"><code>lambda</code></a> 创建匿名函数，语法为 <code>lambda 参数: 表达式</code>。函数体只能是一个表达式。较复杂的逻辑使用 <code>def</code>。</p>
<pre><code>pairs = [(2, "b"), (1, "a"), (3, "c")]
pairs.sort(key=lambda p: p[0])
</code></pre>
<h2>命名空间与作用域</h2>
<p>变量按 <strong>LEGB</strong> 顺序查找：<strong>L</strong>ocal（当前函数）、<strong>E</strong>nclosing（外层函数）、<strong>G</strong>lobal（模块全局）、<strong>B</strong>uilt-in（内置名，如 <code>len</code>）。<a href="https://docs.python.org/3/reference/executionmodel.html#naming-and-binding">命名与绑定</a></p>
<pre><code>x = "global"

def outer():
    x = "enclosing"

    def inner():
        x = "local"
        print(x)  # local

    inner()
    print(x)  # enclosing

outer()
print(x)  # global
</code></pre>
<p>函数定义时会捕获外层作用域（闭包）。内层函数可以读取外层变量；若要给外层非全局变量赋值，使用 <code>nonlocal</code>；给模块级变量赋值使用 <code>global</code>。</p>
<p><code>if</code> / <code>for</code> / <code>while</code> / <code>try</code> 不会像函数那样创建局部作用域，块中赋值的变量在块外仍可使用。<a href="https://docs.python.org/3/reference/executionmodel.html#naming-and-binding">Naming and binding</a>。</p>
<p>内置名在 <code>builtins</code> 模块：<code>import builtins; dir(builtins)</code>。</p>
<h2>global 与 nonlocal</h2>
<p><a href="https://docs.python.org/3/reference/simple_stmts.html#the-global-statement"><code>global</code></a> 把本函数里的名字标成<strong>模块全局</strong>。<a href="https://docs.python.org/3/reference/simple_stmts.html#the-nonlocal-statement"><code>nonlocal</code></a> 标成<strong>最近一层外层函数</strong>里的名字（不能是全局）。</p>
<p>函数体内只要出现对某名字的赋值，整个函数都把它当局部。因此下面会 <code>UnboundLocalError</code>：</p>
<pre><code>x = 1

def f():
    print(x)  # 想读全局 x
    x = 2     # 这一行让 x 在整个 f 里都是局部
</code></pre>
<p>要改全局就写 <code>global x</code>，或不要在函数里给同名赋值。</p>
<pre><code>count = 0

def inc_global():
    global count
    count += 1

def make_counter():
    n = 0

    def inc():
        nonlocal n
        n += 1
        return n

    return inc

c = make_counter()
print(c(), c())  # 1, 2
inc_global()
print(count)     # 1
</code></pre>
<p><code>global</code> 会让数据流难追踪；状态用参数、返回值或类封装。</p>
<h2>try / except / else / finally</h2>
<p><a href="https://docs.python.org/3/reference/compound_stmts.html#the-try-statement">The try statement</a> 与 <a href="https://docs.python.org/3/tutorial/errors.html">Errors and Exceptions</a>：</p>
<pre><code>try:
    risky()
except ValueError as e:
    handle(e)
except (TypeError, OSError):
    recover()
else:
    # 仅当 try 块未抛出异常时执行
    on_success()
finally:
    # 总是执行（除非进程被杀死等）
    cleanup()
</code></pre>
<ul>
<li><strong><code>except</code></strong>：匹配异常类型（元组表示多个）。<code>as e</code> 绑定异常实例；在 Python 3 里，<code>except</code> 块结束后 <code>e</code> 会被删除，避免循环引用（<a href="https://peps.python.org/pep-3110/">PEP 3110</a>）。</li>
<li><strong><code>else</code></strong>：没有异常时运行，常把可能抛错的逻辑放在 <code>try</code>、成功路径放在 <code>else</code>，避免把 <code>else</code> 里的错误当成 <code>except</code> 能处理的同一类问题。</li>
<li><strong><code>finally</code></strong>：用于释放资源；若有 <code>return</code>，<code>finally</code> 仍会在返回前执行。</li>
<li><strong>顺序</strong>：<code>try</code> → 若异常则匹配的 <code>except</code> → 无异常则 <code>else</code> → 始终 <code>finally</code>。</li>
</ul>
<p><a href="https://docs.python.org/3/reference/simple_stmts.html#raise"><code>raise</code></a> 抛出异常：<code>raise ValueError("msg")</code>；<code>raise</code> 单独使用则重新抛出当前异常（常在 <code>except</code> 里记录后再抛）。</p>
<pre><code>def parse_int(s):
    try:
        return int(s)
    except ValueError:
        raise ValueError(f"不是整数: {s!r}") from None  # 抑制异常链
</code></pre>
<p><a href="https://docs.python.org/3/tutorial/errors.html#exception-chaining"><code>Exception chaining</code></a>：内层 <code>raise ... from e</code> 保留原因；<code>from None</code> 隐藏上下文。</p>
<p>用户自定义异常：继承 <a href="https://docs.python.org/3/library/exceptions.html#Exception"><code>Exception</code></a>（不要直接继承 <a href="https://docs.python.org/3/library/exceptions.html#BaseException"><code>BaseException</code></a>，除非做系统退出类异常）。</p>
<pre><code>class MyAppError(Exception):
    pass
</code></pre>
<p><a href="https://docs.python.org/3/reference/simple_stmts.html#assert"><code>assert</code></a> 用于断言。使用 <code>-O</code> 优化时会被忽略，不适合作为常规错误处理。</p>
<h2>内置异常</h2>
<p>下列名字来自 <a href="https://docs.python.org/3/library/exceptions.html">Built-in Exceptions</a>。</p>
<table>
<thead>
<tr>
<th>异常</th>
<th>典型触发场景</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Exception</code></td>
<td>几乎所有用户可捕获异常的基类</td>
</tr>
<tr>
<td><code>StopIteration</code></td>
<td>迭代器没有更多元素（<code>next</code> / <code>for</code> 结束）</td>
</tr>
<tr>
<td><code>StopAsyncIteration</code></td>
<td>异步迭代器结束</td>
</tr>
<tr>
<td><code>ArithmeticError</code></td>
<td>算术错误分支的基类</td>
</tr>
<tr>
<td><code>FloatingPointError</code></td>
<td>浮点运算失败（极少见，依赖平台）</td>
</tr>
<tr>
<td><code>OverflowError</code></td>
<td>数值结果太大</td>
</tr>
<tr>
<td><code>ZeroDivisionError</code></td>
<td>除法或取模除零</td>
</tr>
<tr>
<td><code>AssertionError</code></td>
<td><code>assert</code> 失败</td>
</tr>
<tr>
<td><code>AttributeError</code></td>
<td>属性不存在或无法设置</td>
</tr>
<tr>
<td><code>EOFError</code></td>
<td><code>input()</code> 在无数据可读时（如 Ctrl+D）</td>
</tr>
<tr>
<td><code>ImportError</code></td>
<td><code>import</code> 失败</td>
</tr>
<tr>
<td><code>ModuleNotFoundError</code></td>
<td>找不到模块（<code>ImportError</code> 子类）</td>
</tr>
<tr>
<td><code>LookupError</code></td>
<td>索引/键错误的基类</td>
</tr>
<tr>
<td><code>IndexError</code></td>
<td>序列下标越界</td>
</tr>
<tr>
<td><code>KeyError</code></td>
<td>字典键不存在</td>
</tr>
<tr>
<td><code>MemoryError</code></td>
<td>内存不足</td>
</tr>
<tr>
<td><code>NameError</code></td>
<td>未定义的名字</td>
</tr>
<tr>
<td><code>UnboundLocalError</code></td>
<td>局部名字在赋值前被引用</td>
</tr>
<tr>
<td><code>OSError</code></td>
<td>系统 / I/O 相关（<code>IOError</code> 是其别名）</td>
</tr>
<tr>
<td><code>FileNotFoundError</code></td>
<td>文件不存在</td>
</tr>
<tr>
<td><code>PermissionError</code></td>
<td>权限不足</td>
</tr>
<tr>
<td><code>RecursionError</code></td>
<td>超过最大递归深度</td>
</tr>
<tr>
<td><code>ReferenceError</code></td>
<td>弱引用对象已销毁（少见）</td>
</tr>
<tr>
<td><code>RuntimeError</code></td>
<td>一般不落在更具体类型时的运行时错误</td>
</tr>
<tr>
<td><code>NotImplementedError</code></td>
<td>抽象方法 / 待实现</td>
</tr>
<tr>
<td><code>SyntaxError</code></td>
<td>语法错误（编译期）</td>
</tr>
<tr>
<td><code>IndentationError</code></td>
<td>缩进错误</td>
</tr>
<tr>
<td><code>TabError</code></td>
<td>Tab 与空格混用</td>
</tr>
<tr>
<td><code>SystemError</code></td>
<td>解释器内部错误</td>
</tr>
<tr>
<td><code>TypeError</code></td>
<td>类型不对或参数个数不对</td>
</tr>
<tr>
<td><code>ValueError</code></td>
<td>类型对但值不合法（如 <code>int("abc")</code>）</td>
</tr>
<tr>
<td><code>UnicodeError</code></td>
<td>Unicode 编解码相关基类</td>
</tr>
<tr>
<td><code>UnicodeDecodeError</code> / <code>UnicodeEncodeError</code></td>
<td>解码 / 编码失败</td>
</tr>
<tr>
<td><code>Warning</code></td>
<td>警告类别基类（不是 <code>Exception</code> 同级习惯捕获对象）</td>
</tr>
</tbody>
</table>
<p>一般不捕获 <a href="https://docs.python.org/3/library/exceptions.html#BaseException"><code>BaseException</code></a> 下的 <a href="https://docs.python.org/3/library/exceptions.html#KeyboardInterrupt"><code>KeyboardInterrupt</code></a>、<a href="https://docs.python.org/3/library/exceptions.html#SystemExit"><code>SystemExit</code></a>、<a href="https://docs.python.org/3/library/exceptions.html#GeneratorExit"><code>GeneratorExit</code></a>，除非在做框架级顶层处理。</p>
<hr />
<h2>with 与上下文管理器</h2>
<p><a href="https://docs.python.org/3/reference/compound_stmts.html#the-with-statement"><code>with</code></a> 语句用于管理资源：进入时 setup，离开时 teardown，即使发生异常也会清理。<a href="https://peps.python.org/pep-0343/">PEP 343</a> 定义协议。</p>
<p>上下文管理器需实现：</p>
<ul>
<li><a href="https://docs.python.org/3/reference/datamodel.html#object.__enter__"><code>__enter__(self)</code></a>：进入 <code>with</code> 块前调用；返回值绑定到 <code>as</code> 后的变量（若无 <code>as</code> 则忽略返回值）。</li>
<li><a href="https://docs.python.org/3/reference/datamodel.html#object.__exit__"><code>__exit__(self, exc_type, exc_val, exc_tb)</code></a>：离开 <code>with</code> 块时调用。若返回真值，会<strong>抑制</strong>当前异常（一般不这么做）；否则异常继续传播。</li>
</ul>
<pre><code>class Tag:
    def __init__(self, name):
        self.name = name

    def __enter__(self):
        print(f"&lt;{self.name}&gt;")
        return self

    def __exit__(self, exc_type, exc, tb):
        print(f"&lt;/{self.name}&gt;")
        return False

with Tag("p") as t:
    print("body")
</code></pre>
<p>内置 <a href="https://docs.python.org/3/library/functions.html#open"><code>open()</code></a> 返回的文件对象、<a href="https://docs.python.org/3/library/threading.html#threading.Lock"><code>threading.Lock</code></a> 等已实现该协议。自定义资源管理可用 <a href="https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager"><code>contextlib.contextmanager</code></a> 装饰生成器，或用 <a href="https://docs.python.org/3/library/contextlib.html#contextlib.AbstractContextManager"><code>contextlib.AbstractContextManager</code></a>。多个上下文管理器可写 <code>with A() as a, B() as b:</code>。</p>
<h2>iter 与 next</h2>
<p><a href="https://docs.python.org/3/glossary.html#term-iterator">迭代器</a>实现迭代器协议：<code>__iter__()</code> 返回自身（或另一迭代器），该对象再提供 <code>__next__()</code>。内置 <a href="https://docs.python.org/3/library/functions.html#iter"><code>iter()</code></a> 优先调用 <code>__iter__()</code>；若没有 <code>__iter__</code> 但实现了从 0 开始的 <code>__getitem__</code>，仍可按序列方式迭代（见 <a href="https://docs.python.org/3/library/stdtypes.html#iterator-types">iterator protocol</a>）。自定义可迭代对象时，实现 <code>__iter__</code> 即可。</p>
<p><a href="https://docs.python.org/3/library/functions.html#next"><code>next(iterator[, default])</code></a> 调用 <code>iterator.__next__()</code>。没有更多元素时，<code>__next__()</code> 抛出 <a href="https://docs.python.org/3/library/exceptions.html#StopIteration"><code>StopIteration</code></a>；若提供了 <code>default</code>，则返回 <code>default</code> 而不抛异常。</p>
<p>容器（如 <code>list</code>）本身通常不是迭代器，但可迭代（<a href="https://docs.python.org/3/glossary.html#term-iterable">iterable</a>）：<code>__iter__()</code> 返回一个新的迭代器。每次 <code>for x in lst:</code> 都会先 <code>iter(lst)</code> 再反复 <code>next</code>。</p>
<pre><code>it = iter([10, 20, 30])
print(next(it))  # 10
print(next(it))  # 20
print(next(it, "没了"))  # 30
print(next(it, "没了"))  # 没了
</code></pre>
<p><code>for</code> 循环在底层等价于：取迭代器 → 反复 <code>next</code> → 捕获 <code>StopIteration</code> 结束。实现 <code>__iter__</code> 与 <code>__next__</code>：</p>
<pre><code>class CountDown:
    def __init__(self, n):
        self.n = n

    def __iter__(self):
        return self

    def __next__(self):
        if self.n &lt;= 0:
            raise StopIteration
        self.n -= 1
        return self.n + 1
</code></pre>
<h2>生成器与 yield</h2>
<p><a href="https://docs.python.org/3/glossary.html#term-generator">生成器</a>是一种迭代器，由含 <a href="https://docs.python.org/3/reference/simple_stmts.html#yield"><code>yield</code></a> 的函数或 <a href="https://docs.python.org/3/reference/simple_stmts.html#yield-from"><code>yield from</code></a> 表达式产生。调用生成器函数不会立刻执行函数体，而是返回一个生成器对象；每次 <code>next()</code> 或 <code>for</code> 循环驱动执行到下一个 <code>yield</code>，产出值并暂停。</p>
<pre><code>def count_up_to(n):
    i = 1
    while i &lt;= n:
        yield i
        i += 1

for x in count_up_to(3):
    print(x)  # 1, 2, 3
</code></pre>
<p>生成器表达式 <code>(x * x for x in range(5))</code> 语法类似列表推导，但结果是生成器而非列表，适合大数据或流水线。<a href="https://docs.python.org/3/reference/expressions.html#generator-expressions">Generator expressions</a>。</p>
<p><code>yield from iterable</code> 把产出委托给另一个可迭代对象，常用于扁平化嵌套生成器。要用 <code>with</code> 包一层生成器，需 <a href="https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager"><code>contextlib.contextmanager</code></a>；生成器本身不是上下文管理器。</p>
<h2>装饰器</h2>
<p><a href="https://peps.python.org/pep-0318/">PEP 318</a> 引入函数装饰器语法：<code>@decorator</code> 放在 <code>def</code>（或 <code>class</code>）前，等价于 <code>func = decorator(func)</code>。装饰器通常是<strong>接受 callable、返回 callable</strong> 的可调用对象。</p>
<pre><code>def log_calls(fn):
    def wrapper(*args, **kwargs):
        print(f"calling {fn.__name__}")
        return fn(*args, **kwargs)
    return wrapper

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

add(1, 2)  # 先打印 calling add，再返回 3
</code></pre>
<p>被包装后 <code>wrapper.__name__</code> 等元数据会丢；用 <a href="https://docs.python.org/3/library/functools.html#functools.wraps"><code>functools.wraps</code></a> 拷回去：</p>
<pre><code>import functools

def log_calls(fn):
    @functools.wraps(fn)
    def wrapper(*args, **kwargs):
        print(f"calling {fn.__name__}")
        return fn(*args, **kwargs)
    return wrapper
</code></pre>
<p><strong>带参装饰器</strong>：外层函数先接收装饰器参数，返回真正的装饰器。</p>
<pre><code>def repeat(times):
    def decorator(fn):
        def wrapper(*args, **kwargs):
            for _ in range(times):
                result = fn(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(3)
def hi():
    print("hi")
</code></pre>
<p>类也可以当装饰器（实现 <code>__call__</code>）。内置装饰器如 <a href="https://docs.python.org/3/library/functions.html#staticmethod"><code>staticmethod</code></a>、<a href="https://docs.python.org/3/library/functions.html#classmethod"><code>classmethod</code></a>、<a href="https://docs.python.org/3/library/functions.html#property"><code>property</code></a>。装饰器栈从下往上应用：<code>@a @b def f</code> 等价于 <code>f = a(b(f))</code>。</p>
<h2>附录：内置异常继承关系</h2>
<p><a href="https://docs.python.org/3/library/exceptions.html#exception-hierarchy">Exception hierarchy</a>。缩进表示子类。</p>
<table>
<thead>
<tr>
<th>基类</th>
<th>直接子类</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>BaseException</code></td>
<td><code>SystemExit</code>, <code>KeyboardInterrupt</code>, <code>GeneratorExit</code>, <code>Exception</code></td>
</tr>
<tr>
<td><code>Exception</code></td>
<td><code>StopIteration</code>, <code>StopAsyncIteration</code>, <code>ArithmeticError</code>, <code>AssertionError</code>, <code>AttributeError</code>, <code>BufferError</code>, <code>EOFError</code>, <code>ImportError</code>, <code>LookupError</code>, <code>MemoryError</code>, <code>NameError</code>, <code>OSError</code>, <code>ReferenceError</code>, <code>RuntimeError</code>, <code>SyntaxError</code>, <code>SystemError</code>, <code>TypeError</code>, <code>ValueError</code>, <code>Warning</code></td>
</tr>
<tr>
<td><code>ArithmeticError</code></td>
<td><code>FloatingPointError</code>, <code>OverflowError</code>, <code>ZeroDivisionError</code></td>
</tr>
<tr>
<td><code>LookupError</code></td>
<td><code>IndexError</code>, <code>KeyError</code></td>
</tr>
<tr>
<td><code>OSError</code></td>
<td><code>BlockingIOError</code>, <code>ChildProcessError</code>, <code>ConnectionError</code>, <code>FileExistsError</code>, <code>FileNotFoundError</code>, <code>InterruptedError</code>, <code>IsADirectoryError</code>, <code>NotADirectoryError</code>, <code>PermissionError</code>, <code>ProcessLookupError</code>, <code>TimeoutError</code> …</td>
</tr>
<tr>
<td><code>ConnectionError</code></td>
<td><code>BrokenPipeError</code>, <code>ConnectionAbortedError</code>, <code>ConnectionRefusedError</code>, <code>ConnectionResetError</code></td>
</tr>
<tr>
<td><code>ImportError</code></td>
<td><code>ModuleNotFoundError</code></td>
</tr>
<tr>
<td><code>NameError</code></td>
<td><code>UnboundLocalError</code></td>
</tr>
<tr>
<td><code>RuntimeError</code></td>
<td><code>NotImplementedError</code>, <code>RecursionError</code></td>
</tr>
<tr>
<td><code>SyntaxError</code></td>
<td><code>IndentationError</code> → <code>TabError</code></td>
</tr>
<tr>
<td><code>UnicodeError</code></td>
<td><code>UnicodeDecodeError</code>, <code>UnicodeEncodeError</code>, <code>UnicodeTranslateError</code></td>
</tr>
<tr>
<td><code>ValueError</code></td>
<td><code>UnicodeError</code> 等</td>
</tr>
</tbody>
</table>
<p><code>except OSError</code> 可捕 <code>FileNotFoundError</code>；<code>except LookupError</code> 可捕 <code>IndexError</code> 与 <code>KeyError</code>。</p>
<hr />
<h2>附录：函数参数汇总</h2>
<table>
<thead>
<tr>
<th>写法</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>def f(a, b)</code></td>
<td>位置或关键字</td>
</tr>
<tr>
<td><code>def f(a, b=0)</code></td>
<td><code>b</code> 有默认值</td>
</tr>
<tr>
<td><code>def f(a, /, b)</code></td>
<td><code>a</code> 仅位置（3.8+）</td>
</tr>
<tr>
<td><code>def f(a, *, b)</code></td>
<td><code>b</code> 仅关键字</td>
</tr>
<tr>
<td><code>def f(*args)</code></td>
<td>多余位置 → 元组</td>
</tr>
<tr>
<td><code>def f(**kwargs)</code></td>
<td>多余关键字 → 字典</td>
</tr>
<tr>
<td><code>def f(a, /, b, *args, c=1, **kw)</code></td>
<td>顺序：仅位置、<code>/</code>、位置或关键字、<code>*args</code>、仅关键字、<code>**kwargs</code></td>
</tr>
</tbody>
</table>
<hr />
<h2>参考</h2>
<p>官方文档（<a href="https://docs.python.org/zh-cn/3/">中文版</a>）：</p>
<ol>
<li><a href="https://docs.python.org/3/tutorial/controlflow.html#defining-functions">More Control Flow Tools — Defining Functions</a> — 默认参数、关键字参数、<code>*args</code> / <code>**kwargs</code>、Docstring</li>
<li><a href="https://docs.python.org/3/tutorial/controlflow.html#intermezzo-coding-style">More Control Flow Tools — Intermezzo: Coding Style</a> — 命名与可读性</li>
<li><a href="https://docs.python.org/3/tutorial/errors.html">Errors and Exceptions</a> — 异常处理、<code>else</code> / <code>finally</code>、<code>raise</code>、用户异常、异常链</li>
<li><a href="https://docs.python.org/3/reference/compound_stmts.html#function-definitions">Compound statements — Function definitions</a> — 完整参数语法、<code>lambda</code> 不能替代 <code>def</code> 的全部能力</li>
<li><a href="https://docs.python.org/3/reference/compound_stmts.html#the-try-statement">Compound statements — The try statement</a> — <code>try</code> / <code>except</code> / <code>else</code> / <code>finally</code> 语义</li>
<li><a href="https://docs.python.org/3/reference/compound_stmts.html#the-with-statement">Compound statements — The with statement</a> — <code>with</code> 与 <code>as</code></li>
<li><a href="https://docs.python.org/3/reference/datamodel.html#with-statement-context-managers">Data model — With Statement Context Managers</a> — <code>__enter__</code> / <code>__exit__</code></li>
<li><a href="https://docs.python.org/3/glossary.html">Glossary — iterator / generator / iterable</a> — 术语定义</li>
<li><a href="https://docs.python.org/3/reference/executionmodel.html#naming-and-binding">Execution model — Naming and binding</a> — LEGB、闭包</li>
<li><a href="https://docs.python.org/3/reference/simple_stmts.html">Simple statements — global / nonlocal / return / raise / yield</a></li>
<li><a href="https://docs.python.org/3/library/exceptions.html">Built-in Exceptions</a> — 异常列表与继承树</li>
<li><a href="https://docs.python.org/3/library/functools.html#functools.wraps">functools.wraps</a> — 装饰器保留元数据</li>
<li><a href="https://docs.python.org/3/library/contextlib.html">contextlib</a> — 上下文管理器工具</li>
</ol>
<p>PEP：</p>
<ol>
<li><a href="https://peps.python.org/pep-0318/">PEP 318 — Decorators for Functions and Methods</a></li>
<li><a href="https://peps.python.org/pep-0343/">PEP 343 — The "with" Statement</a></li>
<li><a href="https://peps.python.org/pep-3102/">PEP 3102 — Keyword-Only Arguments</a></li>
<li><a href="https://peps.python.org/pep-03110/">PEP 3110 — Catching exceptions in Python 3000</a></li>
</ol>
<p>菜鸟教程：</p>
<ol>
<li><a href="https://www.runoob.com/python3/python3-function.html">菜鸟教程 · Python3 函数</a></li>
<li><a href="https://www.runoob.com/python3/python3-iterator-generator.html">菜鸟教程 · Python3 迭代器与生成器</a></li>
<li><a href="https://www.runoob.com/python3/python3-decorator.html">菜鸟教程 · Python3 装饰器</a></li>
<li><a href="https://www.runoob.com/python3/python3-errors-exceptions.html">菜鸟教程 · Python3 错误和异常</a></li>
<li><a href="https://www.runoob.com/python3/python3-namespace-scope.html">菜鸟教程 · Python3 命名空间/作用域</a></li>
<li><a href="https://www.runoob.com/python3/python3-with.html">菜鸟教程 · Python3 with</a></li>
<li><a href="https://www.runoob.com/python3/python3-lambda.html">菜鸟教程 · Python3 lambda</a></li>
</ol>
<hr />
]]>
    </content>
  </entry>
  <entry>
    <id>https://blog.chuwu.top/posts/sql/mysql6/</id>
    <updated>2026-08-30T00:00:00.000Z</updated>
    <title>MySQL学习笔记 (6)</title>
    <author>
      <name>ChuwuYo</name>
    </author>
    <link href="https://blog.chuwu.top/posts/sql/mysql6/" rel="alternate"/>
    <published>2026-08-30T00:00:00.000Z</published>
    <summary>COUNT/SUM/AVG/MIN/MAX、GROUP BY、HAVING</summary>
    <content type="html" xml:base="https://blog.chuwu.top/posts/sql/mysql6/">
      <![CDATA[<p>:::note
基于 MySQL 26.7。<a href="https://dev.mysql.com/doc/refman/26.7/en/aggregate-functions.html">聚合函数</a>
:::</p>
<h2>聚合函数</h2>
<p>聚合函数对一组值进行计算，返回单个值。</p>
<table>
<thead>
<tr>
<th>函数</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>COUNT(*)</code></td>
<td>行数</td>
</tr>
<tr>
<td><code>COUNT(col)</code></td>
<td><code>col</code> 不是 NULL 的个数</td>
</tr>
<tr>
<td><code>COUNT(DISTINCT col)</code></td>
<td>去重后的个数</td>
</tr>
<tr>
<td><code>SUM(expr)</code></td>
<td>合计</td>
</tr>
<tr>
<td><code>AVG(expr)</code></td>
<td>平均值</td>
</tr>
<tr>
<td><code>MIN(expr)</code></td>
<td>最小值</td>
</tr>
<tr>
<td><code>MAX(expr)</code></td>
<td>最大值</td>
</tr>
</tbody>
</table>
<pre><code>SELECT COUNT(*)          AS n_emp,
       COUNT(manager_id) AS n_with_mgr,
       AVG(salary)       AS avg_sal
FROM employee;
</code></pre>
<p>没有 <code>GROUP BY</code> 时，整张表（经过 <code>WHERE</code> 之后）算作一组。</p>
<p><code>COUNT(phone)</code> 只统计填了手机号的行；<code>COUNT(*)</code> 统计所有行。</p>
<h2>GROUP BY</h2>
<p><code>GROUP BY</code> 用于结合聚合函数，按一个或多个列分组。</p>
<pre><code>SELECT dept_no, COUNT(*) AS n, AVG(salary) AS avg_sal
FROM   employee
GROUP  BY dept_no;
</code></pre>
<p><code>SELECT</code> 里没有用聚合函数的列，一般要出现在 <code>GROUP BY</code> 中。下面这样是不对的：按部门分组，却直接选出每个人的姓名。</p>
<pre><code>SELECT dept_no, last_name, COUNT(*)
FROM employee
GROUP BY dept_no;
</code></pre>
<p>按多列分组：</p>
<pre><code>GROUP BY dept_no, job_id
</code></pre>
<p><code>WHERE</code> 在分组之前筛选行。分组之后的条件要用 <code>HAVING</code>。</p>
<h2>HAVING</h2>
<p><code>HAVING</code> 用于筛选分组后的结果。</p>
<pre><code>SELECT dept_no, COUNT(*) AS n
FROM   employee
WHERE  salary &gt;= 3000
GROUP  BY dept_no
HAVING COUNT(*) &gt;= 2
ORDER  BY n DESC;
</code></pre>
<p><code>HAVING</code> 里可以使用聚合函数，<code>WHERE</code> 里不行。</p>
<h2>NULL 和聚合</h2>
<p>分组键是 NULL 的行会分到同一组。</p>
<p>某一组里 <code>SUM</code> 的值全是 NULL 时，结果是 NULL，不是 0。需要显示 0 时可以写成：</p>
<pre><code>COALESCE(SUM(amount), 0)
</code></pre>
<hr />
<h2>参考</h2>
<ol>
<li><a href="https://dev.mysql.com/doc/refman/26.7/en/aggregate-functions.html">MySQL 26.7 · Aggregate Functions</a></li>
<li><a href="https://dev.mysql.com/doc/refman/26.7/en/group-by-handling.html">MySQL 26.7 · GROUP BY</a></li>
<li><a href="https://dev.mysql.com/doc/refman/26.7/en/select.html">MySQL 26.7 · SELECT</a></li>
<li><a href="https://www.iso.org/standard/76583.html">ISO/IEC 9075（SQL:2023）</a></li>
</ol>
]]>
    </content>
  </entry>
  <entry>
    <id>https://blog.chuwu.top/posts/python/python6/</id>
    <updated>2026-08-29T00:00:00.000Z</updated>
    <title>Python 札记 (6)</title>
    <author>
      <name>ChuwuYo</name>
    </author>
    <link href="https://blog.chuwu.top/posts/python/python6/" rel="alternate"/>
    <published>2026-08-29T00:00:00.000Z</published>
    <summary>条件与循环、match / case、range/enumerate/zip、斐波那契与推导式</summary>
    <content type="html" xml:base="https://blog.chuwu.top/posts/python/python6/">
      <![CDATA[<p>:::note
基于 Python 3。
:::</p>
<h2>条件：if / elif / else</h2>
<pre><code>score = 85
if score &gt;= 90:
    grade = "A"
elif score &gt;= 80:
    grade = "B"
elif score &gt;= 60:
    grade = "C"
else:
    grade = "F"
print(grade)
</code></pre>
<p><code>elif</code> 可以没有或有多个，<code>else</code> 可有可无。空分支写 <code>pass</code>。</p>
<p><code>None</code>、<code>False</code>、<code>0</code>、<code>''</code>、<code>[]</code>、<code>{}</code> 在条件里当假，其余一般为真。<a href="https://docs.python.org/3/library/stdtypes.html#truth-value-testing">Truth Value Testing</a></p>
<p><code>if</code> / <code>for</code> / <code>while</code> 中赋值的变量在代码块外仍然可以使用。函数具有独立的局部作用域。</p>
<h3>比较与链式比较</h3>
<p>比较运算符：<code>==</code> <code>!=</code> <code>&lt;</code> <code>&lt;=</code> <code>&gt;</code> <code>&gt;=</code>，以及 <code>is</code> / <code>is not</code>（身份）、<code>in</code> / <code>not in</code>（成员）。可链式写：<code>a &lt; b &lt;= c</code> 等价于 <code>a &lt; b and b &lt;= c</code>，且 <strong>b 只算一次</strong>（<a href="https://docs.python.org/3/library/stdtypes.html#comparisons">Comparisons</a>）。</p>
<h3>三元表达式（条件表达式）</h3>
<p>语法：<code>value_if_true if condition else value_if_false</code>。先计算 <code>condition</code>，再选择其中一个表达式：</p>
<pre><code>status = "adult" if age &gt;= 18 else "minor"
</code></pre>
<p><code>:=</code> 可在表达式中赋值（<a href="https://peps.python.org/pep-0572/">PEP 572</a>）：</p>
<pre><code>words = ["a", "b", "c", "d"]
if (n := len(words)) &gt; 3:
    print(n)
</code></pre>
<h2>循环：while 与 for</h2>
<h3>while</h3>
<p>条件为 <code>True</code> 时重复执行循环体。</p>
<pre><code>n = 3
while n &gt; 0:
    print(n)
    n -= 1
print("lift off")
</code></pre>
<h3>for</h3>
<p><a href="https://docs.python.org/3/reference/compound_stmts.html#the-for-statement">for 语句</a> 依次取出元素赋给循环变量。列表、字符串、<code>range</code> 都可以。</p>
<pre><code>words = ["Python", "is", "great"]
for w in words:
    print(w)
</code></pre>
<p>遍历 dict 时，默认迭代<strong>键</strong>；要键值用 <code>.items()</code>。</p>
<h3>break、continue</h3>
<ul>
<li><strong><code>break</code></strong> — 立刻跳出<strong>最内层</strong> enclosing 循环。</li>
<li><strong><code>continue</code></strong> — 跳过本次剩余 suite，进入下一轮迭代。</li>
</ul>
<pre><code>for x in range(10):
    if x % 2 == 0:
        continue
    if x &gt; 5:
        break
    print(x)    # 1, 3, 5
</code></pre>
<h3>循环的 else 子句</h3>
<p><code>while</code> / <code>for</code> 可以带 <code>else</code>：循环正常结束（没有被 <code>break</code> 中断）时执行 <code>else</code> 子句。</p>
<pre><code>for n in [2, 4, 6, 8]:
    if n % 2 == 1:
        print("found odd")
        break
else:
    print("all even")
</code></pre>
<h3>match / case（3.10+）</h3>
<p>3.10 起可用 <code>match</code> / <code>case</code> 按值分支（<a href="https://peps.python.org/pep-0636/">PEP 636</a>）。3.9 及更早没有这套语法。</p>
<pre><code>code = 404
match code:
    case 200:
        msg = "OK"
    case 404:
        msg = "Not Found"
    case 500 | 502 | 503:
        msg = "Server Error"
    case _:
        msg = "Unknown"
print(msg)
</code></pre>
<p><code>_</code> 为通配模式。可解构序列、映射、对象属性。</p>
<h2>range</h2>
<p><a href="https://docs.python.org/3/library/stdtypes.html#range"><code>range</code></a> 是不可变整数序列，<strong>不占</strong>完整 list 内存。</p>
<pre><code>range(stop)                    # 0 .. stop-1
range(start, stop)             # start .. stop-1
range(start, stop, step)       # step 不可为 0
</code></pre>
<pre><code>print(list(range(5)))           # [0, 1, 2, 3, 4]
print(list(range(2, 8, 2)))     # [2, 4, 6]
print(list(range(0, -3, -1)))   # [0, -1, -2]
</code></pre>
<p>需要 list 时再 <code>list(range(...))</code>。</p>
<h2>enumerate 与 zip</h2>
<p><strong><a href="https://docs.python.org/3/library/functions.html#enumerate"><code>enumerate(iterable, start=0)</code></a></strong> — 产出 <code>(index, item)</code>：</p>
<pre><code>for i, fruit in enumerate(["apple", "banana"], start=1):
    print(i, fruit)
</code></pre>
<p><strong><a href="https://docs.python.org/3/library/functions.html#zip"><code>zip</code></a></strong> — 并行迭代，每次得到一组值，长度取<strong>最短</strong>：</p>
<pre><code>names = ["Alice", "Bob"]
scores = [90, 85]
for name, score in zip(names, scores):
    print(name, score)
</code></pre>
<p>长度不等时余下部分丢弃。要对齐最长一侧，用 <a href="https://docs.python.org/3/library/itertools.html#itertools.zip_longest"><code>itertools.zip_longest</code></a>。</p>
<h2>斐波那契</h2>
<pre><code>a, b = 0, 1
while a &lt; 1000:
    print(a, end=", ")
    a, b = b, a + b
print()
</code></pre>
<p>固定项数可以用 <code>for</code>：</p>
<pre><code>a, b = 0, 1
for _ in range(10):
    print(a, end=" ")
    a, b = b, a + b
print()
</code></pre>
<p>要存起来就往 list 里 <code>append</code>：</p>
<pre><code>result = []
a, b = 0, 1
for _ in range(8):
    result.append(a)
    a, b = b, a + b
print(result)   # [0, 1, 1, 2, 3, 5, 8, 13]
</code></pre>
<h2>列表推导式</h2>
<p><a href="https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions">List Comprehensions</a>：<code>[expression for item in iterable if condition]</code>。</p>
<pre><code>squares = [x * x for x in range(10)]
evens = [x for x in range(20) if x % 2 == 0]
</code></pre>
<p>嵌套循环从左到右等价于嵌套 <code>for</code>：</p>
<pre><code>pairs = [(x, y) for x in range(3) for y in range(3) if x != y]
</code></pre>
<p>简单变换用推导式；副作用多或逻辑复杂时用普通 <code>for</code>。</p>
<h3>保序去重</h3>
<pre><code>raw = [3, 1, 2, 3, 2, 1]
ordered_unique = list(dict.fromkeys(raw))
print(ordered_unique)   # [3, 1, 2]
</code></pre>
<h2>集合与字典推导式</h2>
<p><strong>集合推导式</strong> — <code>{expr for item in iterable if cond}</code>，花括号与 dict 字面量相同，但<strong>无冒号</strong>：</p>
<pre><code>unique_lengths = {len(w) for w in ["hi", "hello", "hi"]}
print(unique_lengths)   # {2, 5}
</code></pre>
<p><strong>字典推导式</strong> — <code>{key: value for ...}</code>：</p>
<pre><code>word_map = {w: len(w) for w in ["Python", "Java", "Go"]}
print(word_map)
</code></pre>
<h2>生成器表达式</h2>
<p>语法与 list 推导式类似，圆括号：<code>(expr for item in iterable if cond)</code>。产出 <strong>iterator</strong>，惰性求值：</p>
<pre><code>total = sum(x * x for x in range(1000000))   # 外层函数吞掉括号
gen = (x * x for x in range(5))
print(next(gen))   # 0
print(list(gen))   # [1, 4, 9, 16] — 已消耗 0
</code></pre>
<p>单独写 <code>(x for x in iterable)</code> 是生成器表达式；仅 <code>{}</code>、<code>[]</code> 构成 dict / list 推导式。<strong>单层</strong> <code>(x for ...)</code> 与调用写成 <code>func(x for ...)</code> 时，圆括号可省略一层（<a href="https://docs.python.org/3/reference/expressions.html#generator-expressions">Generator expressions</a>）。</p>
<p>推导式里的海象 <code>:=</code> 见 <a href="https://peps.python.org/pep-0572/">PEP 572</a>。</p>
<hr />
<h2>附录：控制流与推导式语法速查</h2>
<table>
<thead>
<tr>
<th>构造</th>
<th>语法要点</th>
</tr>
</thead>
<tbody>
<tr>
<td>条件</td>
<td><code>if</code> / <code>elif</code> / <code>else</code> + 缩进 suite</td>
</tr>
<tr>
<td>三元</td>
<td><code>a if cond else b</code></td>
</tr>
<tr>
<td>match</td>
<td><code>match subject:</code> + <code>case pattern:</code>（3.10+）</td>
</tr>
<tr>
<td>while</td>
<td><code>while cond:</code></td>
</tr>
<tr>
<td>for</td>
<td><code>for target in iterable:</code></td>
</tr>
<tr>
<td>break / continue</td>
<td>仅作用于最内层循环</td>
</tr>
<tr>
<td>循环 else</td>
<td>未 <code>break</code> 时执行</td>
</tr>
<tr>
<td>list 推导</td>
<td><code>[e for x in it if c]</code></td>
</tr>
<tr>
<td>set 推导</td>
<td><code>{e for x in it if c}</code></td>
</tr>
<tr>
<td>dict 推导</td>
<td><code>{k: v for x in it if c}</code></td>
</tr>
<tr>
<td>生成器表达式</td>
<td><code>(e for x in it if c)</code></td>
</tr>
</tbody>
</table>
<h3>常用内置（循环相关）</h3>
<table>
<thead>
<tr>
<th>名称</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>range</code></td>
<td>整数序列</td>
</tr>
<tr>
<td><code>enumerate</code></td>
<td>带索引迭代</td>
</tr>
<tr>
<td><code>zip</code></td>
<td>并行迭代</td>
</tr>
<tr>
<td><code>len</code></td>
<td>长度</td>
</tr>
<tr>
<td><code>sum</code> / <code>min</code> / <code>max</code></td>
<td>聚合（可接可迭代对象）</td>
</tr>
<tr>
<td><code>sorted</code></td>
<td>返回新排序 list</td>
</tr>
<tr>
<td><code>reversed</code></td>
<td>返回 reverse iterator</td>
</tr>
</tbody>
</table>
<hr />
<h2>参考</h2>
<p>官方文档（<a href="https://docs.python.org/zh-cn/3/">中文版</a>）：</p>
<ol>
<li><a href="https://docs.python.org/3/tutorial/controlflow.html">More Control Flow Tools</a> — <code>if</code>、<code>while</code>、<code>for</code>、<code>break</code>/<code>continue</code>/<code>else</code>、匹配语句</li>
<li><a href="https://docs.python.org/3/tutorial/introduction.html#first-steps-towards-programming">First Steps Towards Programming</a> — 斐波那契 <code>while</code> 示例</li>
<li><a href="https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions">List Comprehensions</a> — 列表推导与集合推导</li>
<li><a href="https://docs.python.org/3/tutorial/datastructures.html#looping-techniques">Looping Techniques</a> — <code>enumerate</code>、<code>zip</code>、dict 遍历</li>
<li><a href="https://docs.python.org/3/reference/compound_stmts.html#the-for-statement">The for statement</a></li>
<li><a href="https://docs.python.org/3/reference/compound_stmts.html#the-while-statement">The while statement</a></li>
<li><a href="https://docs.python.org/3/reference/expressions.html#generator-expressions">Generator expressions</a></li>
<li><a href="https://docs.python.org/3/library/stdtypes.html#range">range</a> — 构造与步长</li>
<li><a href="https://docs.python.org/3/library/functions.html#enumerate">enumerate()</a> / <a href="https://docs.python.org/3/library/functions.html#zip">zip()</a></li>
<li><a href="https://docs.python.org/3/library/stdtypes.html#truth-value-testing">Truth Value Testing</a></li>
</ol>
<p>PEP：</p>
<ol>
<li><a href="https://peps.python.org/pep-0636/">PEP 636 — Structural Pattern Matching</a> — <code>match</code> / <code>case</code></li>
</ol>
<p>菜鸟教程：</p>
<ol>
<li><a href="https://www.runoob.com/python3/python3-conditional-statements.html">菜鸟教程 · 条件控制</a></li>
<li><a href="https://www.runoob.com/python3/python3-loop.html">菜鸟教程 · 循环语句</a></li>
</ol>
<hr />
]]>
    </content>
  </entry>
  <entry>
    <id>https://blog.chuwu.top/posts/sql/mysql5/</id>
    <updated>2026-08-29T00:00:00.000Z</updated>
    <title>MySQL学习笔记 (5)</title>
    <author>
      <name>ChuwuYo</name>
    </author>
    <link href="https://blog.chuwu.top/posts/sql/mysql5/" rel="alternate"/>
    <published>2026-08-29T00:00:00.000Z</published>
    <summary>WHERE、AND/OR、IN、BETWEEN、LIKE、IS NULL、CASE</summary>
    <content type="html" xml:base="https://blog.chuwu.top/posts/sql/mysql5/">
      <![CDATA[<p>:::note
基于 MySQL 26.7。<a href="https://dev.mysql.com/doc/refman/26.7/en/comparison-operators.html">比较运算符</a>
:::</p>
<h2>WHERE 子句</h2>
<p><code>WHERE</code> 用于筛选记录。</p>
<pre><code>SELECT last_name, salary
FROM   employee
WHERE  salary &gt;= 8000
AND    dept_no = 'D01';
</code></pre>
<p>运算符：</p>
<table>
<thead>
<tr>
<th>运算符</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>=</code></td>
<td>等于</td>
</tr>
<tr>
<td><code>&lt;&gt;</code></td>
<td>不等于</td>
</tr>
<tr>
<td><code>&gt;</code></td>
<td>大于</td>
</tr>
<tr>
<td><code>&lt;</code></td>
<td>小于</td>
</tr>
<tr>
<td><code>&gt;=</code></td>
<td>大于等于</td>
</tr>
<tr>
<td><code>&lt;=</code></td>
<td>小于等于</td>
</tr>
</tbody>
</table>
<h2>AND、OR、NOT</h2>
<ul>
<li><code>AND</code>：并且</li>
<li><code>OR</code>：或者</li>
<li><code>NOT</code>：非</li>
</ul>
<p><code>NOT</code> 优先于 <code>AND</code>，<code>AND</code> 优先于 <code>OR</code>。可以用括号改变顺序：</p>
<pre><code>WHERE dept_no = 'D01' OR dept_no = 'D02' AND salary &gt; 9000
-- D01 的全部，加上 D02 且工资大于 9000 的

WHERE (dept_no = 'D01' OR dept_no = 'D02') AND salary &gt; 9000
</code></pre>
<h2>IN</h2>
<p><code>IN</code> 用于在多个可能值中匹配：</p>
<pre><code>WHERE dept_no IN ('D01', 'D02', 'D03')
WHERE dept_no NOT IN ('D01', 'D02')
</code></pre>
<h2>BETWEEN</h2>
<p><code>BETWEEN</code> 选取介于两个值之间的数据，包含两端：</p>
<pre><code>WHERE salary BETWEEN 5000 AND 12000
</code></pre>
<p>相当于 <code>salary &gt;= 5000 AND salary &lt;= 12000</code>。</p>
<h2>LIKE</h2>
<p><code>LIKE</code> 用于在 <code>WHERE</code> 子句中搜索指定模式。</p>
<table>
<thead>
<tr>
<th>通配符</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>%</code></td>
<td>替代 0 个或多个字符</td>
</tr>
<tr>
<td><code>_</code></td>
<td>替代一个字符</td>
</tr>
</tbody>
</table>
<pre><code>WHERE last_name LIKE '张%'
WHERE last_name LIKE '_明'
WHERE last_name NOT LIKE '%test%'
</code></pre>
<p>如果要匹配 <code>%</code> 或 <code>_</code> 本身，使用 <code>ESCAPE</code>：</p>
<pre><code>WHERE title LIKE '%10$%%' ESCAPE '$'
</code></pre>
<h2>IS NULL</h2>
<p><code>IS NULL</code> 用于查找 NULL 值。</p>
<pre><code>WHERE manager_id IS NULL
WHERE manager_id IS NOT NULL
</code></pre>
<p>注意：不能写成 <code>manager_id = NULL</code>。</p>
<h2>CASE</h2>
<p><code>CASE</code> 根据条件返回不同的值：</p>
<pre><code>SELECT last_name,
       CASE
           WHEN salary &gt;= 12000 THEN 'high'
           WHEN salary &gt;= 8000  THEN 'mid'
           ELSE 'low'
       END AS band
FROM employee;
</code></pre>
<p>也可以写成：</p>
<pre><code>CASE salary WHEN 0 THEN 'zero' ELSE 'nonzero' END
</code></pre>
<p>没有 <code>ELSE</code> 时，不匹配的结果是 NULL。</p>
<hr />
<h2>参考</h2>
<ol>
<li><a href="https://dev.mysql.com/doc/refman/26.7/en/comparison-operators.html">MySQL 26.7 · Comparison Operators</a></li>
<li><a href="https://dev.mysql.com/doc/refman/26.7/en/logical-operators.html">MySQL 26.7 · Logical Operators</a></li>
<li><a href="https://dev.mysql.com/doc/refman/26.7/en/string-comparison-functions.html#operator_like">MySQL 26.7 · LIKE</a></li>
<li><a href="https://dev.mysql.com/doc/refman/26.7/en/flow-control-functions.html">MySQL 26.7 · CASE</a></li>
<li><a href="https://www.iso.org/standard/76583.html">ISO/IEC 9075（SQL:2023）</a></li>
</ol>
]]>
    </content>
  </entry>
  <entry>
    <id>https://blog.chuwu.top/posts/python/python5/</id>
    <updated>2026-08-28T00:00:00.000Z</updated>
    <title>Python 札记 (5)</title>
    <author>
      <name>ChuwuYo</name>
    </author>
    <link href="https://blog.chuwu.top/posts/python/python5/" rel="alternate"/>
    <published>2026-08-28T00:00:00.000Z</published>
    <summary>字典与集合：映射与去重、视图方法、集合运算与 frozenset</summary>
    <content type="html" xml:base="https://blog.chuwu.top/posts/python/python5/">
      <![CDATA[<p>:::note
基于 Python 3。
:::</p>
<h2>字典 dict</h2>
<pre><code>user = {"name": "Alice", "age": 25}
print(user["name"])
user["age"] = 26
</code></pre>
<p><code>dict</code> 用键取值。键必须可哈希（<a href="https://docs.python.org/3/glossary.html#term-hashable">hashable</a>）：数字、字符串、元组可以；list、dict 不行。<code>1</code>、<code>1.0</code>、<code>True</code> 会当成同一个键。</p>
<h3>创建</h3>
<pre><code>empty = {}
also_empty = dict()

user = {"name": "Alice", "age": 25}
from_pairs = dict([("a", 1), ("b", 2)])
from_zip = dict(zip(["a", "b"], [1, 2]))
from_keys = dict.fromkeys(["x", "y"], 0)   # 值默认为 None
from_kwargs = dict(red=1, blue=2)          # 键须为合法标识符
</code></pre>
<p><code>{}</code> 是空 dict，不是空 set。空 set 必须写 <code>set()</code>。</p>
<p>自 3.7 起，dict <strong>保持插入顺序</strong>；更新已有键的值<strong>不改变</strong>该键在顺序中的位置；删除后再插入的键排到末尾。</p>
<h3>插入、查找、删除</h3>
<table>
<thead>
<tr>
<th>操作</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>d[key] = value</code></td>
<td>插入或更新</td>
</tr>
<tr>
<td><code>value = d[key]</code></td>
<td>取值；键不存在 → <code>KeyError</code></td>
</tr>
<tr>
<td><code>del d[key]</code></td>
<td>删除；键不存在 → <code>KeyError</code></td>
</tr>
<tr>
<td><code>key in d</code> / <code>key not in d</code></td>
<td>成员测试（测的是键）</td>
</tr>
<tr>
<td><code>len(d)</code></td>
<td>键值对个数</td>
</tr>
<tr>
<td><code>list(d)</code></td>
<td>键的 list（插入顺序）</td>
</tr>
</tbody>
</table>
<pre><code>scores = {"math": 90}
scores["english"] = 85
scores["math"] = 95          # 更新，顺序不变
print(scores["math"])        # 95
del scores["english"]
# print(scores["physics"])   # KeyError
</code></pre>
<h3>get 与 setdefault</h3>
<p><strong><code>d.get(key[, default])</code></strong> — 键存在则返回值，否则返回 <code>default</code>（省略时为 <code>None</code>），<strong>不抛</strong> <code>KeyError</code>：</p>
<pre><code>config = {"host": "localhost"}
port = config.get("port", 8080)
print(port)                  # 8080
print(config.get("host"))    # localhost
</code></pre>
<p><strong><code>d.setdefault(key[, default])</code></strong> — 键存在则返回值；不存在则插入 <code>key: default</code> 并返回 <code>default</code>（<code>default</code> 省略时为 <code>None</code>）：</p>
<pre><code>cache = {}
cache.setdefault("count", 0)
cache["count"] += 1
print(cache)                 # {'count': 1}
</code></pre>
<p>统计词频、按 key 分组时，常用 <code>setdefault</code> 初始化默认值。缺省值若是新 list 这类可变对象，更常用 <a href="https://docs.python.org/3/library/collections.html#collections.defaultdict"><code>collections.defaultdict</code></a>。</p>
<h3>keys、values、items</h3>
<p>三者返回<strong>视图对象</strong>（<a href="https://docs.python.org/3/library/stdtypes.html#dictionary-view-objects">Dictionary view objects</a>）：反映 dict 当前内容，不是静态快照。</p>
<pre><code>d = {"a": 1, "b": 2}
print(list(d.keys()))        # ['a', 'b']
print(list(d.values()))      # [1, 2]
print(list(d.items()))       # [('a', 1), ('b', 2)]

for k, v in d.items():
    print(k, v)
</code></pre>
<p>视图支持 <code>in</code>、<code>len</code>、迭代。两个 <code>dict.values()</code> 视图互相比永远为 <code>False</code>（即使来自同一 dict）。</p>
<p>自 3.8 起 dict 及其视图<strong>可逆</strong>：<code>reversed(d)</code>、<code>reversed(d.keys())</code> 等按插入顺序反序。</p>
<h3>合并（3.9+）</h3>
<pre><code>defaults = {"theme": "light", "lang": "zh"}
user_pref = {"lang": "en"}
merged = defaults | user_pref    # 新 dict，右侧优先
print(merged)                    # {'theme': 'light', 'lang': 'en'}

defaults |= user_pref            # 原地更新 defaults
</code></pre>
<h3>update、pop、copy</h3>
<table>
<thead>
<tr>
<th>方法</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>d.update(...)</code></td>
<td>批量更新，覆盖已有键</td>
</tr>
<tr>
<td><code>d.pop(key[, default])</code></td>
<td>删除并返回值</td>
</tr>
<tr>
<td><code>d.popitem()</code></td>
<td>删除并返回 LIFO 的 <code>(key, value)</code></td>
</tr>
<tr>
<td><code>d.clear()</code></td>
<td>清空</td>
</tr>
<tr>
<td><code>d.copy()</code></td>
<td>浅拷贝</td>
</tr>
</tbody>
</table>
<h3>字典推导式</h3>
<p>语法 <code>{key_expr: value_expr for ... in iterable if ...}</code>，与 list 推导式类似：</p>
<pre><code>squares = {x: x * x for x in range(5)}
print(squares)   # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
</code></pre>
<p><code>dict.fromkeys(iterable, value)</code> 的 <code>value</code> 若用可变对象（如 <code>[]</code>），所有键会共享<strong>同一</strong>对象；要各自独立应改用 dict 推导式。</p>
<h2>集合 set</h2>
<p><a href="https://docs.python.org/3/library/stdtypes.html#set-types-set-frozenset">Set Types — set, frozenset</a>：<strong>无序</strong>、<strong>元素不重复</strong>的可哈希对象集合。用途：成员测试、去重、交并差等。</p>
<h3>创建与去重</h3>
<pre><code>empty = set()                # 不能写 {}
tags = {"python", "blog", "python"}   # {'python', 'blog'}
from_list = set([1, 2, 2, 3])         # {1, 2, 3}
from_str = set("hello")               # {'h', 'e', 'l', 'o'}
</code></pre>
<pre><code>raw = [1, 2, 2, 3, 3, 3]
unique = list(set(raw))      # 顺序不保证
print(unique)
</code></pre>
<p>set <strong>不支持</strong>索引、切片；元素无固定顺序，<code>for</code> 迭代顺序实现相关，不要依赖。</p>
<h3>成员与长度</h3>
<pre><code>s = {1, 2, 3}
print(2 in s)      # True
print(len(s))      # 3
</code></pre>
<h3>集合运算：运算符与方法</h3>
<p>对 <strong>set ↔ set</strong>，运算符与对应方法等价；方法形式常可接受任意 iterable，运算符要求两侧为 set。</p>
<table>
<thead>
<tr>
<th>运算符</th>
<th>方法</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>s | t</code></td>
<td><code>s.union(t, ...)</code></td>
<td>并集</td>
</tr>
<tr>
<td><code>s &amp; t</code></td>
<td><code>s.intersection(t, ...)</code></td>
<td>交集</td>
</tr>
<tr>
<td><code>s - t</code></td>
<td><code>s.difference(t, ...)</code></td>
<td>差集（在 s 不在 t）</td>
</tr>
<tr>
<td><code>s ^ t</code></td>
<td><code>s.symmetric_difference(t)</code></td>
<td>对称差（恰在一侧）</td>
</tr>
<tr>
<td><code>s &lt;= t</code></td>
<td><code>s.issubset(t)</code></td>
<td>子集</td>
</tr>
<tr>
<td><code>s &gt;= t</code></td>
<td><code>s.issuperset(t)</code></td>
<td>超集</td>
</tr>
<tr>
<td></td>
<td><code>s.isdisjoint(t)</code></td>
<td>无交集为 True</td>
</tr>
</tbody>
</table>
<pre><code>a = {1, 2, 3}
b = {3, 4, 5}
print(a | b)   # {1, 2, 3, 4, 5}
print(a &amp; b)   # {3}
print(a - b)   # {1, 2}
print(a ^ b)   # {1, 2, 4, 5}
</code></pre>
<p><strong>原地</strong>更新（仅 <code>set</code>，非 <code>frozenset</code>）：</p>
<table>
<thead>
<tr>
<th>运算符</th>
<th>方法</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>s |= t</code></td>
<td><code>s.update(t, ...)</code></td>
</tr>
<tr>
<td><code>s &amp;= t</code></td>
<td><code>s.intersection_update(t, ...)</code></td>
</tr>
<tr>
<td><code>s -= t</code></td>
<td><code>s.difference_update(t, ...)</code></td>
</tr>
<tr>
<td><code>s ^= t</code></td>
<td><code>s.symmetric_difference_update(t)</code></td>
</tr>
</tbody>
</table>
<pre><code>skills = {"python", "git"}
skills |= {"docker", "python"}
print(skills)    # {'python', 'git', 'docker'}
</code></pre>
<h3>增删元素（可变 set）</h3>
<pre><code>s = {1, 2}
s.add(3)
s.remove(2)      # 不存在 → KeyError
s.discard(99)    # 不存在不报错
x = s.pop()      # 任意移除一项；空 set → KeyError
s.clear()
</code></pre>
<p><code>set</code> 与 <code>frozenset</code> 混合运算时，<strong>结果类型与左操作数一致</strong>。</p>
<h2>frozenset</h2>
<p>不可变、<strong>可哈希</strong>的集合。不能 <code>add</code> / <code>remove</code>，可作 dict 键或嵌套在 set 里：</p>
<pre><code>inner = frozenset([1, 2])
outer = {inner, frozenset([3])}
print(inner in outer)   # True

# { {1, 2} }              # TypeError: set 不能作元素
registry = {frozenset("ab"): "pair"}
</code></pre>
<p>构造：<code>frozenset()</code>、<code>frozenset(iterable)</code>。支持与 <code>set</code> 相同的只读运算（<code>union</code>、<code>intersection</code> 等及对应运算符）。</p>
<h2>容易踩的坑</h2>
<ol>
<li><strong><code>{}</code> 是 dict，<code>set()</code> 才是空 set。</strong></li>
<li><strong>dict 键必须可哈希</strong> —— 数字、字符串、tuple（元素皆哈希）可以；list、dict 不行。</li>
<li><strong>set 去重不保序</strong> —— 要保留首次出现顺序：<code>list(dict.fromkeys(seq))</code>（3.7+ dict 保序），比 <code>list(set(seq))</code> 稳。</li>
<li><strong>集合元素必须可哈希</strong> —— 同 dict 键；要存 list 可先 <code>tuple(...)</code>。</li>
</ol>
<hr />
<h2>附录：dict 方法</h2>
<p><a href="https://docs.python.org/3/library/stdtypes.html#mapping-types-dict">Mapping Types — dict</a>。</p>
<table>
<thead>
<tr>
<th>方法 / 操作</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>clear()</code></td>
<td>清空</td>
</tr>
<tr>
<td><code>copy()</code></td>
<td>浅拷贝</td>
</tr>
<tr>
<td><code>fromkeys(iterable, value=None)</code></td>
<td>类方法，键来自 iterable</td>
</tr>
<tr>
<td><code>get(key, default=None)</code></td>
<td>安全取值</td>
</tr>
<tr>
<td><code>items()</code></td>
<td><code>(key, value)</code> 视图</td>
</tr>
<tr>
<td><code>keys()</code></td>
<td>键视图</td>
</tr>
<tr>
<td><code>values()</code></td>
<td>值视图</td>
</tr>
<tr>
<td><code>pop(key[, default])</code></td>
<td>删除并返回</td>
</tr>
<tr>
<td><code>popitem()</code></td>
<td>LIFO 弹出一项</td>
</tr>
<tr>
<td><code>setdefault(key, default=None)</code></td>
<td>无则插入</td>
</tr>
<tr>
<td><code>update([mapping,] **kwargs)</code></td>
<td>批量更新</td>
</tr>
<tr>
<td><code>d | other</code> / <code>d |= other</code></td>
<td>合并（3.9+）</td>
</tr>
<tr>
<td><code>reversed(d)</code></td>
<td>逆序键（3.8+）</td>
</tr>
</tbody>
</table>
<p>另有：<code>d[key]</code>、<code>del d[key]</code>、<code>key in d</code>、<code>len(d)</code>、<code>iter(d)</code>。</p>
<h2>附录：set / frozenset 方法</h2>
<table>
<thead>
<tr>
<th>方法</th>
<th>set</th>
<th>frozenset</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>union</code> / <code>|</code></td>
<td>✓</td>
<td>✓</td>
</tr>
<tr>
<td><code>intersection</code> / <code>&amp;</code></td>
<td>✓</td>
<td>✓</td>
</tr>
<tr>
<td><code>difference</code> / <code>-</code></td>
<td>✓</td>
<td>✓</td>
</tr>
<tr>
<td><code>symmetric_difference</code> / <code>^</code></td>
<td>✓</td>
<td>✓</td>
</tr>
<tr>
<td><code>copy()</code></td>
<td>✓</td>
<td>✓</td>
</tr>
<tr>
<td><code>isdisjoint</code></td>
<td>✓</td>
<td>✓</td>
</tr>
<tr>
<td><code>issubset</code> / <code>&lt;=</code> / <code>&lt;</code></td>
<td>✓</td>
<td>✓</td>
</tr>
<tr>
<td><code>issuperset</code> / <code>&gt;=</code> / <code>&gt;</code></td>
<td>✓</td>
<td>✓</td>
</tr>
<tr>
<td><code>update</code> / <code>|=</code> 等原地运算</td>
<td>✓</td>
<td>—</td>
</tr>
<tr>
<td><code>add</code> / <code>remove</code> / <code>discard</code> / <code>pop</code> / <code>clear</code></td>
<td>✓</td>
<td>—</td>
</tr>
</tbody>
</table>
<p>另有：<code>x in s</code>、<code>len(s)</code>、<code>for x in s</code>。</p>
<hr />
<h2>参考</h2>
<p>官方文档（<a href="https://docs.python.org/zh-cn/3/">中文版</a>）：</p>
<ol>
<li><a href="https://docs.python.org/3/library/stdtypes.html#mapping-types-dict">Mapping Types — dict</a> — 键值操作、<code>get</code> / <code>setdefault</code>、视图</li>
<li><a href="https://docs.python.org/3/library/stdtypes.html#dictionary-view-objects">Dictionary view objects</a></li>
<li><a href="https://docs.python.org/3/library/stdtypes.html#set-types-set-frozenset">Set Types — set, frozenset</a> — 运算符与方法</li>
<li><a href="https://docs.python.org/3/tutorial/datastructures.html#dictionaries">Dictionaries</a> — 教程示例</li>
<li><a href="https://docs.python.org/3/tutorial/datastructures.html#sets">Sets</a> — 教程中的集合用法</li>
</ol>
<p>菜鸟教程：</p>
<ol>
<li><a href="https://www.runoob.com/python3/python3-data-structure.html">菜鸟教程 · 数据结构</a></li>
</ol>
<hr />
]]>
    </content>
  </entry>
  <entry>
    <id>https://blog.chuwu.top/posts/sql/mysql4/</id>
    <updated>2026-08-28T00:00:00.000Z</updated>
    <title>MySQL学习笔记 (4)</title>
    <author>
      <name>ChuwuYo</name>
    </author>
    <link href="https://blog.chuwu.top/posts/sql/mysql4/" rel="alternate"/>
    <published>2026-08-28T00:00:00.000Z</published>
    <summary>SELECT、DISTINCT、别名、ORDER BY、LIMIT</summary>
    <content type="html" xml:base="https://blog.chuwu.top/posts/sql/mysql4/">
      <![CDATA[<p>:::note
基于 MySQL 26.7。<a href="https://dev.mysql.com/doc/refman/26.7/en/select.html">SELECT</a>
:::</p>
<h2>SELECT 语句</h2>
<p><code>SELECT</code> 语句用于从数据库中选取数据。</p>
<p>结果存放在结果表中，称为结果集。</p>
<p>语法：</p>
<pre><code>SELECT column1, column2, ...
FROM table_name;
</code></pre>
<p>选取所有列：</p>
<pre><code>SELECT * FROM table_name;
</code></pre>
<p>下面的语句从 <code>employee</code> 表选取姓名和入职日期：</p>
<pre><code>SELECT last_name, hire_date
FROM   employee
WHERE  dept_no = 'D01'
ORDER  BY last_name;
</code></pre>
<h2>列别名</h2>
<p>可以用 <code>AS</code> 给列起别名：</p>
<pre><code>SELECT emp_id,
       salary * 12 AS year_pay
FROM   employee;
</code></pre>
<p><code>AS</code> 可以省略。</p>
<h2>DISTINCT</h2>
<p><code>SELECT DISTINCT</code> 用于返回互不相同的值。</p>
<pre><code>SELECT DISTINCT dept_no
FROM employee;
</code></pre>
<h2>ORDER BY</h2>
<p><code>ORDER BY</code> 用于对结果集排序。</p>
<pre><code>SELECT last_name, salary
FROM   employee
ORDER  BY salary DESC, last_name ASC;
</code></pre>
<ul>
<li><code>ASC</code>：升序（默认）</li>
<li><code>DESC</code>：降序</li>
</ul>
<p>可以按列名排序，也可以按多列排序。</p>
<p>注意：没有 <code>ORDER BY</code> 时，行的顺序不固定。</p>
<h2>LIMIT</h2>
<p><code>LIMIT</code> 用于限制返回的行数。</p>
<pre><code>SELECT last_name
FROM   employee
ORDER  BY emp_id
LIMIT  10;
</code></pre>
<p>跳过前面若干行再用 <code>OFFSET</code>：</p>
<pre><code>SELECT last_name
FROM   employee
ORDER  BY emp_id
LIMIT  2 OFFSET 2;
</code></pre>
<hr />
<h2>参考</h2>
<ol>
<li><a href="https://dev.mysql.com/doc/refman/26.7/en/select.html">MySQL 26.7 · SELECT</a></li>
<li><a href="https://dev.mysql.com/doc/refman/26.7/en/distinct-optimization.html">MySQL 26.7 · SELECT … DISTINCT</a></li>
<li><a href="https://dev.mysql.com/doc/refman/26.7/en/sorting-rows.html">MySQL 26.7 · Sorting Rows</a></li>
<li><a href="https://www.iso.org/standard/76583.html">ISO/IEC 9075（SQL:2023）</a></li>
</ol>
]]>
    </content>
  </entry>
  <entry>
    <id>https://blog.chuwu.top/posts/python/python4/</id>
    <updated>2026-08-27T00:00:00.000Z</updated>
    <title>Python 札记 (4)</title>
    <author>
      <name>ChuwuYo</name>
    </author>
    <link href="https://blog.chuwu.top/posts/python/python4/" rel="alternate"/>
    <published>2026-08-27T00:00:00.000Z</published>
    <summary>列表与元组：序列共性、可变列表操作、元组不可变、解包与 *rest</summary>
    <content type="html" xml:base="https://blog.chuwu.top/posts/python/python4/">
      <![CDATA[<p>:::note
基于 Python 3。
:::</p>
<h2>列表</h2>
<pre><code>fruits = ["apple", "banana", "cherry"]
print(fruits[0])       # apple
print(fruits[-1])      # cherry
print(fruits[1:3])     # ['banana', 'cherry']
print("banana" in fruits)
print(len(fruits))
</code></pre>
<p><code>list</code> 有序、可变。下标从 0 起，负数从末尾数。切片左闭右开。</p>
<p>list、tuple、字符串都能做这些事（<a href="https://docs.python.org/3/library/stdtypes.html#common-sequence-operations">Common Sequence Operations</a>）：<code>in</code>、<code>+</code>、<code>*</code>、<code>[]</code>、切片、<code>len</code>、<code>min</code> / <code>max</code>。</p>
<h3>索引与切片</h3>
<p>负索引 <code>i</code> 等价于 <code>len(s) + i</code>。切片 <code>[i:j]</code> 在 <code>i &gt;= j</code> 时为空；省略 <code>i</code> 视为 <code>0</code>，省略 <code>j</code> 视为 <code>len(s)</code>。带步长 <code>[i:j:k]</code> 时，<code>k</code> 为负则方向反向，且 <code>i</code>、<code>j</code> 会被截断到合法范围。</p>
<pre><code>nums = [0, 1, 2, 3, 4, 5]
print(nums[::2])    # [0, 2, 4]
print(nums[::-1])   # 新 list，原 list 不变
print(nums[4:1:-1]) # [4, 3, 2]
</code></pre>
<h3>拼接与重复</h3>
<p><code>s + t</code> 与 <code>s * n</code> 都会产生新对象。对可变序列反复使用 <code>+=</code> 拼接效率较低，可以使用 <code>extend</code> 或 <code>str.join</code>。</p>
<p><code>[[]] * 3</code> 会使三个元素指向同一个内层 list，修改其中一个，其余也会变化。若需要三个独立的空 list，使用 <code>[[] for _ in range(3)]</code>。</p>
<h3>count 与 index</h3>
<p>序列还支持（部分类型对 <code>start</code>/<code>stop</code> 有限制）：</p>
<pre><code>letters = ["a", "b", "c", "b"]
print(letters.count("b"))   # 2
print(letters.index("c"))   # 2
# letters.index("z")        # ValueError
</code></pre>
<h2>列表的增删改</h2>
<p><code>list</code> 可原地增删改（<a href="https://docs.python.org/3/library/stdtypes.html#mutable-sequence-types">Mutable Sequence Types</a>）：</p>
<table>
<thead>
<tr>
<th>操作</th>
<th>效果</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>s[i] = x</code></td>
<td>替换下标 <code>i</code></td>
</tr>
<tr>
<td><code>del s[i]</code></td>
<td>删除下标 <code>i</code></td>
</tr>
<tr>
<td><code>s[i:j] = t</code></td>
<td>用可迭代对象 <code>t</code> 替换切片</td>
</tr>
<tr>
<td><code>del s[i:j]</code></td>
<td>删除切片（同 <code>s[i:j] = []</code>）</td>
</tr>
<tr>
<td><code>s += t</code></td>
<td>原地扩展，大致同 <code>s[len(s):len(s)] = t</code></td>
</tr>
<tr>
<td><code>s *= n</code></td>
<td>原地重复 <code>n</code> 次</td>
</tr>
</tbody>
</table>
<h3>创建列表</h3>
<pre><code>empty = []
from_literal = [1, 2, 3]
from_iter = list("abc")          # ['a', 'b', 'c']
from_range = list(range(3))      # [0, 1, 2]
</code></pre>
<p><code>list(iterable)</code> 若参数已是 list，会返回<strong>浅拷贝</strong>（类似 <code>iterable[:]</code>）。</p>
<h3>常用方法</h3>
<p><strong><code>append(x)</code></strong> — 在末尾追加<strong>一个</strong>元素（不是展开可迭代对象）：</p>
<pre><code>stack = [1, 2]
stack.append(3)       # [1, 2, 3]
stack.append([4, 5])  # [1, 2, 3, [4, 5]]
</code></pre>
<p><strong><code>extend(iterable)</code></strong> — 把可迭代对象里的元素逐个追加：</p>
<pre><code>a = [1, 2]
a.extend([3, 4])      # [1, 2, 3, 4]
# 等价于 a[len(a):len(a)] = [3, 4]
</code></pre>
<p><strong><code>insert(index, x)</code></strong> — 在指定位置插入；等价于 <code>s[index:index] = [x]</code>。</p>
<p><strong><code>pop([index=-1])</code></strong> — 取出并删除元素，默认最后一个；空序列或越界会 <code>IndexError</code>。</p>
<p><strong><code>remove(value)</code></strong> — 删除<strong>第一个</strong>等于 <code>value</code> 的项；找不到则 <code>ValueError</code>（按 <code>==</code> 比较）。</p>
<p><strong><code>sort(*, key=None, reverse=False)</code></strong> — <strong>原地</strong>排序，只使用 <code>&lt;</code> 比较；返回 <code>None</code>。稳定排序。要新列表用内置 <code>sorted(s)</code>。</p>
<p><strong><code>reverse()</code></strong> — 原地反转，返回 <code>None</code>。</p>
<p><strong><code>copy()</code></strong> — 浅拷贝，等价于 <code>s[:]</code>（Python 3.3+）。</p>
<pre><code>items = [3, 1, 4, 1, 5]
items.sort()
print(items)              # [1, 1, 3, 4, 5]
items.sort(reverse=True)
print(items)              # [5, 4, 3, 1, 1]

dup = items.copy()
dup.reverse()
print(dup)                # [1, 1, 3, 4, 5]
</code></pre>
<p>原地方法（<code>sort</code>、<code>reverse</code>、<code>append</code> 等）多数返回 <code>None</code>；不要写 <code>x = lst.sort()</code>。</p>
<h3>浅拷贝与嵌套</h3>
<p><code>copy()</code> 与 <code>[:]</code> 只复制<strong>一层</strong>。内层可变对象仍共享：</p>
<pre><code>matrix = [[1], [2]]
shadow = matrix.copy()
shadow[0].append(9)
print(matrix)   # [[1, 9], [2]] — 内层 list 共用
</code></pre>
<p>需要独立嵌套结构时，用 <code>copy.deepcopy()</code>。</p>
<h2>元组 tuple：不可变序列</h2>
<p>tuple 创建后<strong>不能</strong>通过 <code>t[i] = x</code> 改元素，也没有 <code>append</code>、<code>sort</code> 等原地方法。优势：可哈希（元素都哈希时）、占内存通常更小、语义上表达「固定字段组合」。</p>
<h3>创建与「逗号才是 tuple」</h3>
<pre><code>empty = ()
singleton = (42,)      # 单元素必须带逗号
not_tuple = (42)     # 这是 int，括号只是分组
pair = 1, 2          # 逗号构成 tuple，括号可省略
from_iter = tuple([1, 2, 3])
</code></pre>
<p><strong>真正构成 tuple 的是逗号</strong>，括号常可省略；空 tuple 必须是 <code>()</code>，单元素时 <code>(a,)</code> 避免与表达式分组混淆。</p>
<h3>不可变 ≠ 内容全不可变</h3>
<p>tuple 不能换引用，但若元素是可变对象，<strong>对象内部</strong>仍可改：</p>
<pre><code>t = ([1, 2], "ok")
t[0].append(3)   # 合法
print(t)         # ([1, 2, 3], 'ok')
# t[0] = []      # TypeError: 不能改 tuple 里的引用
</code></pre>
<p>含不可哈希元素的 tuple 不能作 dict 键或 set 元素。</p>
<h3>哈希与 dict 键</h3>
<p>不可变且元素均可哈希的 tuple 可 <code>hash()</code>，能当 dict 键：</p>
<pre><code>point_index = {(0, 0): "origin", (1, 0): "east"}
print(point_index[(0, 0)])
</code></pre>
<h2>解包</h2>
<h3>平行赋值</h3>
<pre><code>a, b, c = [1, 2, 3]
a, b = b, a          # 交换，无需临时变量
</code></pre>
<p>左右两侧个数必须一致，否则 <code>ValueError</code>。</p>
<h3>带 * 的扩展解包</h3>
<p><a href="https://peps.python.org/pep-3132/">PEP 3132</a> / <a href="https://peps.python.org/pep-0448/">PEP 448</a>：一个带 <code>*</code> 的目标收集「剩余」元素为 <strong>list</strong>：</p>
<pre><code>first, *middle, last = [1, 2, 3, 4, 5]
print(first, middle, last)   # 1 [2, 3, 4] 5

head, *tail = "Python"
print(head, tail)            # P ['y', 't', 'h', 'o', 'n']
</code></pre>
<p>调用函数时，<code>*</code> 把列表展开成多个参数：<code>print(*[1, 2, 3])</code> 相当于 <code>print(1, 2, 3)</code>。</p>
<h3>忽略部分值</h3>
<p>用 <code>_</code> 作占位（约定俗成，不是语法）：</p>
<pre><code>name, _, city = ("Alice", 28, "Seoul")
</code></pre>
<h2>list 与 tuple 怎么选</h2>
<table>
<thead>
<tr>
<th>场景</th>
<th>倾向</th>
</tr>
</thead>
<tbody>
<tr>
<td>长度或内容会变</td>
<td><code>list</code></td>
</tr>
<tr>
<td>固定字段、函数多返回值</td>
<td><code>tuple</code></td>
</tr>
<tr>
<td>作 dict 键 / set 元素</td>
<td>元素可哈希的 <code>tuple</code></td>
</tr>
<tr>
<td>大量只读数据</td>
<td><code>tuple</code> 略省内存</td>
</tr>
</tbody>
</table>
<p>需要「命名访问」时，官方推荐 <a href="https://docs.python.org/3/library/collections.html#collections.namedtuple"><code>collections.namedtuple</code></a> 或 <a href="https://docs.python.org/3/library/dataclasses.html"><code>dataclass</code></a>。</p>
<hr />
<h2>附录：list 可变序列方法</h2>
<p><a href="https://docs.python.org/3/library/stdtypes.html#mutable-sequence-types">Mutable Sequence Types</a> 与 <a href="https://docs.python.org/3/library/stdtypes.html#lists">Lists</a>。<code>list</code> 另独有 <code>sort()</code>。</p>
<table>
<thead>
<tr>
<th>方法</th>
<th>说明</th>
<th>返回值</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>append(value)</code></td>
<td>末尾追加一项</td>
<td><code>None</code></td>
</tr>
<tr>
<td><code>clear()</code></td>
<td>删除全部（同 <code>del s[:]</code>）</td>
<td><code>None</code></td>
</tr>
<tr>
<td><code>copy()</code></td>
<td>浅拷贝（同 <code>s[:]</code>）</td>
<td>新 <code>list</code></td>
</tr>
<tr>
<td><code>extend(iterable)</code></td>
<td>追加 iterable 中各项</td>
<td><code>None</code></td>
</tr>
<tr>
<td><code>insert(index, value)</code></td>
<td>在 index 处插入</td>
<td><code>None</code></td>
</tr>
<tr>
<td><code>pop(index=-1)</code></td>
<td>取出并删除 index 处项</td>
<td>被删元素</td>
</tr>
<tr>
<td><code>remove(value)</code></td>
<td>删除首个等于 value 的项</td>
<td><code>None</code></td>
</tr>
<tr>
<td><code>reverse()</code></td>
<td>原地反转</td>
<td><code>None</code></td>
</tr>
<tr>
<td><code>sort(*, key=None, reverse=False)</code></td>
<td>原地排序（仅 list）</td>
<td><code>None</code></td>
</tr>
</tbody>
</table>
<p>序列共有 <code>count(value)</code>、<code>index(value[, start[, stop]])</code>。</p>
<h3>tuple 可用操作小结</h3>
<p>tuple 仅实现<strong>不可变</strong>序列操作：<code>+</code>、<code>*</code>、<code>in</code>、<code>len</code>、<code>min</code>、<code>max</code>、索引切片、<code>count</code>、<code>index</code>。无 <code>append</code>、<code>sort</code> 等。</p>
<hr />
<h2>参考</h2>
<p>官方文档（<a href="https://docs.python.org/zh-cn/3/">中文版</a>）：</p>
<ol>
<li><a href="https://docs.python.org/3/library/stdtypes.html#sequence-types-list-tuple-range">Sequence Types — list, tuple, range</a> — 共性操作、可变/不可变区分</li>
<li><a href="https://docs.python.org/3/library/stdtypes.html#common-sequence-operations">Common Sequence Operations</a> — 索引、切片、拼接、重复</li>
<li><a href="https://docs.python.org/3/library/stdtypes.html#mutable-sequence-types">Mutable Sequence Types</a> — 原地赋值、删除、方法表</li>
<li><a href="https://docs.python.org/3/library/stdtypes.html#lists">Lists</a> — 构造、<code>sort()</code> 关键字参数</li>
<li><a href="https://docs.python.org/3/library/stdtypes.html#tuples">Tuples</a> — 单元素逗号、括号与逗号</li>
<li><a href="https://docs.python.org/3/tutorial/datastructures.html#more-on-lists">More on Lists</a></li>
<li><a href="https://docs.python.org/3/tutorial/datastructures.html#tuples-and-sequences">Tuples and Sequences</a></li>
</ol>
<p>PEP：</p>
<ol>
<li><a href="https://peps.python.org/pep-3132/">PEP 3132 — Extended Iterable Unpacking</a></li>
<li><a href="https://peps.python.org/pep-0448/">PEP 448 — Additional Unpacking Generalizations</a></li>
</ol>
<p>菜鸟教程：</p>
<ol>
<li><a href="https://www.runoob.com/python3/python3-data-structure.html">菜鸟教程 · 数据结构</a> — list / tuple 章节</li>
</ol>
<hr />
]]>
    </content>
  </entry>
  <entry>
    <id>https://blog.chuwu.top/posts/sql/mysql3/</id>
    <updated>2026-08-27T00:00:00.000Z</updated>
    <title>MySQL学习笔记 (3)</title>
    <author>
      <name>ChuwuYo</name>
    </author>
    <link href="https://blog.chuwu.top/posts/sql/mysql3/" rel="alternate"/>
    <published>2026-08-27T00:00:00.000Z</published>
    <summary>CREATE SCHEMA、CREATE TABLE、约束、ALTER、DROP、索引</summary>
    <content type="html" xml:base="https://blog.chuwu.top/posts/sql/mysql3/">
      <![CDATA[<p>:::note
基于 MySQL 26.7。<a href="https://dev.mysql.com/doc/refman/26.7/en/create-table.html">CREATE TABLE</a>
:::</p>
<h2>创建数据库</h2>
<pre><code>CREATE SCHEMA scoredb;
</code></pre>
<p>然后选中这个数据库：</p>
<pre><code>USE scoredb;
</code></pre>
<p>以后可以用 <code>scoredb.employee</code> 这种写法指定库名和表名。</p>
<h2>CREATE TABLE</h2>
<p><code>CREATE TABLE</code> 语句用于创建表。</p>
<pre><code>CREATE TABLE department (
    dept_no   CHAR(3)      NOT NULL,
    dept_name VARCHAR(50)  NOT NULL,
    director  VARCHAR(50)  DEFAULT '院长',
    CONSTRAINT department_pk PRIMARY KEY (dept_no),
    CONSTRAINT department_name_un UNIQUE (dept_name)
);

CREATE TABLE employee (
    emp_id     INTEGER       NOT NULL,
    last_name  VARCHAR(40)   NOT NULL,
    first_name VARCHAR(40),
    dept_no    CHAR(3),
    hire_date  DATE,
    salary     NUMERIC(12, 2),
    manager_id INTEGER,
    is_active  BOOLEAN,
    job_id     VARCHAR(10),
    phone      VARCHAR(20),
    title      VARCHAR(50),
    CONSTRAINT employee_pk PRIMARY KEY (emp_id),
    CONSTRAINT emp_id_ck CHECK (emp_id &gt; 0),
    CONSTRAINT employee_dept_fk
        FOREIGN KEY (dept_no) REFERENCES department (dept_no)
);
</code></pre>
<p>约束可以写在列后面，也可以写成 <code>CONSTRAINT 名字 …</code>。</p>
<p>先插入部门，再插入员工（因为员工的部门编号要在部门表里存在）：</p>
<pre><code>INSERT INTO department (dept_no, dept_name, director)
VALUES ('D01', '教务处', '院长'),
       ('D02', '人事处', '院长'),
       ('D03', '财务处', '院长');

INSERT INTO employee (
    emp_id, last_name, first_name, dept_no, hire_date,
    salary, manager_id, is_active, job_id, phone, title
)
VALUES
    (101, '张三', '明', 'D01', DATE '2018-03-01',
     9000, NULL, TRUE, 'T01', '13800000001', '讲师'),
    (102, '李四', '华', 'D01', DATE '2019-07-15',
     12000, 101, TRUE, 'T01', NULL, '副教授'),
    (103, '王五', '强', 'D02', DATE '2021-01-10',
     7500, 101, TRUE, 'T02', '13800000003', '助教'),
    (104, '赵六', '芳', 'D02', DATE '2016-11-20',
     15000, NULL, FALSE, 'T02', '13800000004', '教授');
</code></pre>
<h2>约束</h2>
<p>约束用来限制表中的数据。</p>
<table>
<thead>
<tr>
<th>约束</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>NOT NULL</code></td>
<td>列不能是 NULL</td>
</tr>
<tr>
<td><code>UNIQUE</code></td>
<td>列的值不能重复</td>
</tr>
<tr>
<td><code>PRIMARY KEY</code></td>
<td>主键：非空且唯一，一张表一个</td>
</tr>
<tr>
<td><code>FOREIGN KEY</code></td>
<td>外键：值必须在另一张表里存在</td>
</tr>
<tr>
<td><code>CHECK</code></td>
<td>值必须满足条件，例如 <code>emp_id &gt; 0</code></td>
</tr>
<tr>
<td><code>DEFAULT</code></td>
<td>插入时没给值就用默认值</td>
</tr>
</tbody>
</table>
<p>外键列可以是 NULL（如果该列允许 NULL）。</p>
<h2>ALTER TABLE</h2>
<p><code>ALTER TABLE</code> 语句用于修改表。</p>
<pre><code>ALTER TABLE employee ADD COLUMN email VARCHAR(100);
ALTER TABLE employee ADD CONSTRAINT employee_email_un UNIQUE (email);
ALTER TABLE employee DROP INDEX employee_email_un;
ALTER TABLE employee DROP COLUMN email;
ALTER TABLE department ALTER COLUMN director SET DEFAULT '院长';
ALTER TABLE department ALTER COLUMN director DROP DEFAULT;
</code></pre>
<h2>DROP TABLE</h2>
<p><code>DROP TABLE</code> 语句用于删除表。</p>
<pre><code>DROP TABLE employee, department;
</code></pre>
<p>有外键时，先删除子表，或者先删除外键。</p>
<h2>索引</h2>
<p><code>CREATE INDEX</code> 语句用于创建索引，加快查找。</p>
<pre><code>CREATE INDEX employee_name_idx ON employee (last_name);
</code></pre>
<p>主键和 <code>UNIQUE</code> 已经带有索引。删除索引：</p>
<pre><code>DROP INDEX employee_name_idx ON employee;
</code></pre>
<hr />
<h2>参考</h2>
<ol>
<li><a href="https://dev.mysql.com/doc/refman/26.7/en/create-database.html">MySQL 26.7 · CREATE SCHEMA</a></li>
<li><a href="https://dev.mysql.com/doc/refman/26.7/en/create-table.html">MySQL 26.7 · CREATE TABLE</a></li>
<li><a href="https://dev.mysql.com/doc/refman/26.7/en/constraints.html">MySQL 26.7 · Constraints</a></li>
<li><a href="https://dev.mysql.com/doc/refman/26.7/en/create-table-foreign-keys.html">MySQL 26.7 · FOREIGN KEY</a></li>
<li><a href="https://dev.mysql.com/doc/refman/26.7/en/alter-table.html">MySQL 26.7 · ALTER TABLE</a></li>
<li><a href="https://dev.mysql.com/doc/refman/26.7/en/create-index.html">MySQL 26.7 · CREATE INDEX</a></li>
<li><a href="https://www.iso.org/standard/76583.html">ISO/IEC 9075（SQL:2023）</a></li>
</ol>
]]>
    </content>
  </entry>
  <entry>
    <id>https://blog.chuwu.top/posts/python/python3/</id>
    <updated>2026-08-26T00:00:00.000Z</updated>
    <title>Python 札记 (3)</title>
    <author>
      <name>ChuwuYo</name>
    </author>
    <link href="https://blog.chuwu.top/posts/python/python3/" rel="alternate"/>
    <published>2026-08-26T00:00:00.000Z</published>
    <summary>int/float/complex 与进制、地板除与幂、math 与 random 常用函数、字符串不可变与切片、转义与 raw、格式化与 f-string、常用 str 方法</summary>
    <content type="html" xml:base="https://blog.chuwu.top/posts/python/python3/">
      <![CDATA[<p>:::note
基于 Python 3。
:::</p>
<h2>数字类型总览</h2>
<p>Python 3 内置三种数值类型（<a href="https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex">Numeric Types</a>）：</p>
<table>
<thead>
<tr>
<th>类型</th>
<th>示例</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>int</code></td>
<td><code>0</code> <code>42</code> <code>-7</code></td>
<td>任意精度整数，仅受内存限制</td>
</tr>
<tr>
<td><code>float</code></td>
<td><code>3.14</code> <code>1e-3</code></td>
<td>双精度浮点（实现依赖平台，通常 IEEE 754）</td>
</tr>
<tr>
<td><code>complex</code></td>
<td><code>3+4j</code> <code>complex(2, -1)</code></td>
<td>实部 + 虚部，虚部后缀 <code>j</code></td>
</tr>
</tbody>
</table>
<pre><code>type(1)             # int
type(1.0)           # float
type(2+3j)          # complex
(3+4j).real         # 3.0
(3+4j).imag         # 4.0
</code></pre>
<p><code>bool</code> 是 <code>int</code> 子类；算术里 <code>True</code>/<code>False</code> 当 1/0。精确小数用 <code>decimal.Decimal</code>、<code>fractions.Fraction</code>。</p>
<h2>字面量与进制</h2>
<p><code>int</code> 字面量可带进制前缀（<a href="https://docs.python.org/3/reference/lexical_analysis.html#integer-literals">Integer literals</a>）；数字里可用下划线分组（<a href="https://peps.python.org/pep-0515/">PEP 515</a>）：<code>1_000_000</code>。</p>
<table>
<thead>
<tr>
<th>前缀</th>
<th>含义</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>0b</code> / <code>0B</code></td>
<td>二进制</td>
<td><code>0b1010</code> → 10</td>
</tr>
<tr>
<td><code>0o</code> / <code>0O</code></td>
<td>八进制</td>
<td><code>0o12</code> → 10</td>
</tr>
<tr>
<td><code>0x</code> / <code>0X</code></td>
<td>十六进制</td>
<td><code>0xFF</code> → 255</td>
</tr>
</tbody>
</table>
<pre><code>int("1010", 2)      # 10，任意进制字符串 → int
bin(10)             # '0b1010'
oct(10)             # '0o12'
hex(255)            # '0xff'
</code></pre>
<p>浮点可用科学计数：<code>1.5e2</code> → <code>150.0</code>。不要使用 <code>0.1 + 0.2 == 0.3</code> 判断浮点数是否相等；精确小数使用 <code>decimal</code>，或改用整数运算。</p>
<h2>算术：//、%、**</h2>
<p>Python 3 的 <code>/</code> 恒为真除，结果是 <code>float</code>。<code>//</code> 是地板除（向负无穷取整），<code>%</code> 与之配套。<code>**</code> 是幂。</p>
<pre><code>7 / 3               # 2.333...，真除恒为 float
7 // 3              # 2，地板除
-7 // 3             # -3（向负无穷，不是向零）
7 % 3               # 1
-7 % 3              # 2，与 // 配套
2 ** 10             # 1024
pow(2, 8)           # 256，内置函数
pow(2, 8, 5)        # 1，模幂：2**8 % 5
</code></pre>
<p><code>divmod(a, b)</code> 返回 <code>(a // b, a % b)</code> 一对。复数不支持 <code>//</code> 和 <code>%</code>。</p>
<p>混合类型：<code>int</code> 与 <code>float</code> 运算结果为 <code>float</code>；<code>int</code> 与 <code>complex</code> 得 <code>complex</code>。</p>
<h2>math 模块（常用）</h2>
<p>标准库 <a href="https://docs.python.org/3/library/math.html">math</a> 提供<strong>实数</strong>数学函数（多数要求 <code>float</code>，返回 <code>float</code>）。</p>
<pre><code>import math
math.sqrt(2)          # 1.414...
math.pi               # 常量
math.e
math.floor(3.7)       # 3
math.ceil(3.2)        # 4
math.fabs(-3)         # 3.0
math.log(100, 10)     # 2.0，默认自然对数 math.log(x)
math.sin(math.pi / 2) # 1.0
math.hypot(3, 4)      # 5.0，sqrt(x*x + y*y)
</code></pre>
<p><code>math.inf</code>、<code>math.nan</code> 对应浮点无穷与非数。<code>nan</code> 与任何值（包括自身）比较都不为真，用 <code>math.isnan(x)</code> 判断。</p>
<h2>random 模块（常用）</h2>
<p><a href="https://docs.python.org/3/library/random.html">random</a> 提供<strong>伪随机</strong>数（非密码学安全；安全场景用 <code>secrets</code>）。另有分布采样如 <code>gauss</code>、<code>uniform</code>。</p>
<pre><code>import random
random.random()       # [0.0, 1.0) 均匀 float
random.randint(1, 6)  # 闭区间 [1, 6] 整数
random.randrange(0, 10, 2)  # 0,2,4,6,8
random.choice(['a', 'b', 'c'])
items = [1, 2, 3, 4]
random.shuffle(items) # 原地打乱
random.sample(range(100), 5)  # 无重复抽样
random.seed(42)       # 可复现（调试）；生产环境慎固定种子
</code></pre>
<h2>字符串：不可变文本序列</h2>
<p><code>str</code> 是 Unicode 码点序列（<a href="https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str">Text Sequence Type</a>），<strong>不可变</strong>。引号成对即可：<code>'</code>、<code>"</code>、<code>'''</code>、<code>"""</code>；内容相同时等价。</p>
<pre><code>s = "Python"
# s[0] = "p"        # TypeError
s = "python"        # 新对象
</code></pre>
<p><code>len(s)</code> 返回字符个数。<a href="https://docs.python.org/3/howto/unicode.html">Unicode HOWTO</a></p>
<h2>索引与切片</h2>
<p>下标从 <strong>0</strong> 起；负下标从末尾计：<code>-1</code> 是最后一个。</p>
<pre><code>s = "Python"
s[0]                # 'P'
s[-1]               # 'n'
s[1:4]              # 'yth'，左闭右开
s[:3]               # 'Pyt'
s[3:]               # 'hon'
s[::2]              # 'Pto'，步长 2
s[::-1]             # 'nohtyP'，反转
</code></pre>
<p>切片<strong>总是</strong>产生新 <code>str</code>，不修改原串。越界单下标 <code>IndexError</code>；切片边界可宽松（超出当作端点）。</p>
<h2>转义与 raw 字符串</h2>
<p>反斜杠 <code>\</code> 引入转义（<a href="https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals">String and Bytes literals</a>）：</p>
<table>
<thead>
<tr>
<th>转义</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>\n</code></td>
<td>换行</td>
</tr>
<tr>
<td><code>\t</code></td>
<td>制表</td>
</tr>
<tr>
<td><code>\\</code></td>
<td>反斜杠</td>
</tr>
<tr>
<td><code>\'</code> <code>\"</code></td>
<td>引号</td>
</tr>
<tr>
<td><code>\uXXXX</code></td>
<td>Unicode 码点（16 位）</td>
</tr>
<tr>
<td><code>\UXXXXXXXX</code></td>
<td>Unicode 码点（32 位）</td>
</tr>
</tbody>
</table>
<pre><code>print("line1\nline2")
print(r"C:\new\test")   # raw：不处理转义，\\ 仍保留
# r"C:\new"  末尾 \ 可能引发语法问题，注意结尾反斜杠
</code></pre>
<p>路径、正则模式常用 <code>r"..."</code>；raw 字面量不能以奇数个 <code>\</code> 结尾。</p>
<h2>拼接与重复</h2>
<pre><code>"Hello, " + "World"  # 连接
"*" * 30              # 重复 30 次
# "a" + 1             # TypeError，需 str(1)
</code></pre>
<p>大列表拼串时，<code>"".join(iterable)</code> 往往比循环 <code>+</code> 高效。</p>
<h2>格式化：%、str.format、f-string</h2>
<p><code>%</code> 与 <code>str.format</code> 仍可用；f-string（<a href="https://peps.python.org/pep-0498/">PEP 498</a>，3.6+）把表达式写进字面量。</p>
<h3>% 格式化（旧式）</h3>
<pre><code>"%.2f %s" % (3.14159, "pi")     # '3.14 pi'
"%d" % 42
"%(name)s" % {"name": "Alice"}
</code></pre>
<p>映射与 <code>%</code> 运算符见 <a href="https://docs.python.org/3/library/stdtypes.html#printf-style-string-formatting">printf-style String Formatting</a>。</p>
<h3>str.format</h3>
<pre><code>"{} + {} = {}".format(1, 2, 3)
"{1} {0}".format("world", "hello")
"{name}: {age}".format(name="Bob", age=20)
"{:.2f}".format(3.14159)
"{:&gt;10}".format("右对齐")
</code></pre>
<p>格式规格见 <a href="https://docs.python.org/3/library/string.html#formatstrings">Format String Syntax</a>。</p>
<h3>f-string（推荐）</h3>
<pre><code>name = "Alice"
age = 30
f"{name} is {age} years old"
f"{3.14159:.2f}"
f"{2 + 3}"
f"{name!r}"          # 等价于 repr(name)
f"{name.lower()=}"   # 3.8+ 调试
</code></pre>
<p>花括号内可以是表达式；<strong>不能</strong>嵌套同一引号未转义的花括号。3.12 起允许更自由的引号嵌套与多行（<a href="https://docs.python.org/3/whatsnew/3.12.html">What's New in 3.12</a>）。</p>
<h2>常用 str 方法</h2>
<h3>split 与 join</h3>
<pre><code>"a,b,c".split(",")           # ['a', 'b', 'c']
"a  b  c".split()             # 默认按任意空白切
",".join(['a', 'b', 'c'])     # 'a,b,c'
</code></pre>
<h3>strip 系列</h3>
<pre><code>"  hello  ".strip()           # 'hello'
"xxxhelloxxx".strip("x")      # 'hello'
"  hi".lstrip()
"hi  ".rstrip()
</code></pre>
<h3>replace</h3>
<pre><code>"hello".replace("l", "L")     # 'heLLo'
"hello".replace("l", "L", 1)  # 只换 1 次
</code></pre>
<h3>find / index</h3>
<pre><code>"python".find("th")           # 2，找不到 -1
"python".index("th")          # 2，找不到 ValueError
"python".count("o")           # 1
</code></pre>
<h3>startswith / endswith</h3>
<pre><code>"hello.py".startswith("hello")
"hello.py".endswith(".py")
"hello.py".removesuffix(".py")  # 3.9+，'hello'
</code></pre>
<h3>大小写与判断</h3>
<pre><code>"Hello".lower()
"Hello".upper()
"hello world".title()
"123".isdigit()
"abc".isalpha()
"  ".isspace()
</code></pre>
<h3>encode</h3>
<pre><code>"中文".encode("utf-8")        # bytes
b"\xe4\xb8\xad".decode("utf-8")
</code></pre>
<p><code>encode</code>/<code>decode</code> 可用 <code>errors=</code>（如 <code>strict</code>、<code>replace</code>、<code>ignore</code>）；编码名见 <a href="https://docs.python.org/3/library/codecs.html">codecs</a>。</p>
<h2>字符串与 bytes</h2>
<p><code>str</code> 是文本，<code>bytes</code> 是字节序列（0–255）。网络、文件二进制读写要在二者间<strong>显式</strong>编解码，Python 3 <strong>不会</strong>自动混用。<code>b"..."</code> 字面量只能写 ASCII 或转义；<code>bytes</code> 下标得到的是 <strong>int</strong>。需要原地改用 <code>bytearray</code>。</p>
<pre><code>text = "你好"
data = text.encode("utf-8")
text2 = data.decode("utf-8")
type(data)          # bytes
ord("中")           # 码点
chr(20013)          # '中'
</code></pre>
<hr />
<h2>附录：math 模块函数表</h2>
<p><a href="https://docs.python.org/3/library/math.html">math — Mathematical functions</a>：</p>
<table>
<thead>
<tr>
<th>函数 / 常量</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>pi</code>, <code>e</code>, <code>tau</code>, <code>inf</code>, <code>nan</code></td>
<td>常数</td>
</tr>
<tr>
<td><code>ceil(x)</code>, <code>floor(x)</code>, <code>trunc(x)</code></td>
<td>取整方向不同</td>
</tr>
<tr>
<td><code>fabs(x)</code></td>
<td>绝对值（float）</td>
</tr>
<tr>
<td><code>factorial(n)</code></td>
<td>n!</td>
</tr>
<tr>
<td><code>gcd(a, b)</code>, <code>lcm(a, b)</code></td>
<td>最大公约数 / 最小公倍数（3.9+ lcm）</td>
</tr>
<tr>
<td><code>sqrt(x)</code>, <code>pow(x, y)</code></td>
<td>平方根 / x^y</td>
</tr>
<tr>
<td><code>exp(x)</code>, <code>log(x[, base])</code>, <code>log10(x)</code>, <code>log2(x)</code></td>
<td>指数与对数</td>
</tr>
<tr>
<td><code>sin</code>, <code>cos</code>, <code>tan</code>, <code>asin</code>, <code>acos</code>, <code>atan</code>, <code>atan2</code></td>
<td>三角</td>
</tr>
<tr>
<td><code>degrees(x)</code>, <code>radians(x)</code></td>
<td>弧度 ↔ 角度</td>
</tr>
<tr>
<td><code>hypot(x, y)</code></td>
<td>sqrt(x²+y²)</td>
</tr>
<tr>
<td><code>isfinite(x)</code>, <code>isinf(x)</code>, <code>isnan(x)</code></td>
<td>浮点检测</td>
</tr>
<tr>
<td><code>copysign(x, y)</code>, <code>fmod(x, y)</code>, <code>remainder(x, y)</code></td>
<td>符号与余数</td>
</tr>
</tbody>
</table>
<h2>附录：random 模块函数表</h2>
<p><a href="https://docs.python.org/3/library/random.html">random — Generate pseudo-random numbers</a>：</p>
<table>
<thead>
<tr>
<th>函数</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>seed(a=None, version=2)</code></td>
<td>初始化生成器</td>
</tr>
<tr>
<td><code>random()</code></td>
<td>[0.0, 1.0) float</td>
</tr>
<tr>
<td><code>uniform(a, b)</code></td>
<td>[a, b] 均匀 float</td>
</tr>
<tr>
<td><code>randint(a, b)</code></td>
<td>[a, b] 整数</td>
</tr>
<tr>
<td><code>randrange(start, stop[, step])</code></td>
<td>半开区间整数</td>
</tr>
<tr>
<td><code>choice(seq)</code></td>
<td>随机元素</td>
</tr>
<tr>
<td><code>choices(population, weights=None, k=1)</code></td>
<td>可重复抽样</td>
</tr>
<tr>
<td><code>sample(population, k)</code></td>
<td>无重复抽样</td>
</tr>
<tr>
<td><code>shuffle(x)</code></td>
<td>原地洗牌</td>
</tr>
<tr>
<td><code>getrandbits(k)</code></td>
<td>k 位随机整数</td>
</tr>
<tr>
<td><code>gauss(mu, sigma)</code>, <code>normalvariate</code>, <code>expovariate</code> 等</td>
<td>常见分布</td>
</tr>
</tbody>
</table>
<h2>附录：str 常用方法表</h2>
<p><a href="https://docs.python.org/3/library/stdtypes.html#string-methods">Text Sequence Type — str</a>：</p>
<table>
<thead>
<tr>
<th>方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>capitalize()</code></td>
<td>首字母大写</td>
</tr>
<tr>
<td><code>casefold()</code></td>
<td>强大小写折叠（比较用）</td>
</tr>
<tr>
<td><code>center(width[, fillchar])</code></td>
<td>居中填充</td>
</tr>
<tr>
<td><code>count(sub[, start[, end]])</code></td>
<td>子串出现次数</td>
</tr>
<tr>
<td><code>encode(encoding='utf-8', errors='strict')</code></td>
<td>→ bytes</td>
</tr>
<tr>
<td><code>endswith(suffix[, start[, end]])</code></td>
<td>后缀判断</td>
</tr>
<tr>
<td><code>expandtabs(tabsize=8)</code></td>
<td>tab → 空格</td>
</tr>
<tr>
<td><code>find(sub[, start[, end]])</code></td>
<td>查找，失败 -1</td>
</tr>
<tr>
<td><code>format(*args, **kwargs)</code></td>
<td>格式化</td>
</tr>
<tr>
<td><code>format_map(mapping)</code></td>
<td>映射格式化</td>
</tr>
<tr>
<td><code>index(sub[, start[, end]])</code></td>
<td>查找，失败 ValueError</td>
</tr>
<tr>
<td><code>isalnum()</code>, <code>isalpha()</code>, <code>isdigit()</code>, <code>isspace()</code> …</td>
<td>字符类判断</td>
</tr>
<tr>
<td><code>join(iterable)</code></td>
<td>连接</td>
</tr>
<tr>
<td><code>ljust</code>, <code>rjust</code>, <code>zfill</code></td>
<td>对齐与填零</td>
</tr>
<tr>
<td><code>lower()</code>, <code>upper()</code>, <code>swapcase()</code>, <code>title()</code></td>
<td>大小写</td>
</tr>
<tr>
<td><code>lstrip</code>, <code>rstrip</code>, <code>strip([chars])</code></td>
<td>去空白或字符集</td>
</tr>
<tr>
<td><code>replace(old, new[, count])</code></td>
<td>替换</td>
</tr>
<tr>
<td><code>removeprefix</code>, <code>removesuffix</code></td>
<td>3.9+ 去前后缀</td>
</tr>
<tr>
<td><code>split(sep=None, maxsplit=-1)</code></td>
<td>拆分</td>
</tr>
<tr>
<td><code>rsplit</code>, <code>splitlines</code></td>
<td>右拆 / 按行</td>
</tr>
<tr>
<td><code>startswith(prefix[, start[, end]])</code></td>
<td>前缀判断</td>
</tr>
<tr>
<td><code>partition(sep)</code>, <code>rpartition(sep)</code></td>
<td>三分</td>
</tr>
<tr>
<td><code>maketrans</code>, <code>translate</code></td>
<td>字符映射</td>
</tr>
</tbody>
</table>
<hr />
<h2>参考</h2>
<p>官方文档（<a href="https://docs.python.org/zh-cn/3/">中文版</a>）：</p>
<ol>
<li><a href="https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex">Numeric Types — int, float, complex</a></li>
<li><a href="https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str">Text Sequence Type — str</a></li>
<li><a href="https://docs.python.org/3/library/stdtypes.html#printf-style-string-formatting">printf-style String Formatting</a></li>
<li><a href="https://docs.python.org/3/tutorial/introduction.html#numbers">An Informal Introduction to Python — Numbers, Strings</a></li>
<li><a href="https://docs.python.org/3/tutorial/introduction.html#strings">Using Python as a Calculator — Strings</a></li>
<li><a href="https://docs.python.org/3/library/string.html#formatstrings">Format String Syntax</a></li>
<li><a href="https://docs.python.org/3/library/math.html">math — Mathematical functions</a></li>
<li><a href="https://docs.python.org/3/library/random.html">random — Generate pseudo-random numbers</a></li>
<li><a href="https://docs.python.org/3/reference/lexical_analysis.html#literals">Lexical analysis — Literals</a></li>
</ol>
<p>PEP：</p>
<ol>
<li><a href="https://peps.python.org/pep-0498/">PEP 498 — Literal String Interpolation (f-strings)</a></li>
<li><a href="https://peps.python.org/pep-0515/">PEP 515 — Underscores in Numeric Literals</a></li>
</ol>
<p>菜鸟教程：</p>
<ol>
<li><a href="https://www.runoob.com/python3/python3-number.html">菜鸟教程 · Number</a></li>
<li><a href="https://www.runoob.com/python3/python3-string.html">菜鸟教程 · 字符串</a></li>
<li><a href="https://www.runoob.com/python3/python3-string-format.html">菜鸟教程 · 格式化</a></li>
</ol>
<hr />
]]>
    </content>
  </entry>
  <entry>
    <id>https://blog.chuwu.top/posts/sql/mysql2/</id>
    <updated>2026-08-26T00:00:00.000Z</updated>
    <title>MySQL学习笔记 (2)</title>
    <author>
      <name>ChuwuYo</name>
    </author>
    <link href="https://blog.chuwu.top/posts/sql/mysql2/" rel="alternate"/>
    <published>2026-08-26T00:00:00.000Z</published>
    <summary>数据类型、日期时间、CAST、字符串函数</summary>
    <content type="html" xml:base="https://blog.chuwu.top/posts/sql/mysql2/">
      <![CDATA[<p>:::note
基于 MySQL 26.7。<a href="https://dev.mysql.com/doc/refman/26.7/en/data-types.html">数据类型</a>
:::</p>
<h2>数据类型</h2>
<p>创建表时要为每一列指定类型。常用类型：</p>
<table>
<thead>
<tr>
<th>类型</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>CHAR(n)</code></td>
<td>定长字符串</td>
</tr>
<tr>
<td><code>VARCHAR(n)</code></td>
<td>变长字符串，最长 <code>n</code></td>
</tr>
<tr>
<td><code>INTEGER</code> / <code>INT</code></td>
<td>整数</td>
</tr>
<tr>
<td><code>SMALLINT</code></td>
<td>较小整数</td>
</tr>
<tr>
<td><code>BIGINT</code></td>
<td>较大整数</td>
</tr>
<tr>
<td><code>NUMERIC(p,s)</code> / <code>DECIMAL(p,s)</code></td>
<td>精确小数</td>
</tr>
<tr>
<td><code>REAL</code> / <code>DOUBLE PRECISION</code></td>
<td>浮点数</td>
</tr>
<tr>
<td><code>DATE</code></td>
<td>日期</td>
</tr>
<tr>
<td><code>TIME</code></td>
<td>时间</td>
</tr>
<tr>
<td><code>TIMESTAMP</code></td>
<td>日期 + 时间</td>
</tr>
<tr>
<td><code>BOOLEAN</code></td>
<td>真 / 假</td>
</tr>
</tbody>
</table>
<p>金额用 <code>NUMERIC</code>，例如 <code>NUMERIC(12, 2)</code> 表示最多 12 位数字，其中 2 位小数。</p>
<p><code>CHAR</code> 不足长度时会补空格。空字符串 <code>''</code> 和 <code>NULL</code> 不是一回事。</p>
<h2>日期时间</h2>
<p>日期和时间可以写成：</p>
<pre><code>DATE '2026-09-01'
TIME '14:30:00'
TIMESTAMP '2026-09-01 14:30:00'
</code></pre>
<p>当前日期和时间：</p>
<pre><code>CURRENT_DATE
CURRENT_TIME
CURRENT_TIMESTAMP
</code></pre>
<p>取出年份、月份：</p>
<pre><code>EXTRACT(YEAR FROM hire_date)
EXTRACT(MONTH FROM hire_date)
</code></pre>
<h2>布尔</h2>
<p><code>BOOLEAN</code> 的值是 <code>TRUE</code>、<code>FALSE</code>，也可以是 NULL。</p>
<pre><code>SELECT * FROM employee WHERE is_active = TRUE;
</code></pre>
<h2>CAST</h2>
<p><code>CAST</code> 用来转换类型：</p>
<pre><code>CAST(emp_id AS CHAR(10))
CAST('2026-09-01' AS DATE)
CAST(3.14 AS NUMERIC(4, 2))
</code></pre>
<p><code>COALESCE(a, b, …)</code> 返回第一个不是 NULL 的值。</p>
<h2>字符串</h2>
<p>连接字符串用 <code>CONCAT</code>：</p>
<pre><code>SELECT CONCAT(last_name, ', ', first_name) AS full_name
FROM employee;
</code></pre>
<p>如果某一段是 NULL，整个 <code>CONCAT</code> 的结果也是 NULL。可以用 <code>COALESCE</code> 把 NULL 换成空字符串：</p>
<pre><code>CONCAT(last_name, ', ', COALESCE(middle_name, ''))
</code></pre>
<p>常用函数：</p>
<table>
<thead>
<tr>
<th>函数</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>CHAR_LENGTH(s)</code></td>
<td>字符个数</td>
</tr>
<tr>
<td><code>SUBSTRING(s FROM start FOR len)</code></td>
<td>截取，起点从 1 开始</td>
</tr>
<tr>
<td><code>UPPER(s)</code> / <code>LOWER(s)</code></td>
<td>大小写</td>
</tr>
</tbody>
</table>
<pre><code>SUBSTRING('abcdef' FROM 2 FOR 3)  -- 结果是 bcd
</code></pre>
<hr />
<h2>参考</h2>
<ol>
<li><a href="https://dev.mysql.com/doc/refman/26.7/en/data-types.html">MySQL 26.7 · Data Types</a></li>
<li><a href="https://dev.mysql.com/doc/refman/26.7/en/date-and-time-types.html">MySQL 26.7 · Date and Time Types</a></li>
<li><a href="https://dev.mysql.com/doc/refman/26.7/en/cast-functions.html#function_cast">MySQL 26.7 · CAST</a></li>
<li><a href="https://dev.mysql.com/doc/refman/26.7/en/string-functions.html">MySQL 26.7 · String Functions</a></li>
<li><a href="https://dev.mysql.com/doc/refman/26.7/en/comparison-operators.html#function_coalesce">MySQL 26.7 · COALESCE</a></li>
<li><a href="https://www.iso.org/standard/76583.html">ISO/IEC 9075（SQL:2023）</a></li>
</ol>
]]>
    </content>
  </entry>
  <entry>
    <id>https://blog.chuwu.top/posts/python/python2/</id>
    <updated>2026-08-25T00:00:00.000Z</updated>
    <title>Python 札记 (2)</title>
    <author>
      <name>ChuwuYo</name>
    </author>
    <link href="https://blog.chuwu.top/posts/python/python2/" rel="alternate"/>
    <published>2026-08-25T00:00:00.000Z</published>
    <summary>变量与赋值、可变与不可变、六种内置类型、bool 与 int、type 与 id、类型转换、算术/比较/赋值/位/逻辑/成员/身份运算符、优先级</summary>
    <content type="html" xml:base="https://blog.chuwu.top/posts/python/python2/">
      <![CDATA[<p>:::note
基于 Python 3。
:::</p>
<h2>变量与赋值</h2>
<pre><code>x = 10
x = "hello"   # 合法：同一个变量可以先后指向不同类型
</code></pre>
<p>Python 中的变量不需要声明类型。同一个变量可以先后指向不同类型的对象。<code>=</code> 将变量名指向一个对象。</p>
<pre><code>a, b = 1, 2
a, b = b, a          # 交换
x = y = []           # 两个变量指向同一个列表，修改其中一个，另一个也会变化
x, y = [], []        # 两个独立的空列表
</code></pre>
<p><code>+=</code>、<code>-=</code> 等是增强赋值运算符。对可变对象，增强赋值可能原地修改对象。</p>
<p><code>0 &lt; x &lt; 10</code> 可以这么写，等价于 <code>0 &lt; x and x &lt; 10</code>。</p>
<h2>可变与不可变</h2>
<p><a href="https://docs.python.org/3/library/stdtypes.html">Built-in Types</a> 里，对象分<strong>可变</strong>与<strong>不可变</strong>：</p>
<table>
<thead>
<tr>
<th>不可变（常见）</th>
<th>可变（常见）</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>int</code> <code>float</code> <code>complex</code> <code>bool</code></td>
<td><code>list</code></td>
</tr>
<tr>
<td><code>str</code> <code>bytes</code></td>
<td><code>dict</code> <code>set</code></td>
</tr>
<tr>
<td><code>tuple</code>（容器本身不可变；元素若是 list 仍可改内容）</td>
<td><code>bytearray</code></td>
</tr>
</tbody>
</table>
<p>不可变对象的内容不能原地修改；重新赋值会创建新对象。可变对象可以原地修改，通过其他变量也能看到变化。</p>
<pre><code>s = "hello"
# s[0] = "H"        # TypeError：str 不可变
s = "Hello"         # 新字符串，变量 s 指向新对象

lst = [1, 2, 3]
lst.append(4)       # 原地改，id(lst) 不变
other = lst
other.append(5)
print(lst)          # [1, 2, 3, 4, 5] —— 同一对象
</code></pre>
<p><code>tuple</code> 本身不可变，但其中的可变元素（如 list）仍可以修改内容：</p>
<pre><code>t = ([1, 2], 3)
t[0].append(3)      # 合法
# t[0] = [9]        # TypeError：不能替换 tuple 的元素
</code></pre>
<h2>六种内置类型（总览）</h2>
<p>Python 常用内置类型如下（<a href="https://docs.python.org/3/library/stdtypes.html">Built-in Types</a>）：</p>
<table>
<thead>
<tr>
<th>类型</th>
<th>字面量示例</th>
<th>要点</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>数字</strong> <code>int</code> / <code>float</code> / <code>complex</code></td>
<td><code>42</code> <code>3.14</code> <code>1+2j</code></td>
<td>任意精度整数、浮点、复数</td>
</tr>
<tr>
<td><strong>字符串</strong> <code>str</code></td>
<td><code>'hi'</code> <code>"你好"</code></td>
<td>不可变文本</td>
</tr>
<tr>
<td><strong>列表</strong> <code>list</code></td>
<td><code>[1, 2, 3]</code></td>
<td>有序、可变</td>
</tr>
<tr>
<td><strong>元组</strong> <code>tuple</code></td>
<td><code>(1, 2)</code> <code>(1,)</code></td>
<td>有序、不可变；单元素要逗号</td>
</tr>
<tr>
<td><strong>字典</strong> <code>dict</code></td>
<td><code>{'a': 1}</code></td>
<td>键值对；空的是 <code>{}</code></td>
</tr>
<tr>
<td><strong>集合</strong> <code>set</code></td>
<td><code>{1, 2, 3}</code></td>
<td>元素不重复；空集合写 <code>set()</code></td>
</tr>
</tbody>
</table>
<p>此外还有 <code>bool</code>、<code>NoneType</code>（唯一值 <code>None</code>）、<code>bytes</code> / <code>bytearray</code> 等。</p>
<p>用内置函数探测类型：</p>
<pre><code>type(42)            # &lt;class 'int'&gt;
type([]) is list    # True
isinstance(3.0, int)  # False
isinstance(True, int) # True
</code></pre>
<h2>bool 是 int 的子类</h2>
<p><code>True</code> / <code>False</code> 是 <code>bool</code> 类型的两个实例；<code>bool</code> <strong>继承自</strong> <code>int</code>（<a href="https://docs.python.org/3/library/stdtypes.html#boolean-values">Boolean Type</a>）。</p>
<pre><code>True == 1           # True
False == 0          # True
True + True         # 2
int(True)           # 1
isinstance(False, int)  # True
type(True) is int   # False —— 类型仍是 bool
</code></pre>
<p>因此 <code>bool</code> 可以参与算术。<code>True is 1</code> 为 <code>False</code>。空列表、空字符串在 <code>if</code> 中视为假，见 <a href="https://docs.python.org/3/library/stdtypes.html#truth-value-testing">Truth Value Testing</a>。</p>
<h2>type 与 id</h2>
<ul>
<li><strong><code>type(obj)</code></strong>：返回对象的类型（类）。</li>
<li><strong><code>id(obj)</code></strong>：在 CPython 中通常是对象内存地址的整数，<strong>同一时刻</strong>同一对象 id 相同；对象被回收后 id 可能复用。</li>
</ul>
<pre><code>a = [1, 2]
b = a
c = [1, 2]
print(id(a) == id(b))   # True —— 同一对象
print(a is b)           # True
print(a is c)           # False —— 值相等，对象不同
print(a == c)           # True
</code></pre>
<p><code>is</code> 比较对象身份（是否为同一个对象），<code>==</code> 比较值是否相等。判断是否为 <code>None</code> 应使用 <code>x is None</code>。小整数和字符串驻留可能导致 <code>is</code> 碰巧为真，这是实现细节。</p>
<p>:::caution
相等的整数或元组上 <code>is</code> 有时为真，那是 <strong>CPython 小整数缓存等实现细节</strong>。<a href="https://docs.python.org/3/reference/expressions.html#is-not">Comparisons</a> 的语义是：<code>is</code> 只判断「是不是同一个对象」，相等用 <code>==</code>。不要把 <code>a is b</code> 当成 <code>a == b</code> 的快捷写法。
:::</p>
<h2>类型转换</h2>
<h3>显式转换</h3>
<p>内置构造函数即转换函数（<a href="https://docs.python.org/3/library/functions.html">Built-in Functions</a>）：</p>
<pre><code>int("42")           # 42
int("1010", 2)      # 二进制字符串 → 10
float("3.14")
str(123)
list("abc")         # ['a', 'b', 'c']
tuple([1, 2])
bool(0)             # False；非零数为 True
bool("")            # False；非空串为 True
</code></pre>
<p>转换失败抛 <code>ValueError</code>；具体规则以各类型文档为准。另外还有 <code>chr</code> / <code>ord</code> / <code>repr</code> 等。<strong>不要</strong>用 <code>eval</code> 去「转换」不可信字符串。</p>
<h3>隐式转换</h3>
<p>混合数值运算时，Python 按 <a href="https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex">Numeric Types</a> <strong>拓宽</strong>类型。记忆口诀 <code>bool → int → float → complex</code> 只描述运算结果类型；<code>bool</code> 本身仍是 <code>int</code> 的子类（上一节）。<strong>不会</strong>把数字和字符串自动相加：</p>
<pre><code>3 + 4.5             # 7.5，int 与 float 运算得 float
# "3" + 4           # TypeError
"3" + str(4)        # "34"，需显式转
</code></pre>
<p>布尔上下文（<code>if</code> / <code>while</code> / <code>and</code> / <code>or</code>）里会调用对象的<strong>真值测试</strong>，不是把 <code>int</code> 转成 <code>bool</code> 再存回变量。</p>
<h2>del 语句</h2>
<p><a href="https://docs.python.org/3/reference/simple_stmts.html#the-del-statement"><code>del</code></a> 是语句，不是函数。常见用法：</p>
<pre><code>x = 1
del x              # 删除变量，再访问 x 会 NameError
lst = [1, 2, 3]
del lst[0]         # 删下标 0，后面元素前移 → [2, 3]
del lst[1:]        # 删切片，lst 变成 [2]
d = {"a": 1}
del d["a"]         # 删键 "a"；键不存在则 KeyError
</code></pre>
<h2>bytes</h2>
<p><a href="https://docs.python.org/3/library/stdtypes.html#binary-sequence-types">Binary Sequence Types</a>：<code>bytes</code> 不可变，元素是 0–255 的整数；<code>bytearray</code> 可变。字面量 <code>b"hello"</code>（内容须为 ASCII 或转义）。文本 ↔ 二进制：<code>str.encode(encoding)</code> 得到 <code>bytes</code>，<code>bytes.decode(encoding)</code> 还原为 <code>str</code>，编码对不上会 <code>UnicodeError</code>。</p>
<pre><code>b = b"hi"
b[0]                # 104，不是 'h'
bytearray(b"hi")[0] = 72
"你好".encode("utf-8")   # b'\xe4\xbd\xa0\xe5\xa5\xbd'
b"hi".decode("ascii")    # 'hi'
</code></pre>
<h2>算术运算符</h2>
<table>
<thead>
<tr>
<th>运算符</th>
<th>含义</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>+</code> <code>-</code> <code>*</code> <code>/</code></td>
<td>加 减 乘 真除</td>
<td><code>/</code> 总是 float（3/2 → 1.5）</td>
</tr>
<tr>
<td><code>//</code></td>
<td>地板除</td>
<td>向负无穷取整；<code>-7//3</code> → <code>-3</code></td>
</tr>
<tr>
<td><code>%</code></td>
<td>余数</td>
<td>与地板除配套：<code>a == (a//b)*b + (a%b)</code></td>
</tr>
<tr>
<td><code>**</code></td>
<td>幂</td>
<td>右结合：<code>2**3**2</code> → <code>2**(3**2)</code></td>
</tr>
<tr>
<td><code>+x</code> <code>-x</code></td>
<td>一元正负</td>
<td></td>
</tr>
</tbody>
</table>
<pre><code>7 // 3              # 2
-7 // 3             # -3
7 % 3               # 1
2 ** 10             # 1024
</code></pre>
<p>对非数字类型，<code>+</code> 可能被重载（如 str 拼接、list 合并）；其他算术若未定义则 <code>TypeError</code>。</p>
<h2>比较运算符</h2>
<p>链式比较：<code>a &lt; b &lt;= c</code> 等价于 <code>a &lt; b and b &lt;= c</code>，且 <strong><code>b</code> 只求值一次</strong>。</p>
<table>
<thead>
<tr>
<th>运算符</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>==</code> <code>!=</code></td>
<td>相等 / 不等</td>
</tr>
<tr>
<td><code>&lt;</code> <code>&gt;</code> <code>&lt;=</code> <code>&gt;=</code></td>
<td>大小（同类型或可比协议）</td>
</tr>
</tbody>
</table>
<p>不同类型比较是否合法、如何比，以 <a href="https://docs.python.org/3/reference/expressions.html#comparisons">Comparisons</a> 与具体类型为准；不要依赖 Python 2 的旧规则。</p>
<h2>赋值与增强赋值</h2>
<table>
<thead>
<tr>
<th>形式</th>
<th>等价于（概念上）</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>x = expr</code></td>
<td>名字绑定</td>
</tr>
<tr>
<td><code>x += y</code></td>
<td><code>x = x.__iadd__(y)</code> 或回退到 <code>x = x + y</code></td>
</tr>
<tr>
<td><code>x //= y</code> <code>x **= y</code> 等</td>
<td>同理</td>
</tr>
</tbody>
</table>
<p>对<strong>不可变</strong>对象，<code>+=</code> 通常创建新对象再绑定；对 <strong>list</strong> 等，<code>+=</code> 往往<strong>原地</strong>扩展（<code>extend</code>）。若需要新列表，用 <code>x = x + y</code> 或切片复制，别假设所有类型行为一致。</p>
<p>海象运算符 <code>:=</code>（<a href="https://peps.python.org/pep-0572/">PEP 572</a>）在表达式内赋值。</p>
<h2>逻辑运算符</h2>
<table>
<thead>
<tr>
<th>运算符</th>
<th>含义</th>
<th>短路</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>and</code></td>
<td>全真则取最后一值</td>
<td>左假则不再求右</td>
</tr>
<tr>
<td><code>or</code></td>
<td>有真则取第一个真值</td>
<td>左真则不再求右</td>
</tr>
<tr>
<td><code>not</code></td>
<td>逻辑非</td>
<td>返回 <code>True</code>/<code>False</code></td>
</tr>
</tbody>
</table>
<pre><code>0 and 99            # 0（不是 False，是操作数本身）
"" or "default"     # "default"
not []              # True
</code></pre>
<p>逻辑运算返回的是<strong>操作数对象</strong>，不一定是 <code>bool</code>；条件语句里再按真值规则解释。</p>
<h2>成员与身份运算符</h2>
<table>
<thead>
<tr>
<th>运算符</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>in</code> / <code>not in</code></td>
<td>成员测试（序列、映射、集合）</td>
</tr>
<tr>
<td><code>is</code> / <code>is not</code></td>
<td>是否同一对象（比较 <code>id</code>）</td>
</tr>
</tbody>
</table>
<pre><code>"a" in "abc"        # True
1 in {1, 2}         # True
x is None
x is not y
</code></pre>
<p><code>==</code> 与 <code>is</code> 勿混：小整数缓存等实现细节<strong>不应</strong>依赖 <code>a is b</code> 代替 <code>a == b</code>。</p>
<h2>位运算符</h2>
<p>作用于整数二进制位（<a href="https://docs.python.org/3/library/stdtypes.html#bitwise-operations-on-integer-types">Bitwise operations</a>）：</p>
<table>
<thead>
<tr>
<th>运算符</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>&amp;</code> <code>|</code> <code>^</code></td>
<td>与、或、异或</td>
</tr>
<tr>
<td><code>~</code></td>
<td>按位取反</td>
</tr>
<tr>
<td><code>&lt;&lt;</code> <code>&gt;&gt;</code></td>
<td>左移、右移</td>
</tr>
</tbody>
</table>
<pre><code>0b1010 &amp; 0b1100    # 0b1000 → 8
1 &lt;&lt; 4              # 16
</code></pre>
<p><code>bool</code> 参与位运算会先当 <code>0</code>/<code>1</code> 用。读标志位、权限掩码时会碰到。</p>
<h2>运算符优先级</h2>
<p>表达式求值顺序见 <a href="https://docs.python.org/3/reference/expressions.html#operator-precedence">Operator precedence</a>。记不住时加括号。官方从高到低（同级除 <code>**</code> 外从左到右；<code>**</code> 右结合，且<strong>高于</strong>左侧的一元 <code>+</code>/<code>-</code>/<code>~</code>，所以 <code>-2**2</code> 是 <code>-4</code>）：</p>
<table>
<thead>
<tr>
<th>级别（高→低）</th>
<th>运算符</th>
</tr>
</thead>
<tbody>
<tr>
<td>原子、订阅、调用</td>
<td><code>(…)</code> <code>x[i]</code> <code>x()</code> <code>x.attr</code></td>
</tr>
<tr>
<td>幂</td>
<td><code>**</code></td>
</tr>
<tr>
<td>一元算术 / 位</td>
<td><code>+x</code> <code>-x</code> <code>~x</code></td>
</tr>
<tr>
<td>乘除模</td>
<td><code>*</code> <code>@</code> <code>/</code> <code>//</code> <code>%</code></td>
</tr>
<tr>
<td>加减</td>
<td><code>+</code> <code>-</code></td>
</tr>
<tr>
<td>移位</td>
<td><code>&lt;&lt;</code> <code>&gt;&gt;</code></td>
</tr>
<tr>
<td>位与</td>
<td><code>&amp;</code></td>
</tr>
<tr>
<td>位异或</td>
<td><code>^</code></td>
</tr>
<tr>
<td>位或</td>
<td><code>|</code></td>
</tr>
<tr>
<td>比较、成员、身份</td>
<td><code>==</code> <code>in</code> <code>is</code> 等</td>
</tr>
<tr>
<td>逻辑非</td>
<td><code>not x</code></td>
</tr>
<tr>
<td>逻辑与</td>
<td><code>and</code></td>
</tr>
<tr>
<td>逻辑或</td>
<td><code>or</code></td>
</tr>
<tr>
<td>条件表达式</td>
<td><code>x if c else y</code></td>
</tr>
<tr>
<td>lambda</td>
<td><code>lambda</code></td>
</tr>
<tr>
<td>海象</td>
<td><code>:=</code></td>
</tr>
</tbody>
</table>
<pre><code>2 + 3 * 4           # 14，不是 20
(not False) and True
2 ** 3 ** 2         # 512，即 2 ** (3 ** 2)
</code></pre>
<hr />
<h2>附录：内置类型速查</h2>
<table>
<thead>
<tr>
<th>函数</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>type(x)</code></td>
<td>类型</td>
</tr>
<tr>
<td><code>isinstance(x, cls)</code></td>
<td>是否该类型（含子类）</td>
</tr>
<tr>
<td><code>id(x)</code></td>
<td>对象标识</td>
</tr>
<tr>
<td><code>len(x)</code></td>
<td>长度（有 <code>__len__</code> 的对象）</td>
</tr>
<tr>
<td><code>hash(x)</code></td>
<td>哈希值（可哈希对象）</td>
</tr>
</tbody>
</table>
<p>参数形式以 <a href="https://docs.python.org/3/library/functions.html">Built-in Functions</a> 为准。另见 <code>round()</code> <code>abs()</code> <code>divmod()</code>。<strong><code>eval</code> 会执行代码，不要对不可信输入使用。</strong></p>
<p>常见构造函数：| 函数 | 作用 |
| --- | --- |
| <code>int</code> <code>float</code> <code>complex</code> | 数值 |
| <code>str</code> <code>repr</code> <code>ascii</code> | 文本 / 可打印表示 |
| <code>chr</code> / <code>ord</code> | 码点 ↔ 单字符 |
| <code>bin</code> <code>oct</code> <code>hex</code> | 整数 → 带前缀的进制字符串 |
| <code>list</code> <code>tuple</code> <code>set</code> <code>frozenset</code> | 从可迭代对象构造 |
| <code>dict</code> | 从键值对可迭代对象或关键字参数构造 |
| <code>bytes</code> <code>bytearray</code> <code>memoryview</code> | 二进制视图 |
| <code>bool</code> | 真值测试 |</p>
<h2>附录：运算符一览</h2>
<table>
<thead>
<tr>
<th>类别</th>
<th>运算符</th>
</tr>
</thead>
<tbody>
<tr>
<td>算术</td>
<td><code>+</code> <code>-</code> <code>*</code> <code>/</code> <code>//</code> <code>%</code> <code>**</code> <code>@</code>（矩阵乘，NumPy 等）</td>
</tr>
<tr>
<td>比较</td>
<td><code>==</code> <code>!=</code> <code>&gt;</code> <code>&lt;</code> <code>&gt;=</code> <code>&lt;=</code></td>
</tr>
<tr>
<td>赋值</td>
<td><code>=</code> <code>+=</code> <code>-=</code> … <code>:=</code></td>
</tr>
<tr>
<td>逻辑</td>
<td><code>and</code> <code>or</code> <code>not</code></td>
</tr>
<tr>
<td>位</td>
<td><code>&amp;</code> <code>|</code> <code>^</code> <code>~</code> <code>&lt;&lt;</code> <code>&gt;&gt;</code></td>
</tr>
<tr>
<td>成员</td>
<td><code>in</code> <code>not in</code></td>
</tr>
<tr>
<td>身份</td>
<td><code>is</code> <code>is not</code></td>
</tr>
</tbody>
</table>
<hr />
<h2>参考</h2>
<p>官方文档（<a href="https://docs.python.org/zh-cn/3/">中文版</a>）：</p>
<ol>
<li><a href="https://docs.python.org/3/tutorial/introduction.html">An Informal Introduction to Python</a> — 数字、字符串入门示例</li>
<li><a href="https://docs.python.org/3/library/stdtypes.html">Built-in Types</a> — 类型分类、可变/不可变、真值测试</li>
<li><a href="https://docs.python.org/3/library/functions.html">Built-in Functions</a> — <code>int</code> / <code>float</code> / <code>str</code> / <code>bool</code> 等</li>
<li><a href="https://docs.python.org/3/reference/expressions.html#operator-precedence">Operator precedence</a></li>
<li><a href="https://docs.python.org/3/reference/simple_stmts.html#assignment-statements">Assignment statements</a></li>
<li><a href="https://docs.python.org/3/reference/expressions.html#comparisons">Comparisons</a></li>
<li><a href="https://docs.python.org/3/library/stdtypes.html#truth-value-testing">Truth Value Testing</a></li>
<li><a href="https://docs.python.org/3/reference/simple_stmts.html#the-del-statement">The del statement</a></li>
<li><a href="https://docs.python.org/3/library/stdtypes.html#binary-sequence-types">Binary Sequence Types</a></li>
</ol>
<p>PEP：</p>
<ol>
<li><a href="https://peps.python.org/pep-0572/">PEP 572 — Assignment Expressions</a></li>
</ol>
<p>菜鸟教程：</p>
<ol>
<li><a href="https://www.runoob.com/python3/python3-data-type.html">菜鸟教程 · 数据类型</a></li>
<li><a href="https://www.runoob.com/python3/python3-type-conversion.html">菜鸟教程 · 数据类型转换</a></li>
<li><a href="https://www.runoob.com/python3/python3-basic-operators.html">菜鸟教程 · 运算符</a></li>
</ol>
<hr />
]]>
    </content>
  </entry>
  <entry>
    <id>https://blog.chuwu.top/posts/sql/mysql1/</id>
    <updated>2026-08-25T00:00:00.000Z</updated>
    <title>MySQL学习笔记 (1)</title>
    <author>
      <name>ChuwuYo</name>
    </author>
    <link href="https://blog.chuwu.top/posts/sql/mysql1/" rel="alternate"/>
    <published>2026-08-25T00:00:00.000Z</published>
    <summary>SQL 简介、登录、语句、术语、E-R 图、注释、NULL</summary>
    <content type="html" xml:base="https://blog.chuwu.top/posts/sql/mysql1/">
      <![CDATA[<p>:::note
基于 MySQL 26.7。<a href="https://dev.mysql.com/doc/refman/26.7/en/">参考手册</a>
:::</p>
<h2>SQL 是什么</h2>
<p>SQL 指结构化查询语言，全称 Structured Query Language。</p>
<ul>
<li>SQL 用来访问和处理数据库。</li>
<li>SQL 可以查询、插入、更新和删除数据。</li>
<li>SQL 可以创建数据库和表。</li>
<li>SQL 由 ANSI / ISO 标准化。现行标准是 <a href="https://www.iso.org/standard/76583.html">ISO/IEC 9075</a>（SQL:2023）。</li>
</ul>
<p>本机是 <strong>MySQL Community Server 26.7.0</strong>。版本号是日历制：<code>年.月.补丁</code>。<a href="https://dev.mysql.com/doc/refman/26.7/en/mysql-nutshell.html">版本说明</a></p>
<p>不同数据库对 SQL 的支持不完全一样，但 <code>SELECT</code>、<code>INSERT</code>、<code>UPDATE</code>、<code>DELETE</code>、<code>CREATE TABLE</code> 这些命令用法相近。</p>
<h2>SQL 能做什么</h2>
<ul>
<li>从数据库取回数据</li>
<li>在数据库中插入新的记录</li>
<li>更新数据库中的数据</li>
<li>从数据库删除记录</li>
<li>创建新数据库</li>
<li>在数据库中创建新表</li>
<li>在数据库中创建视图</li>
<li>设置表的权限</li>
</ul>
<h2>RDBMS</h2>
<p>RDBMS 指关系型数据库管理系统（Relational Database Management System）。</p>
<p>数据存放在表里。表由列和行组成：一列是一个字段，一行是一条记录。</p>
<p>MySQL、PostgreSQL、SQLite 都属于 RDBMS。</p>
<h2>登录</h2>
<pre><code>mysql -u root -p
</code></pre>
<p>查看版本：</p>
<pre><code>mysql --version
</code></pre>
<p>也可以写成 <code>mysql -V</code>（大写 V）。远程连接加上 <code>-h 主机</code>。</p>
<h2>SQL 语句</h2>
<p>大部分工作都由 SQL 语句完成。语句通常以分号结束。</p>
<p>下面的语句从 <code>employee</code> 表选取 <code>last_name</code> 和 <code>hire_date</code> 列：</p>
<pre><code>SELECT last_name, hire_date
FROM   employee
WHERE  dept_no = 'D01';
</code></pre>
<p>SQL 对大小写不敏感：<code>SELECT</code> 与 <code>select</code> 相同。</p>
<p>字符串用单引号。字符串里的单引号要写两个：</p>
<pre><code>SELECT 'O''Brien';
</code></pre>
<p>表名、列名建议用小写字母、数字和下划线，不要用空格。</p>
<h2>一些最重要的 SQL 命令</h2>
<ul>
<li><code>SELECT</code> - 从数据库中提取数据</li>
<li><code>UPDATE</code> - 更新数据库中的数据</li>
<li><code>DELETE</code> - 从数据库中删除数据</li>
<li><code>INSERT INTO</code> - 向数据库中插入新数据</li>
<li><code>CREATE SCHEMA</code> - 创建新数据库</li>
<li><code>CREATE TABLE</code> - 创建新表</li>
<li><code>ALTER TABLE</code> - 修改表</li>
<li><code>DROP TABLE</code> - 删除表</li>
<li><code>CREATE INDEX</code> - 创建索引</li>
<li><code>DROP INDEX</code> - 删除索引</li>
</ul>
<p>按功能也可以分成：</p>
<ol>
<li><strong>DDL</strong>（数据定义）：<code>CREATE</code> / <code>ALTER</code> / <code>DROP</code></li>
<li><strong>DML</strong>（数据操纵）：<code>SELECT</code> / <code>INSERT</code> / <code>UPDATE</code> / <code>DELETE</code></li>
<li><strong>DCL</strong>（数据控制）：<code>GRANT</code> / <code>REVOKE</code></li>
<li><strong>事务</strong>：<code>START TRANSACTION</code> / <code>COMMIT</code> / <code>ROLLBACK</code></li>
</ol>
<h2>术语</h2>
<ul>
<li><strong>数据</strong>：库里存储的基本对象。</li>
<li><strong>数据库（DB）</strong>：有组织、可共享的数据集合。</li>
<li><strong>数据库管理系统（DBMS）</strong>：管理数据库的软件，例如 MySQL。</li>
<li><strong>数据库系统（DBS）</strong>：数据库、DBMS、应用程序和数据库管理员合在一起。</li>
</ul>
<table>
<thead>
<tr>
<th>关系模型</th>
<th>库里常说</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr>
<td>关系</td>
<td>表</td>
<td>列加上行</td>
</tr>
<tr>
<td>属性</td>
<td>列 / 字段</td>
<td>一列的名字和类型</td>
</tr>
<tr>
<td>元组</td>
<td>行 / 记录</td>
<td>一行上的一组值</td>
</tr>
<tr>
<td>分量</td>
<td>字段值</td>
<td>某一格里的值</td>
</tr>
<tr>
<td>域</td>
<td>取值范围</td>
<td>这一列允许哪些值</td>
</tr>
</tbody>
</table>
<ul>
<li><strong>主键</strong>：用来标识一行，不能重复，也不能是 NULL。</li>
<li><strong>外键</strong>：本表的值必须在另一表的主键（或唯一键）里出现，用来关联两表。</li>
<li><strong>复合键</strong>：多列合在一起当一个键。</li>
<li><strong>索引</strong>：加快查找的辅助结构。</li>
<li><strong>冗余</strong>：同一事实存多份，更新时容易不一致。</li>
<li><strong>实体完整性</strong>：主键非空、不重复。</li>
<li><strong>参照完整性</strong>：外键不能指向不存在的键。</li>
</ul>
<h2>E-R 图</h2>
<p>E-R 图用来画实体和它们之间的联系，再对应到表。</p>
<p><img src="./images/E-R%E5%9B%BE1.webp" alt="E-R 图" /></p>
<ul>
<li><strong>实体集</strong>：矩形</li>
<li><strong>属性</strong>：椭圆</li>
<li><strong>联系</strong>：菱形，边上标注 <code>1:1</code>、<code>1:n</code>、<code>m:n</code></li>
</ul>
<p>例如：学生、课程、教师、院系。多对多关系用一张单独的表来存放。</p>
<p>逻辑模型还有层次、网状、关系三种。关系数据库用二维表表示实体和联系：</p>
<p><img src="./images/%E9%80%BB%E8%BE%91%E6%A8%A1%E5%9E%8B.webp" alt="逻辑模型" /></p>
<p>表头是列名，一行是一条记录，格子里的值必须符合该列的类型。</p>
<p><img src="./images/%E8%A1%A8%E6%A0%BC%E7%A4%BA%E4%BE%8B.webp" alt="表格示例" /></p>
<h2>注释</h2>
<pre><code>-- 单行注释（-- 后面要有空格）
SELECT 1;  -- 行尾注释

/* 多行注释 */
</code></pre>
<h2>NULL</h2>
<p>NULL 表示缺少值，不是 <code>0</code>，也不是空字符串 <code>''</code>。</p>
<p>判断是否为 NULL，要用 <code>IS NULL</code> / <code>IS NOT NULL</code>：</p>
<pre><code>SELECT * FROM employee WHERE salary IS NULL;
SELECT * FROM employee WHERE salary IS NOT NULL;
</code></pre>
<p>注意：<code>WHERE salary = NULL</code> 不会找出空值。</p>
<hr />
<h2>参考</h2>
<ol>
<li><a href="https://dev.mysql.com/doc/refman/26.7/en/compatibility.html">MySQL 26.7 · SQL Standards</a></li>
<li><a href="https://dev.mysql.com/doc/refman/26.7/en/sql-statements.html">MySQL 26.7 · SQL Statement Syntax</a></li>
<li><a href="https://dev.mysql.com/doc/refman/26.7/en/language-structure.html">MySQL 26.7 · Language Structure</a></li>
<li><a href="https://dev.mysql.com/doc/refman/26.7/en/working-with-null.html">MySQL 26.7 · Working with NULL</a></li>
<li><a href="https://dev.mysql.com/doc/refman/26.7/en/mysql-nutshell.html">MySQL 26.7 · What Is New</a></li>
<li><a href="https://www.iso.org/standard/76583.html">ISO/IEC 9075（SQL:2023）</a></li>
</ol>
]]>
    </content>
  </entry>
  <entry>
    <id>https://blog.chuwu.top/posts/python/python1/</id>
    <updated>2026-08-24T00:00:00.000Z</updated>
    <title>Python 札记 (1)</title>
    <author>
      <name>ChuwuYo</name>
    </author>
    <link href="https://blog.chuwu.top/posts/python/python1/" rel="alternate"/>
    <published>2026-08-24T00:00:00.000Z</published>
    <summary>解释器、print、缩进、注释、编码与标识符</summary>
    <content type="html" xml:base="https://blog.chuwu.top/posts/python/python1/">
      <![CDATA[<p>:::note
基于 Python 3。
:::</p>
<h2>解释器</h2>
<p>在终端输入 <code>python3</code>（Windows 常用 <code>py</code>）进入交互式编程，提示符为 <code>&gt;&gt;&gt;</code>：</p>
<pre><code>&gt;&gt;&gt; print("Hello, Python!")
Hello, Python!
</code></pre>
<p>退出使用 <code>quit()</code>。Unix 上也可用 <code>Ctrl+D</code>，Windows 上可用 <code>Ctrl+Z</code>。</p>
<p>将代码保存为 <code>hello.py</code> 后执行：</p>
<pre><code>python3 hello.py
</code></pre>
<p>还可以使用 <code>python -c</code>、<code>python -m</code>。以 <code>python 文件.py</code> 运行时，文件开头的 <code>#!/usr/bin/env python3</code> 会被忽略。[^shebang]</p>
<p>详见 <a href="https://docs.python.org/3/tutorial/interpreter.html">Using the Python Interpreter</a>。</p>
<h2>print</h2>
<pre><code>print("Hello, Python!")
print(1, 2, 3, sep="@")  # 1@2@3
print("a", end=" ")
print("b")               # a b
</code></pre>
<p><code>print</code> 默认以空格分隔参数，并以换行结尾。<code>sep</code> 指定分隔符，<code>end</code> 指定结尾。<a href="https://docs.python.org/3/library/functions.html#print">print()</a></p>
<p><code>input("提示")</code> 读取一行输入，返回字符串。</p>
<h2>缩进</h2>
<pre><code>if True:
    print("True")
else:
    print("False")
</code></pre>
<p>Python 使用缩进表示代码块，不使用 <code>{}</code>。<a href="https://peps.python.org/pep-0008/#indentation">PEP 8</a> 建议使用 <strong>4 个空格</strong>，不要与 Tab 混用，否则会触发 <code>TabError</code>。缩进不一致会触发 <code>IndentationError</code>。</p>
<p><code>if</code>、<code>for</code>、<code>def</code> 等语句以冒号结尾，其后缩进的语句构成一个代码块。空代码块使用 <code>pass</code>。</p>
<h2>多行</h2>
<p>圆括号、方括号、花括号内可以直接换行：</p>
<pre><code>total = [
    "item_one",
    "item_two",
    "item_three",
]
</code></pre>
<p>行末也可以使用 <code>\</code> 续行。</p>
<h2>注释</h2>
<pre><code># 整行注释
print("Hello, Python!")  # 行尾注释
</code></pre>
<p><code>#</code> 之后到行末为注释。字符串中的 <code>#</code> 不是注释。[^comment]</p>
<p>三引号 <code>'''</code> / <code>"""</code> 用来表示字符串。单独出现的三引号字符串常被用作多行注释，但不能嵌套。</p>
<pre><code>def add(a, b):
    """返回两数之和。"""
    return a + b

print(add.__doc__)
</code></pre>
<p>函数、模块或类的体中第一条语句如果是字符串，该字符串会保存到 <code>__doc__</code>，称为文档字符串。<a href="https://peps.python.org/pep-0257/">PEP 257</a> 建议使用 <code>"""</code>。</p>
<h2>编码</h2>
<p>默认情况下，Python 3 源文件以 <strong>UTF-8</strong> 编码，可以直接使用中文：</p>
<pre><code>print("你好")
</code></pre>
<p>通常无需编写编码声明。若要指定其他编码，声明必须单独占一行，位于文件第一行；若第一行是 <code>#!</code>，则放在第二行（<a href="https://peps.python.org/pep-0263/">PEP 263</a>）：</p>
<pre><code># -*- coding: utf-8 -*-
</code></pre>
<h2>标识符</h2>
<pre><code>age = 25
user_name = "Alice"
</code></pre>
<p>第一个字符必须是字母或下划线，其余可以是字母、数字或下划线。标识符区分大小写，不能使用保留关键字（如 <code>class</code>、<code>if</code>）。Python 3 允许使用中文、<code>π</code> 等 Unicode 字符。可用 <code>keyword.iskeyword()</code> 判断是否为关键字。[^kw]</p>
<p><a href="https://peps.python.org/pep-0008/">PEP 8</a>：变量和函数使用 <code>snake_case</code>，类名使用 <code>PascalCase</code>。单下划线开头（如 <code>_total</code>）通常表示内部使用；全大写（如 <code>MAX_SIZE</code>）通常表示常量。双下划线开头会触发名称改写。</p>
<hr />
<h2>附录：保留关键字</h2>
<p>当前版本的关键字列表：</p>
<pre><code>import keyword
print(keyword.kwlist)
</code></pre>
<table>
<thead>
<tr>
<th>类别</th>
<th>关键字</th>
</tr>
</thead>
<tbody>
<tr>
<td>值</td>
<td><code>True</code> <code>False</code> <code>None</code></td>
</tr>
<tr>
<td>逻辑</td>
<td><code>and</code> <code>or</code> <code>not</code> <code>is</code> <code>in</code></td>
</tr>
<tr>
<td>条件</td>
<td><code>if</code> <code>elif</code> <code>else</code></td>
</tr>
<tr>
<td>循环</td>
<td><code>for</code> <code>while</code> <code>break</code> <code>continue</code></td>
</tr>
<tr>
<td>异常</td>
<td><code>try</code> <code>except</code> <code>finally</code> <code>raise</code> <code>assert</code></td>
</tr>
<tr>
<td>函数</td>
<td><code>def</code> <code>return</code> <code>lambda</code> <code>yield</code> <code>pass</code></td>
</tr>
<tr>
<td>类</td>
<td><code>class</code> <code>del</code></td>
</tr>
<tr>
<td>模块</td>
<td><code>import</code> <code>from</code> <code>as</code></td>
</tr>
<tr>
<td>作用域</td>
<td><code>global</code> <code>nonlocal</code></td>
</tr>
<tr>
<td>异步</td>
<td><code>async</code> <code>await</code></td>
</tr>
<tr>
<td>资源</td>
<td><code>with</code></td>
</tr>
</tbody>
</table>
<p>3.9 起 <code>match</code> / <code>case</code> / <code>type</code> 是 soft keyword，只在特定语法位置保留。</p>
<p><code>del</code> 既是关键字，也是<a href="https://docs.python.org/3/reference/simple_stmts.html#the-del-statement">删除语句</a>。</p>
<hr />
<h2>参考</h2>
<p>官方文档（<a href="https://docs.python.org/zh-cn/3/">中文版</a>）：</p>
<ol>
<li><a href="https://docs.python.org/3/tutorial/interpreter.html">Using the Python Interpreter</a></li>
<li><a href="https://docs.python.org/3/reference/lexical_analysis.html">Lexical analysis</a></li>
<li><a href="https://docs.python.org/3/reference/compound_stmts.html">Compound statements</a></li>
<li><a href="https://docs.python.org/3/library/keyword.html">keyword</a></li>
<li><a href="https://docs.python.org/3/library/functions.html#print">print()</a></li>
<li><a href="https://docs.python.org/3/howto/unicode.html">Unicode HOWTO</a></li>
</ol>
<p>PEP：</p>
<ol>
<li><a href="https://peps.python.org/pep-0008/">PEP 8 — Style Guide</a></li>
<li><a href="https://peps.python.org/pep-0257/">PEP 257 — Docstring Conventions</a></li>
<li><a href="https://peps.python.org/pep-0263/">PEP 263 — Defining Python Source Code Encodings</a></li>
</ol>
<p>菜鸟教程：</p>
<ol>
<li><a href="https://www.runoob.com/python3/python3-basic-syntax.html">菜鸟教程 · 基础语法</a></li>
<li><a href="https://www.runoob.com/python3/python3-comment.html">菜鸟教程 · 注释</a></li>
<li><a href="https://www.runoob.com/python3/python3-interpreter.html">菜鸟教程 · 解释器</a></li>
</ol>
<hr />
<p>[^shebang]: Unix 可执行脚本依赖 shebang；Windows 主要靠文件关联和 <code>py</code> 启动器。
[^kw]: 硬关键字当变量名是 <code>SyntaxError</code>。
[^comment]: 词法分析里注释从 <code>#</code> 到物理行结束，不含字符串内的井号。</p>
]]>
    </content>
  </entry>
  <entry>
    <id>https://blog.chuwu.top/posts/2025-12-31/hello_2026/</id>
    <updated>2025-12-31T00:00:00.000Z</updated>
    <title>Hello 2026！</title>
    <author>
      <name>ChuwuYo</name>
    </author>
    <link href="https://blog.chuwu.top/posts/2025-12-31/hello_2026/" rel="alternate"/>
    <published>2025-12-31T00:00:00.000Z</published>
    <summary>又过了一年，来到了2025的最后一天，Hello 2026！</summary>
    <content type="html" xml:base="https://blog.chuwu.top/posts/2025-12-31/hello_2026/">
      <![CDATA[<p>过去的一年对我来说还是跌宕起伏。</p>
<p>心里憋了很多话却说不出口。</p>
<p>脑中苦乐参半，眼睛也看不见前后的路途，未知是好事吗？</p>
<p>总是期待着有人来抚平创伤，却把当下也都丢掉了。</p>
<p>总之，2026年我会继续努力，努力成为一个更好的人。</p>
<p>希望大家都好!</p>
<p>爱你，也爱自己。</p>
<hr />
<p>:::tip
下面是原图
:::</p>
<p><img src="./images/Snow.webp" alt="Snow" />
<img src="./images/Snow2.webp" alt="Snow2" /></p>
]]>
    </content>
  </entry>
  <entry>
    <id>https://blog.chuwu.top/posts/2025-11-03/can/</id>
    <updated>2025-11-03T00:00:00.000Z</updated>
    <title>CAN 通信协议</title>
    <author>
      <name>ChuwuYo</name>
    </author>
    <link href="https://blog.chuwu.top/posts/2025-11-03/can/" rel="alternate"/>
    <published>2025-11-03T00:00:00.000Z</published>
    <summary>CAN 通信协议不完全指北, 包括物理层特征、协议核心机制、帧结构、编程开发</summary>
    <content type="html" xml:base="https://blog.chuwu.top/posts/2025-11-03/can/">
      <![CDATA[<hr />
<h2>CAN Protocols - BOSCH (控制器局域网络协议)</h2>
<ul>
<li>CAN 通信, 全称: <strong>控制器局域网 (Controller Area Network)</strong>, 是一种功能丰富的车用总线标准, 有效支持分布式控制系统的串行通信网络.</li>
<li>CAN 数据使用<strong>帧结构</strong>进行传输.</li>
<li>CAN 的物理层: <strong>CAN-H, CAN-L</strong>, 一般是<strong>差分信号</strong>, 根据两根线上的电位差来判断总线电平, 总线电平分为<strong>显性电平</strong>和<strong>隐性电平</strong>, 二者必居其一, 发送方通过使总线电平发生变化, 将消息发送给接收方.</li>
<li>CAN 总线两端有两个 $120\Omega$ 的<strong>终端电阻</strong>, 用作阻抗匹配, 抑制信号反射.</li>
<li>CAN <strong>非破坏性仲裁机制（基于标识符优先级）</strong>: 多个同时的回复不会造成乱码, 高优先级消息通过, 低优先级消息被忽略.</li>
<li>CAN <strong>过滤器</strong>: 对特定信息进行过滤, 只接收指定的有效信息.</li>
</ul>
<h2>特性</h2>
<ol>
<li>
<p><strong>多主控制与仲裁:</strong></p>
<ul>
<li><strong>所有节点都可发送 (多主).</strong></li>
<li><strong>非破坏性仲裁</strong>: 通过 <strong>标识符（ID）优先级</strong> 决定优先级. 高优先级消息通过, 低优先级消息主动退出并转为接收.</li>
</ul>
</li>
<li>
<p><strong>系统柔性与扩展性:</strong></p>
<ul>
<li><strong>无地址信息</strong>: 报文基于内容 (ID) 发送, 而非地址.</li>
<li><strong>易于扩展</strong>: 新增节点不影响现有节点软硬件及应用层.</li>
</ul>
</li>
<li>
<p><strong>高性能传输:</strong></p>
<ul>
<li><strong>通信速率快</strong>: 最高可达 $1\text{ Mbps}$ (短距离).</li>
<li><strong>通信距离远</strong>: 速率降低时, 最远可达 $10\text{ KM}$.</li>
</ul>
</li>
<li>
<p><strong>强大的错误处理:</strong></p>
<ul>
<li>具备 <strong>错误检测、通知和恢复</strong> 机制.</li>
<li><strong>自动重发</strong>: 发送单元检测到错误后, 会强制终止并自动重新发送消息, 直到成功.</li>
</ul>
</li>
<li>
<p><strong>故障隔离 (封闭):</strong></p>
<ul>
<li><strong>故障封闭功能</strong>: 能区分暂时错误和持续性故障.</li>
<li>对于持续性故障, 能将引起故障的单元 <strong>从总线上隔离</strong>, 避免影响整个网络.</li>
</ul>
</li>
<li>
<p><strong>连接节点数量:</strong></p>
<ul>
<li><strong>连接节点多</strong>: 理论上无限制, 但实际数量受限于通信速率、总线延迟和电气负载.</li>
</ul>
</li>
</ol>
<h2>物理层特征与时序同步</h2>
<p>CAN 协议经过 ISO 标准化后有两个标准: ISO11898 标准和 ISO11519-2 标准. 其中 ISO11898 是针对通信速率为 $125\text{ Kbps}\sim 1\text{ Mbps}$ 的高速通信标准, 而 ISO11519-2 是针对通信速率为 $125\text{ Kbps}$ 以下的低速通信标准. 这里使用 ISO11898 标准, $450\text{ Kbps}$ 的通信速率.</p>
<p><img src="./images/CAN%E7%89%A9%E7%90%86%E5%B1%82%E7%89%B9%E5%BE%81.webp" alt="CAN 物理层特征" /></p>
<h3>位时序 (Bit Timing)</h3>
<p>位时序是实现 CAN 网络同步的核心机制. 它将一个<strong>位时间 (Bit Time)</strong> 分解为 4 个时间段, 每个时间段由若干个<strong>时间量子 (Time Quantum, Tq)</strong> 构成. 通过设定位时序, 可以实现多个单元同时采样, 并任意设定采样点. 由发送单元在非同步的情况下发送的每秒钟的位数称为<strong>位速率 (Bit Rate)</strong>.</p>
<p><strong>一个位时间由以下 4 个段组成:</strong></p>
<table>
<thead>
<tr>
<th>段名称</th>
<th>作用说明</th>
<th>Tq 数范围</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>同步段 (SS: Synchronization Segment):</strong></td>
<td>多个连接在总线上的单元通过此段实现时序调整, 同步进行接收和发送的工作. 由隐性到显性或由显性到隐性电平的<strong>边沿</strong>最好出现在此段中.</td>
<td>恒定为 <strong>1Tq</strong></td>
</tr>
<tr>
<td><strong>传播时间段 (PTS: Propagation Time Segment):</strong></td>
<td>用于吸收网络上的<strong>物理延迟</strong> (包括发送单元的输出延迟、总线上传播延迟、接收单元的输入延迟) 的段.</td>
<td><strong>1~8Tq</strong></td>
</tr>
<tr>
<td><strong>相位缓冲段 1 (PBS1: Phase Buffer Segment 1):</strong></td>
<td>当信号边沿不能被包含于 SS 段中时, 可在此段进行补偿.</td>
<td><strong>1~8Tq</strong></td>
</tr>
<tr>
<td><strong>相位缓冲段 2 (PBS2: Phase Buffer Segment 2):</strong></td>
<td>用于吸收细微的<strong>时钟误差</strong>. 可通过对该段<strong>加减 SJW</strong> (再同步补偿宽度) 来吸收误差.</td>
<td><strong>2~8Tq</strong></td>
</tr>
</tbody>
</table>
<p><strong>关键概念:</strong></p>
<ul>
<li><strong>再同步补偿宽度 (SJW: reSynchronization Jump Width):</strong> SJW 是用于补偿因时钟频率偏差、传送延迟等导致的同步误差的最大值. 其 Tq 数范围为 <strong>1~4Tq</strong></li>
<li><strong>采样点 (Sampling Point):</strong> 位于 <strong>PBS1 结束处</strong>. 它是指读取总线电平, 并将读到的电平作为位值的点.</li>
</ul>
<p><img src="./images/%E4%BD%8D%E7%9A%84%E6%9E%84%E6%88%90.webp" alt="位的构成" /></p>
<p>:::tip
上图的采样点, 是指读取总线电平, 并将读到的电平作为位值的点. 位置在 PBS1 结束处. 根据这个位时序, 可以计算 CAN 通信的波特率.
:::</p>
<h2>协议核心机制</h2>
<h3>CAN 总线仲裁机制</h3>
<p>CAN 总线仲裁机制是一种基于<strong>标识符优先级</strong>的<strong>非破坏性仲裁</strong>机制, 确保高优先级消息能够优先使用总线.</p>
<p><strong>1. 仲裁基础</strong></p>
<ul>
<li><strong>多主控制:</strong> 当总线处于空闲状态时, 任何连接到总线的单元 (节点) 都可以尝试开始发送消息.</li>
<li><strong>非破坏性:</strong> 仲裁过程不会中断或破坏总线上正在进行的传输, 低优先级的消息会主动退出, 高优先级的消息不受影响地继续传输.</li>
</ul>
<p><strong>2. 优先级确定</strong></p>
<ul>
<li><strong>基于标识符 (ID):</strong> 消息的<strong>标识符优先级</strong>由其在仲裁段中的<strong>标识符 (ID)</strong> 决定. ID 并不是设备的地址, 而是消息内容的标识和优先级.</li>
<li><strong>优先级规则:</strong>
<ul>
<li><strong>显性电平 (Dominant/逻辑 0):</strong> 具有优先权, 它会覆盖 <strong>隐性电平 (Recessive/逻辑 1).</strong></li>
<li><strong>ID 值越低 (即 ID 前导的显性电平越多):</strong> 消息的优先级就越高.</li>
</ul>
</li>
</ul>
<p><strong>3. 仲裁过程</strong></p>
<p><img src="./images/%E4%BB%B2%E8%A3%81%E8%BF%87%E7%A8%8B.webp" alt="仲裁过程" /></p>
<ul>
<li><strong>逐位比较:</strong> 所有同时发送消息的单元从仲裁段的第一位开始, 逐个位地将自己发送的位电平与总线上的实际电平进行比较.</li>
<li><strong>仲裁获胜:</strong>
<ul>
<li>当一个单元发送<strong>显性电平 (Dominant)</strong>, 且总线上也为显性电平, 则该单元继续发送.</li>
<li><strong>连续输出显性电平最多的单元</strong>将获胜, 并获得总线的控制权.</li>
</ul>
</li>
<li><strong>仲裁失利 (退出):</strong>
<ul>
<li>当一个单元发送<strong>隐性电平 (Recessive/逻辑 1)</strong>, 但同时检测到总线上是<strong>显性电平 (Dominant/逻辑 0)</strong> 时, 表明总线上有更高优先级的消息正在发送.</li>
<li>该单元判定仲裁失利, <strong>立即停止发送</strong>当前消息, 并转为<strong>接收状态</strong>, 等待总线再次空闲后重新尝试发送.</li>
</ul>
</li>
</ul>
<p>:::tip
上图中, 单元 1 和单元 2 同时开始向总线发送数据, 开始部分他们的数据格式是一样的, 故无法区分优先级, 直到 T 时刻, 单元 1 输出隐性电平, 而单元 2 输出显性电平, 此时单元 1 仲裁失利, 立刻转入接收状态工作, 不再与单元 2 竞争, 而单元 2 则顺利获得总线使用权, 继续发送自己的数据. 这就实现了仲裁, 让连续发送显性电平多的单元获得总线使用权.
:::</p>
<p><strong>4. 结果</strong></p>
<ul>
<li>仲裁段结束后, 总线上只有仲裁获胜 (标识符优先级最高) 的消息得以继续完整传输, 确保了实时性和高效性.</li>
</ul>
<h3>消息过滤机制 (Filter)</h3>
<p>CAN 过滤器是 CAN 控制器硬件的重要组成部分, 其核心作用在于对总线上的<strong>报文 ID</strong> 进行高效筛选, 决定哪些报文需要被接收和处理, 哪些报文应该被丢弃.</p>
<p><strong>1. 目的</strong></p>
<ul>
<li><strong>减轻 CPU 负担:</strong> 硬件过滤器在接收消息进入 FIFO (接收缓冲区) 之前就完成 ID 匹配, 避免 CPU 被大量无关数据中断和处理, 从而专注于业务逻辑.</li>
<li><strong>提高效率:</strong> 确保节点只接收与其功能相关的消息, 实现了高效的数据管理.</li>
</ul>
<p><strong>2. 工作原理：筛选模式</strong></p>
<p>CAN 过滤器通过两个核心参数: <strong>过滤器标识符寄存器 (ID Register)</strong> 和 <strong>过滤器屏蔽寄存器 (Mask Register)</strong> 来配置筛选规则.</p>
<table>
<thead>
<tr>
<th>模式</th>
<th>描述</th>
<th>应用场景</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>标识符列表模式 (List Mode)</strong></td>
<td>屏蔽寄存器中的位<strong>全部设置为不关心 (0)</strong>. 过滤器寄存器中存储的是<strong>完整的目标 ID</strong>. 只有当接收到的报文 ID 与过滤器 ID <strong>完全匹配</strong>时才会被接收.</td>
<td>需要精确接收少数几个固定 ID 的报文时.</td>
</tr>
<tr>
<td><strong>屏蔽模式 (Mask Mode)</strong></td>
<td>屏蔽寄存器中的位用于指定 ID 中<strong>哪些位必须匹配</strong> (设置为 1), <strong>哪些位可以忽略</strong> (设置为 0). 接收到的报文 ID 只有在"必须匹配"的位上与过滤器 ID 相符时才会被接收.</td>
<td>需要接收某一<strong>范围 ID</strong> 的报文时, 或者需要区分标准帧和扩展帧时.</td>
</tr>
</tbody>
</table>
<p><strong>3. 硬件实现（以 STM32 bxCAN 为例）</strong></p>
<p>在如 STM32 等微控制器中, CAN 控制器通常集成多达 <strong>28 个</strong>（或更多）独立的过滤器组，这些过滤器组可以配置为:</p>
<ul>
<li><strong>16 位模式:</strong> 每个过滤器组可配置为两个 16 位的过滤器（可以容纳 11 位标准帧 ID 和部分功能位）.</li>
<li><strong>32 位模式:</strong> 每个过滤器组可配置为一个 32 位的过滤器（可以容纳 29 位扩展帧 ID）.</li>
</ul>
<p>每个过滤器最终都会将匹配成功的报文路由到指定的<strong>接收 FIFO</strong>（通常是 FIFO0 或 FIFO1）中, 等待 CPU 读取.</p>
<h2>帧结构</h2>
<h3>CAN 协议的五种帧类型</h3>
<p>CAN 协议通过以下 <strong>5 种类型</strong>的帧进行通信:</p>
<ol>
<li><strong>数据帧 (Data Frame):</strong> 用于发送单元向接收单元传送数据. (有数据段)</li>
<li><strong>遥控帧 (远程帧)  (Remote Frame):</strong> 用于接收单元向具有相同 ID 的发送单元请求数据. (无数据段)</li>
<li><strong>错误帧 (Error Frame):</strong> 用于当检测出错误时向其他单元通知错误.</li>
<li><strong>过载帧 (Overload Frame):</strong> 用于接收单元通知其尚未做好接收准备.</li>
<li><strong>帧间隔 (Interframe Space):</strong> 用于将数据帧及遥控帧与前面的帧分离开来.</li>
</ol>
<p>其中, <strong>数据帧</strong>和<strong>遥控帧</strong>有<strong>标准格式</strong> (11 位标识符 ID) 和<strong>扩展格式</strong> (29 位 ID) 两种标识符长度.</p>
<h3>数据帧 (Data Frame) 结构</h3>
<p>CAN 数据帧是协议中最核心的帧类型, 它由以下 <strong>7 个段</strong>构成:</p>
<table>
<thead>
<tr>
<th>序号</th>
<th>段名称</th>
<th>作用说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>1</strong></td>
<td><strong>帧起始 (Start of Frame, SOF)</strong></td>
<td>表示数据帧开始的段.</td>
</tr>
<tr>
<td><strong>2</strong></td>
<td><strong>仲裁段 (Arbitration Field)</strong></td>
<td>表示该帧优先级的段, 包含报文的标识符 (ID), 用于总线仲裁.</td>
</tr>
<tr>
<td><strong>3</strong></td>
<td><strong>控制段 (Control Field)</strong></td>
<td>表示数据段的<strong>字节数</strong>及保留位的段.</td>
</tr>
<tr>
<td><strong>4</strong></td>
<td><strong>数据段 (Data Field)</strong></td>
<td>数据的内容, 一帧可发送 <strong>0 到 8 个字节</strong>的数据.</td>
</tr>
<tr>
<td><strong>5</strong></td>
<td><strong>CRC 段 (CRC Field)</strong></td>
<td>用于检查帧的传输错误的段.</td>
</tr>
<tr>
<td><strong>6</strong></td>
<td><strong>ACK 段 (Acknowledge Field)</strong></td>
<td>用于表示确认正常接收的段. 接收到正确消息的单元在此段发送显性电平, 通知发送单元正常接收.</td>
</tr>
<tr>
<td><strong>7</strong></td>
<td><strong>帧结束 (End of Frame, EOF)</strong></td>
<td>表示数据帧结束的段.</td>
</tr>
</tbody>
</table>
<p><img src="./images/%E6%95%B0%E6%8D%AE%E5%B8%A7%E7%9A%84%E6%9E%84%E6%88%90.webp" alt="数据帧的构成" /></p>
<p>:::tip
图中 D 表示显性电平, R 表示隐性电平 (下同)
:::</p>
<ol>
<li><strong>帧起始 (Start of Frame, SOF):</strong> 标准帧和扩展帧都是由 1 个位的显性电平表示帧起始.</li>
<li><strong>仲裁段 (Arbitration Field):</strong> 仲裁段, 表示数据优先级的段, 标准帧和扩展帧格式在本段有所区别.
<img src="./images/%E4%BB%B2%E8%A3%81%E6%AE%B5.webp" alt="仲裁段" />
<ul>
<li>标准格式的 ID 有 11 个位. 从 ID28 到 ID18 被依次发送. 禁止高 7 位都为隐性 (禁止设定: ID=1111111XXXX).</li>
<li>扩展格式的 ID 有 29 个位. 基本 ID 从 ID28 到 ID18, 扩展 ID 由 ID17 到 ID0 表示. 基本 ID 和标准格式的 ID 相同. 禁止高 7 位都为隐性 (禁止设定: 基本 ID=1111111XXXX). 其中 RTR 位用于标识是否是远程帧 (0, 数据帧; 1, 远程帧), IDE 位为标识符选择位 (0, 使用标准标识符; 1, 使用扩展标识符), SRR 位为代替远程请求位, 为隐性位, 它代替了标准帧中的 RTR 位.</li>
</ul>
</li>
<li><strong>控制段 (Control Field):</strong> 由 6 个位构成, 表示数据段的字节数. 标准帧和扩展帧的控制段稍有不同.
<img src="./images/%E6%8E%A7%E5%88%B6%E6%AE%B5.webp" alt="控制段" />
r0 和 r1 为保留位, 必须全部以显性电平发送, 但是接收端可以接收显性、隐性及任意组合的电平. DLC 段为数据长度表示段, 高位在前, DLC 段有效值为 0~8, 但是接收方接收到 9~15 的时候并不认为是错误.</li>
<li><strong>数据段 (Data Field):</strong> 从最高位 (MSB) 开始输出, 标准帧和扩展帧在这个段的定义都是一样的.
<img src="./images/%E6%95%B0%E6%8D%AE%E6%AE%B5.webp" alt="数据段" /></li>
<li><strong>CRC 段 (CRC Field):</strong> 用于检查帧的传输错误的段. 由 15 个位的 CRC 顺序和 1 个位的 CRC 界定符 (用于分隔的位) 组成, 标准帧和扩展帧在这个段的格式也是相同的.
<img src="./images/CRC%E6%AE%B5.webp" alt="CRC 段" />
此段 CRC 的值计算范围包括: 帧起始、仲裁段、控制段、数据段. 接收方以同样的算法计算 CRC 值并进行比较, 不一致时会通报错误.</li>
<li><strong>ACK 段 (Acknowledge Field):</strong> 用于表示确认正常接收的段. 由 ACK 槽 (ACK Slot) 和 ACK 界定符 2 个位组成. 标准帧和扩展帧在这个段的格式也是相同的.
<img src="./images/ACK%E6%AE%B5.webp" alt="ACK 段" />
发送单元的 ACK, 发送 2 个位的隐性位, 而接收到正常消息的单元在 ACK 槽 (ACK Slot) 发送显性位, 通知发送单元正常接收结束, 这个过程叫发送 ACK/返回 ACK. 发送 ACK 的是在既不处于总线关闭态也不处于休眠态的所有接收单元中, 接收到正常消息的单元 (发送单元不发送 ACK). 所谓正常消息是指不含填充错误、格式错误、CRC 错误的消息.</li>
<li><strong>帧结束 (End of Frame, EOF):</strong> 标准帧和扩展帧在这个段格式一样, 由 7 个位的隐性位组成.</li>
</ol>
<h2>编程开发</h2>
<p>CAN 协议的编程开发主要围绕微控制器（如 STM32）内部的 CAN 控制器模块（例如 <strong>bxCAN</strong>）进行. 开发的核心在于<strong>初始化</strong>和<strong>配置</strong>控制器, 使其能够正确地与物理总线同步, 并高效地处理报文.</p>
<h3>硬件初始化与总线配置</h3>
<p>这是 CAN 通信开始前必须完成的基础工作, 主要使用 <code>CAN_InitTypeDef</code> 结构体进行配置.</p>
<ol>
<li><strong>时钟与 GPIO 配置:</strong> 使能 CAN 控制器（如 CAN1）及相关 GPIO 口的时钟, 并配置 CAN_TX 和 CAN_RX 引脚（例如 STM32 需要配置 APB1 时钟）.</li>
<li><strong>CAN 控制器初始化:</strong> 设置 CAN 工作模式、自动重传、离线管理等功能, 并配置<strong>位时序（Bit Timing）确定所需的通信波特率</strong>.</li>
</ol>
<pre><code>void CAN_Config(void)
{
    // 1. 配置 GPIO 时钟和引脚 (略)
    // 2. 配置 CAN 时钟 (略)

    CAN_InitTypeDef CAN_InitStructure;

    // CAN 模块工作参数配置
    CAN_InitStructure.CAN_TTCM = DISABLE;          // 非时间触发通信模式
    CAN_InitStructure.CAN_ABOM = ENABLE;           // 自动离线管理
    CAN_InitStructure.CAN_AWUM = ENABLE;           // 自动唤醒模式
    CAN_InitStructure.CAN_NART = DISABLE;          // 自动重传
    CAN_InitStructure.CAN_RFLM = DISABLE;          // 不锁定 FIFO
    CAN_InitStructure.CAN_TXFP = DISABLE;          // 优先级由 ID 决定

    // 工作模式选择:
    // CAN_Mode_Normal (正常模式) 或 CAN_Mode_LoopBack (回环模式)
    CAN_InitStructure.CAN_Mode = CAN_Mode_LoopBack; // 示例使用回环模式进行自测

    // 位时序配置 (例如: 波特率 500Kbps)
    CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;        // 再同步跳转宽度 1Tq
    CAN_InitStructure.CAN_BS1 = CAN_BS1_8tq;        // 时间段 1: 8Tq
    CAN_InitStructure.CAN_BS2 = CAN_BS2_7tq;        // 时间段 2: 7Tq
    CAN_InitStructure.CAN_Prescaler = 6;           // 预分频器 (决定 Tq 长度)

    CAN_Init(CAN1, &amp;CAN_InitStructure);
    // ... 后续进行过滤器配置 ...
    CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE); // 开启 FIFO0 接收中断
}
</code></pre>
<h3>消息过滤机制配置</h3>
<p>使用 <code>CAN_FilterInitTypeDef</code> 结构体配置硬件过滤器, 确保接收效率.</p>
<ol>
<li><strong>过滤器初始化:</strong> 设置过滤器号、规模、工作模式和帧类型.</li>
<li><strong>ID 和屏蔽器设置:</strong> 确定接收规则（标识符列表模式或屏蔽模式）.</li>
<li><strong>FIFO 绑定:</strong> 指定匹配成功的报文进入哪个<strong>接收邮箱（FIFO0 或 FIFO1）</strong>.</li>
</ol>
<pre><code>// 承接上一段代码，设置过滤器结构体
CAN_FilterInitTypeDef CAN_FilterInitStructure;

CAN_FilterInitStructure.CAN_FilterNumber = 0;           // 过滤器 0
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask; // 屏蔽模式 (用于接收某一范围 ID)
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit; // 32 位规模

// 过滤器 ID 寄存器: 设定目标 ID
CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0120;      
CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;       

// 过滤器屏蔽寄存器: 决定 ID 中哪些位必须匹配
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0xFFF0;  
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;

CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0; // 匹配消息放入 FIFO0
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;      // 激活过滤器
CAN_FilterInit(&amp;CAN_FilterInitStructure);
</code></pre>
<h3>数据发送流程</h3>
<p>数据发送通过配置 <code>CanTxMsg</code> 结构体和调用 <code>CAN_Transmit()</code> 函数实现.</p>
<ol>
<li><strong>配置发送参数:</strong> 确定 ID、帧类型、数据长度（DLC, 0~8）和实际数据.</li>
<li><strong>发送请求:</strong> 写入空闲的发送邮箱并设置发送请求标志.</li>
</ol>
<pre><code>void CAN_Send_Msg(u8 *buf, u8 len)
{
    CanTxMsg TxMessage;
    TxMessage.StdId = 0x12;              // 消息 ID
    TxMessage.ExtId = 0x00;
    TxMessage.IDE = CAN_Id_Standard;     // 标准帧
    TxMessage.RTR = CAN_RTR_Data;        // 数据帧 (非遥控帧)
    TxMessage.DLC = len;                 // 数据长度 (0-8)

    for (int i = 0; i &lt; len; i++)
        TxMessage.Data[i] = buf[i];

    CAN_Transmit(CAN1, &amp;TxMessage);      // 写入发送邮箱并设置发送请求
}
</code></pre>
<h3>数据接收流程</h3>
<p>数据接收通常通过中断方式实现, 需要配置 NVIC 并在中断服务函数（ISR）中调用 <code>CAN_Receive()</code> 读取报文.</p>
<pre><code>// CAN FIFO0 接收中断服务函数 (STM32F103 的中断向量)
void USB_LP_CAN1_RX0_IRQHandler(void)
{
    CanRxMsg RxMessage;
    if (CAN_GetITStatus(CAN1, CAN_IT_FMP0) != RESET) // 检查 FIFO0 中是否有新消息
    {
        CAN_Receive(CAN1, CAN_FIFO0, &amp;RxMessage); // 读取消息到 RxMessage 结构体

        // 在这里处理接收到的数据, 例如:
        // u32 id = RxMessage.StdId;
        // u8 data0 = RxMessage.Data[0];
        
        // 示例：将接收到的数据在 LCD 上显示 (参考文档中的逻辑)
        // LCD_ShowxNum(..., RxMessage.Data[i], ...); 
    }
    CAN_ClearITPendingBit(CAN1, CAN_IT_FMP0); // 清除中断标志位
}
</code></pre>
<h3>模式选择</h3>
<p>在开发和测试中, 不同的工作模式用于不同的目的:</p>
<ul>
<li><strong>回环模式（LoopBack Mode）:</strong> 主要用于<strong>自测试</strong>和<strong>调试</strong>. 在这种模式下, CAN 控制器将自己发送的消息当作接收消息立即回传给自己, 不涉及外部 CAN 收发器和物理总线.</li>
<li><strong>正常模式（Normal Mode）:</strong> 用于<strong>正常通信</strong>, 此时 CAN 控制器将数据发送到总线, 并与其他节点进行交互.</li>
<li><strong>静默模式（Silent Mode）:</strong> 主要用于<strong>总线监测</strong>和诊断.</li>
</ul>
<hr />
<p>参考:</p>
<p><a href="http://cc.bjtu.edu.cn:81/meol/common/script/preview/download_preview.jsp?fileid=3057069&amp;resid=372513&amp;lid=25587&amp;preview=preview">STM32F1开发指南-库函数版本_V3.3</a><br />
<a href="https://doc.embedfire.com/products/link/zh/latest/tutorial/ebf_stm32_stdlib_tutorial.html">零死角玩转stm32</a><br />
<a href="https://zh.wikipedia.org/zh-cn/%E6%8E%A7%E5%88%B6%E5%99%A8%E5%8D%80%E5%9F%9F%E7%B6%B2%E8%B7%AF">Wikipedia-控制器區域網路</a></p>
]]>
    </content>
  </entry>
  <entry>
    <id>https://blog.chuwu.top/posts/2025-10-28/nec/</id>
    <updated>2025-10-28T00:00:00.000Z</updated>
    <title>NEC 红外协议笔记</title>
    <author>
      <name>ChuwuYo</name>
    </author>
    <link href="https://blog.chuwu.top/posts/2025-10-28/nec/" rel="alternate"/>
    <published>2025-10-28T00:00:00.000Z</published>
    <summary>NEC 红外协议指北，包括编解码过程、帧结构、示例等</summary>
    <content type="html" xml:base="https://blog.chuwu.top/posts/2025-10-28/nec/">
      <![CDATA[<h2>NEC Protocol - 日电</h2>
<ul>
<li>NEC 红外协议是一种广泛应用于家用电器遥控器的红外通信协议。</li>
<li>红外对射无法穿过障碍物：红外通信是视距 (Line of Sight) 通信，会被大部分非透明障碍物阻挡。</li>
<li>目前广泛使用的是：NEC Protocol的PWM（脉冲宽度调制）和Philips RC-5 Protocol的PPM（脉冲位置调制）。
<ul>
<li>PWM：红外载波的占空比表示‘0’和‘1’。发射时间固定，通过改变不发射载波的时间改变占空比。</li>
<li>PPM：发射载波的位置表示‘0’和‘1’。发射载波到不发射载波位‘0’，反之位‘1’。发射载波和不发射载波的时间相同，均为0.889ms，每位时间固定。</li>
</ul>
</li>
</ul>
<hr />
<h2>一、核心编码规则</h2>
<table>
<thead>
<tr>
<th>逻辑值</th>
<th>红外波形（发射端）</th>
<th>总时长</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>0</code></td>
<td>560 µs 高（载波） + 560 µs 低</td>
<td>≈ 1.125 ms</td>
<td>短间隔</td>
</tr>
<tr>
<td><code>1</code></td>
<td>560 µs 高（载波） + 1690 µs 低</td>
<td>≈ 2.25 ms</td>
<td>长间隔</td>
</tr>
</tbody>
</table>
<p>:::note</p>
<ul>
<li>“高电平” = 发射 38kHz 载波（burst）</li>
<li>“低电平” = 停止载波（静默）</li>
<li>所有数据位均以 <strong>560 µs 高电平</strong> 开始，通过后续低电平长度区分 0/1
:::</li>
</ul>
<hr />
<h2>二、完整帧结构（32位数据）</h2>
<p><img src="./images/%E7%BA%A2%E5%A4%96NEC%E5%8D%8F%E8%AE%AE%E7%BC%96%E7%A0%81%E8%AF%B4%E6%98%8E.webp" alt="红外 NEC 协议编码说明" /></p>
<table>
<thead>
<tr>
<th>字段（中文）</th>
<th>FIELD (English)</th>
<th>位数</th>
<th>示例（十六进制）</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>同步码头</td>
<td>Leader Pulse / Sync Header</td>
<td>—</td>
<td>9ms + 4.5ms</td>
<td>帧起始标志</td>
</tr>
<tr>
<td>地址码</td>
<td>Address Code</td>
<td>8位</td>
<td>0x12</td>
<td>设备地址</td>
</tr>
<tr>
<td>地址反码</td>
<td>Address Inverse</td>
<td>8位</td>
<td>0xED (= ~0x12)</td>
<td>地址校验</td>
</tr>
<tr>
<td>控制码</td>
<td>Command Code</td>
<td>8位</td>
<td>0x45</td>
<td>按键命令</td>
</tr>
<tr>
<td>控制反码</td>
<td>Command Inverse</td>
<td>8位</td>
<td>0xBA (= ~0x45)</td>
<td>命令校验</td>
</tr>
</tbody>
</table>
<p>:::note
总数据位：8 + 8 + 8 + 8 = <strong>32位</strong><br />
校验规则：<code>Address ^ AddressInverse == 0xFF</code>，<code>Command ^ CommandInverse == 0xFF</code>
:::</p>
<h3>示例题①</h3>
<p><img src="./images/%E7%A4%BA%E4%BE%8B%E9%A2%981.webp" alt="示例题1" /></p>
<table>
<thead>
<tr>
<th>字段名称</th>
<th>NEC 值 (LSB First)</th>
<th>十六进制值</th>
</tr>
</thead>
<tbody>
<tr>
<td>Address Code</td>
<td><code>0000 0000</code></td>
<td><code>0x00</code></td>
</tr>
<tr>
<td>Address Inverse</td>
<td><code>1111 1111</code></td>
<td><code>0xFF</code></td>
</tr>
<tr>
<td>Command Code</td>
<td><code>0100 0101</code></td>
<td><code>0x45</code></td>
</tr>
<tr>
<td>Command Inverse</td>
<td><code>1011 1010</code></td>
<td><code>0xBA</code></td>
</tr>
</tbody>
</table>
<p>过程：<br />
<code>start</code>: 9ms + 4.5ms<br />
<code>Address Code</code>: 00000000 → 00000000 (00)<br />
<code>Address Inverse1</code>: 11111111 → 11111111 (FF)<br />
<code>Command Code</code> (原始波形 MSB First → NEC 值 LSB First): 10100010 (A2) → 01000101 (45)<br />
<code>Command Inverse</code> (原始波形 MSB First → NEC 值 LSB First): 01011101 (5D) → 10111010 (BA)</p>
<h3>示例题②</h3>
<p><img src="./images/%E7%A4%BA%E4%BE%8B%E9%A2%982.webp" alt="示例题2" /></p>
<table>
<thead>
<tr>
<th>字段名称</th>
<th>NEC 值 (LSB First)</th>
<th>十六进制值</th>
</tr>
</thead>
<tbody>
<tr>
<td>Address Code</td>
<td><code>0000 0000</code></td>
<td><code>0x00</code></td>
</tr>
<tr>
<td>Address Inverse</td>
<td><code>1111 1111</code></td>
<td><code>0xFF</code></td>
</tr>
<tr>
<td>Command Code</td>
<td><code>0011 0000</code></td>
<td><code>0x30</code></td>
</tr>
<tr>
<td>Command Inverse</td>
<td><code>1100 1111</code></td>
<td><code>0xCF</code></td>
</tr>
</tbody>
</table>
<p>过程：<br />
<code>start</code>: 9ms + 4.5ms<br />
<code>Address Code</code>: 00000000 → 00000000 (00)<br />
<code>Address Inverse1</code>: 11111111 → 11111111 (FF)<br />
<code>Command Code</code> (原始波形 MSB First → NEC 值 LSB First): 00110000 (30) → 00001100 (0C)<br />
<code>Command Inverse</code> (原始波形 MSB First → NEC 值 LSB First): 1100 1111 (CF) → 11110011 (F3)</p>
<hr />
<h2>三、位序规则：LSB First（低位在前）</h2>
<p>所有字节（地址码、控制码等）均按 <strong>LSB First</strong> 发码：</p>
<table>
<thead>
<tr>
<th>发送顺序（时间先后）</th>
<th>对应位编号</th>
<th>在字节中的位置</th>
<th>权重（2ⁿ）</th>
</tr>
</thead>
<tbody>
<tr>
<td>第1位（最先发送）</td>
<td>bit0</td>
<td>最低位（LSB）</td>
<td>(2^0 = 1)</td>
</tr>
<tr>
<td>第2位</td>
<td>bit1</td>
<td></td>
<td>(2^1 = 2)</td>
</tr>
<tr>
<td>第3位</td>
<td>bit2</td>
<td></td>
<td>(2^2 = 4)</td>
</tr>
<tr>
<td>第4位</td>
<td>bit3</td>
<td></td>
<td>(2^3 = 8)</td>
</tr>
<tr>
<td>第5位</td>
<td>bit4</td>
<td></td>
<td>(2^4 = 16)</td>
</tr>
<tr>
<td>第6位</td>
<td>bit5</td>
<td></td>
<td>(2^5 = 32)</td>
</tr>
<tr>
<td>第7位</td>
<td>bit6</td>
<td></td>
<td>(2^6 = 64)</td>
</tr>
<tr>
<td>第8位（最后发送）</td>
<td>bit7</td>
<td>最高位（MSB）</td>
<td>(2^7 = 128)</td>
</tr>
</tbody>
</table>
<p>所有字节（地址码、控制码等）均按 <strong>LSB First</strong> 解码：</p>
<table>
<thead>
<tr>
<th>接收顺序（时间先后）</th>
<th>对应位编号</th>
<th>在字节中的位置</th>
<th>权重（2ⁿ）</th>
</tr>
</thead>
<tbody>
<tr>
<td>第1位（最先收到）</td>
<td>bit0</td>
<td>最低位（LSB）</td>
<td>(2^0 = 1)</td>
</tr>
<tr>
<td>第2位</td>
<td>bit1</td>
<td></td>
<td>(2^1 = 2)</td>
</tr>
<tr>
<td>第3位</td>
<td>bit2</td>
<td></td>
<td>(2^2 = 4)</td>
</tr>
<tr>
<td>第4位</td>
<td>bit3</td>
<td></td>
<td>(2^3 = 8)</td>
</tr>
<tr>
<td>第5位</td>
<td>bit4</td>
<td></td>
<td>(2^4 = 16)</td>
</tr>
<tr>
<td>第6位</td>
<td>bit5</td>
<td></td>
<td>(2^5 = 32)</td>
</tr>
<tr>
<td>第7位</td>
<td>bit6</td>
<td></td>
<td>(2^6 = 64)</td>
</tr>
<tr>
<td>第8位（最后收到）</td>
<td>bit7</td>
<td>最高位（MSB）</td>
<td>(2^7 = 128)</td>
</tr>
</tbody>
</table>
<p>:::tip
示例：发送序列 <code>01000101</code> → 8位字节转换成16进制数 <code>0x45</code>
示例：接收序列 <code>10100010</code> → 组合为 <code>bit7~bit0 = 01000101 = 0x45</code>
:::</p>
<p><img src="./images/%E7%BA%A2%E5%A4%96%E6%8E%A5%E6%94%B6%E7%A8%8B%E5%BA%8F%E6%B5%81%E7%A8%8B%E5%9B%BE.webp" alt="红外接收程序流程图" /></p>
<hr />
<h2>四、NEC Protocol的 PWM （脉冲宽度调制）和 Philips RC-5 Protocol 的 PPM （脉冲位置调制）</h2>
<table>
<thead>
<tr>
<th>项目</th>
<th>NEC 协议（PWM 标准）</th>
<th>Philips RC-5 协议（PPM 标准）</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>编码类型</strong></td>
<td>脉冲宽度调制（PWM）&lt;br&gt;（通过改变无载波时间改变占空比）</td>
<td>脉冲位置调制（PPM）&lt;br&gt;（通过高低电平相位位置区分 0/1）</td>
</tr>
<tr>
<td><strong>载波频率</strong></td>
<td>38 kHz（典型）</td>
<td>36 kHz（典型）</td>
</tr>
<tr>
<td><strong>逻辑“0”表示</strong></td>
<td>560 µs 载波 + 560 µs 无载波</td>
<td>889 µs 低电平 + 889 µs 高电平</td>
</tr>
<tr>
<td><strong>逻辑“1”表示</strong></td>
<td>560 µs 载波 + 1690 µs 无载波</td>
<td>889 µs 高电平 + 889 µs 低电平</td>
</tr>
<tr>
<td><strong>位时长</strong></td>
<td>不固定（=1.12 ms 或 2.25 ms）</td>
<td>固定（≈1.778 ms）</td>
</tr>
<tr>
<td><strong>帧结构</strong></td>
<td>引导码 + 地址码（16位）+ 命令码（16位）</td>
<td>起始位（S）+ 场位（F）+ 控制位（C）+ 系统码（5位）+ 命令码（6位）</td>
</tr>
<tr>
<td><strong>位发送顺序</strong></td>
<td>LSB First（先发低位）</td>
<td>MSB First（先发高位）</td>
</tr>
<tr>
<td><strong>重复按键机制</strong></td>
<td>全码后发送简码（9ms + 2.25ms + 结束位），周期约 108ms</td>
<td>控制位（C）翻转，其余不变，无简码</td>
</tr>
<tr>
<td><strong>校验方式</strong></td>
<td>命令码与其反码配对校验</td>
<td>无显式反码，靠双相位和控制位防误触发</td>
</tr>
<tr>
<td><strong>典型芯片</strong></td>
<td>WD6122、uPD6121</td>
<td>SAA3010</td>
</tr>
<tr>
<td><strong>应用场景</strong></td>
<td>电视、空调、机顶盒等家电</td>
<td>Philips 音响、老式电视等</td>
</tr>
</tbody>
</table>
<hr />
<h2>五、编程实现</h2>
<h3>1. 数据类型与存储策略</h3>
<p>虽然协议由 4 个 8 位字段组成，但在编程中通常将其“拼装”为 32 位整数进行处理：</p>
<ul>
<li><strong>基本单元</strong>：<code>uint8_t</code> 存储地址码、命令码及其反码。</li>
<li><strong>最佳实践</strong>：
<ul>
<li><strong>发送时</strong>：将 4 个 <code>uint8_t</code> 拼装成 1 个 <code>uint32_t</code>，再统一进行位发送循环。</li>
<li><strong>接收时</strong>：先接收 32 个位存入 <code>uint32_t</code>，接收完毕后再拆解为 4 个 <code>uint8_t</code> 校验和处理。</li>
</ul>
</li>
</ul>
<pre><code>// 接收端拆解示例 (假设 rx_data 为接收到的 uint32_t 完整帧)
uint8_t address = (uint8_t)(rx_data &amp; 0xFF);
uint8_t command = (uint8_t)((rx_data &gt;&gt; 16) &amp; 0xFF);
</code></pre>
<h3>2. 校验机制</h3>
<p>NEC 协议通过“原码 + 反码”进行错误检测。接收端的标准校验逻辑如下：</p>
<ul>
<li><strong>校验公式</strong>：<code>(原始值 ^ 反码值) == 0xFF</code></li>
<li>只有当地址码与命令码都通过校验时，才视为有效的帧。</li>
</ul>
<pre><code>// 校验逻辑示例
if ( (address ^ address_inv == 0xFF) &amp;&amp; (command ^ command_inv == 0xFF) ) {
    // 通过校验，执行命令
}
</code></pre>
<h3>3. 接收端的逻辑反相</h3>
<p>接收端输出电平与发射端的载波逻辑是相反 (Active Low) 的：</p>
<table>
<thead>
<tr>
<th>状态</th>
<th>发射端动作</th>
<th>接收头输出引脚</th>
<th>软件解码逻辑</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>有载波</strong></td>
<td>发射 38kHz 脉冲</td>
<td><strong>低电平 (LOW)</strong></td>
<td>对应协议中的“高”时长</td>
</tr>
<tr>
<td><strong>无载波</strong></td>
<td>静默</td>
<td><strong>高电平 (HIGH)</strong></td>
<td>对应协议中的“低”时长</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>编程关键</strong>：软件解码时，需测量的是<strong>引脚低电平</strong>的持续时间（判断是否为 560µs 载波）和随后的<strong>引脚高电平</strong>持续时间（据此判断是逻辑 <code>0</code> 还是 <code>1</code>）。</p>
</blockquote>
<h3>4. 时序测量方式</h3>
<p>由于 NEC 协议对微秒级时序敏感，推荐使用硬件资源进行测量：</p>
<ul>
<li><strong>定时器输入捕获 (Input Capture)</strong>。由硬件自动记录引脚跳变时刻，精度最高，CPU 占用最少。</li>
<li><strong>外部中断 (GPIO Interrupt)</strong>。在引脚跳变时触发中断记录时间，精度较好。</li>
<li><strong>轮询 (Polling)</strong>。容易受其他程序干扰导致时序测量误差，造成解码失败。</li>
</ul>
<h3>5. 状态机实现</h3>
<p>NEC 协议的接收端通常采用状态机结构，按顺序处理引导码、地址码、命令码、命令反码共 4 个字段。每个字段的接收过程中，根据引脚电平变化（高/低）切换状态，记录静默时间（载波时间固定为 560 µs）。</p>
<hr />
<h2>六、关键注意事项 (源自网络，注意甄别)</h2>
<ul>
<li>
<p><strong>载波参数</strong></p>
<ul>
<li>标准频率：<strong>38 kHz</strong>（由 455 kHz 晶振 ÷12 得到）</li>
<li>推荐占空比：<strong>1:3</strong>（高电平占 1/4 周期），但 1:1 或 1:2 在多数接收头下也能工作。</li>
</ul>
</li>
<li>
<p><strong>帧结构完整性</strong></p>
<ul>
<li><strong>必须包含结束位</strong>：全码和简码末尾均需发送 <strong>0.56 ms 载波</strong>，否则部分设备无法识别。</li>
<li><strong>16 位地址码顺序</strong>：先发<strong>低字节</strong>，再发<strong>高字节</strong>，每个字节内部仍为 LSB First。</li>
</ul>
</li>
<li>
<p><strong>重复码（简码）机制</strong></p>
<ul>
<li>按键长按时，<strong>首帧发全码</strong>，后续每 <strong>108 ms</strong> 发送一次简码：<br />
<code>9 ms 载波 + 2.25 ms 无载波 + 0.56 ms 载波（结束位）</code></li>
<li>若省略简码，某些设备（如空调）将无法响应长按操作。</li>
</ul>
</li>
<li>
<p><strong>发送间隔与时序</strong></p>
<ul>
<li>两次全码之间建议间隔 <strong>≥110 ms</strong>，避免接收端因繁忙而丢帧。</li>
<li>连续快速发送多帧容易导致第二帧失效，应该要加入合适的延时。</li>
<li>Sony、机顶盒等二次译码 MCU 在 65 ms 内就准备接收下一帧；做万能遥控可把“全码间隔”做成 60–130 ms 可配置，否则会出现“第二键无响应”。</li>
</ul>
</li>
<li>
<p><strong>接收端信号反相</strong></p>
<ul>
<li>一体化红外接收头输出波形与发射端<strong>逻辑相反</strong>（载波时输出低电平），解码前需注意是否需软件取反。</li>
<li>注意输出类型（开漏 / 推挽）</li>
</ul>
</li>
<li>
<p><strong>厂商变种的兼容性</strong></p>
<ul>
<li>部分品牌（如美的、新科）可能会用 NEC 的变种协议：帧长可能达 120 ms 甚至 500 ms。</li>
<li>通用遥控器建议支持<strong>自学习模式</strong>或动态解析引导码/位宽。</li>
</ul>
</li>
<li>
<p><strong>硬件设计建议</strong></p>
<ul>
<li>红外发射管驱动电流建议 ≥100 mA，推荐使用恒流电路（如射极跟随器）以维持发射强度稳定。</li>
<li>接收头电源引脚需加 <strong>≥22 µF 滤波电容</strong>，必要时串接 330 Ω 电阻抑制电源干扰。</li>
</ul>
</li>
</ul>
<hr />
<p>参考：</p>
<p><a href="https://www.cnblogs.com/mylinux/p/5084264.html">NEC协议</a><br />
<a href="https://blog.csdn.net/wuhenyouyuyouyu/article/details/105900088">红外遥控编码与解码</a><br />
<a href="https://zhuanlan.zhihu.com/p/627341600">STM32F103正点原子学习笔记系列——REMOTE</a><br />
<a href="https://www.sbprojects.net/knowledge/ir/nec.php">SB-Projects NEC Protocol</a><br />
<a href="https://blog.embeddedexpert.io/?p=1138">NEC Protocol Decoding with STM32</a><br />
<a href="https://doc.yonyoucloud.com/doc/wiki/project/mcu-tutorial-three/nec-protocol-infrared-remote-controller.html">NEC 协议红外遥控器</a></p>
]]>
    </content>
  </entry>
  <entry>
    <id>https://blog.chuwu.top/posts/numpy/numpy2/</id>
    <updated>2025-10-25T00:00:00.000Z</updated>
    <title>Numpy学习笔记(2)</title>
    <author>
      <name>ChuwuYo</name>
    </author>
    <link href="https://blog.chuwu.top/posts/numpy/numpy2/" rel="alternate"/>
    <published>2025-10-25T00:00:00.000Z</published>
    <summary>数组访问、形状操作与展平规则、数组组合、数组分割</summary>
    <content type="html" xml:base="https://blog.chuwu.top/posts/numpy/numpy2/">
      <![CDATA[<p>:::note
本笔记基于 Jupyter Lab
:::</p>
<p>:::tip
均从第0个元素开始表述（第0行 / 第0列）
:::</p>
<h2>数组访问</h2>
<h3>一维数组</h3>
<h4>整数与切片索引</h4>
<pre><code>import numpy as np
arr = np.arange(10)
print('整数索引结果为：', arr[5]) # 索引从 0 开始；也支持负数，`-1` 表示数组最后一个元素
print('切片结果为：', arr[2:7]) # 左闭右开，获取从 `start` 到 `end` 的子数组
print('省略起始索引结果为：', arr[:5]) # 默认从数组开头开始，获取到索引为 `end-1` 的元素
print('省略结束索引结果为：', arr[5:]) # 默认获取到数组末尾，包含索引为 `start` 的元素
</code></pre>
<h4>带步长的切片索引</h4>
<pre><code>arr[2:4] = 100, 101
print('索引结果为：', arr) # 左闭右开，切片修改数组元素

print('正步长索引结果为：', arr[::2]) # 左闭右开，每隔 2 个元素取一个，组成子数组
print('负步长索引结果为：', arr[5:1:-1]) # 反向切片，步长为 -1，数组倒序（开始索引必须大于结束索引）
</code></pre>
<h3>多维数组</h3>
<pre><code>import numpy as np
arr = np.array([[1, 2, 3, 4, 5], [4, 5, 6, 7, 8], [7, 8, 9, 10, 11]])
print('创建的二维数组为：\n', arr)
</code></pre>
<h4>单维度切片索引</h4>
<ul>
<li><code>arr[行索引, 列起始索引:列结束索引] 或 arr[行起始索引:行结束索引, 列索引]</code></li>
</ul>
<pre><code>print('单维度切片索引结果为：', arr[0, 3:5]) # 左闭右开，索引第0行中第3、4列的元素
print('索引结果为：', arr[:, 2]) # 索引第2列的元素
</code></pre>
<h4>多维度切片索引</h4>
<ul>
<li><code>arr[行起始索引:行结束索引, 列起始索引:列结束索引]</code></li>
</ul>
<pre><code>print('多维度切片索引结果为：', arr[1:, 2:]) # 索引第1行及之后行中第2列到最后列的元素
print('索引结果为：', arr[:, 1:4]) # 左闭右开，选取所有行中，列索引从 1 到 3 的元素
</code></pre>
<h4>整数序列切片索引</h4>
<ul>
<li><code>arr[(行索引序列), (列索引序列)]</code></li>
</ul>
<pre><code>print('整数序列索引结果为：', arr[(0, 1, 2), (1, 2, 3)]) # 左闭右开，索引第0、1、2行中，第1、2、3列的元素
print('索引结果为：', arr[1:, (0, 2, 3)]) # 左闭右开，索引第1行及之后行中，第0、2、3列的元素
</code></pre>
<h4>布尔数组切片索引</h4>
<ul>
<li><code>arr[布尔数组, 列索引]</code></li>
</ul>
<pre><code>mask = np.array([1, 0, 1], dtype=np.bool_) # [True, False, True]
print('布尔数组索引结果为：', arr[mask, 2]) # 索引第0、2行中第2列的元素
</code></pre>
<h2>形状操作与展平规则</h2>
<pre><code>import numpy as np
arr = np.arange(12)  # 创建一维数组
print('创建的一维数组为：', arr) # [0 1 2 3 4 5 6 7 8 9 10 11]
</code></pre>
<h3>改变数组形状</h3>
<ul>
<li><code>数组.reshape(shape)</code></li>
</ul>
<pre><code>print('新的数组形状为：\n', arr.reshape(3, 4))  # 设置数组的形状为 3x4
</code></pre>
<h3>查看数组维数</h3>
<ul>
<li><code>数组.ndim / 数组.reshape(shape).ndim</code></li>
</ul>
<pre><code>print('数组维数为：', arr.ndim) # 查看数组的维数（轴数）
</code></pre>
<h3>数组展平</h3>
<ul>
<li>将多维数组展平为一维数组</li>
</ul>
<pre><code>import numpy as np
arr = np.arange(12).reshape(3, 4)
print('创建的二维数组为：\n', arr) # [[ 0  1  2  3] [ 4  5  6  7] [ 8  9 10 11]]
</code></pre>
<h4>数组.ravel()</h4>
<pre><code># 返回原数组的视图（修改展平后的数组可能会影响原数组）
print('数组展平为：', arr.ravel()) # [ 0  1  2  3  4  5  6  7  8  9 10 11]
</code></pre>
<h4>数组.flatten()</h4>
<pre><code># 返回原数组的副本（修改展平后的数组不会影响原数组）
# 支持指定展平顺序（默认按行序展平）
print('横向展平为：', arr.flatten()) # [ 0  1  2  3  4  5  6  7  8  9 10 11]
print('纵向展平为：', arr.flatten('F')) # [ 0  4  8  1  5  9  2  6 10  3  7 11]
</code></pre>
<h2>数组组合</h2>
<h3>数组元素运算</h3>
<ul>
<li><strong>格式示例</strong>：<code>新数组 = 原数组 * 数值</code>（或 <code>原数组 + 数值</code> 等）</li>
</ul>
<table>
<thead>
<tr>
<th>运算类型</th>
<th>格式示例</th>
<th>运算逻辑</th>
<th><code>arr1 = np.array([1, 2, 3, 4])</code></th>
<th>运算结果（新数组）</th>
</tr>
</thead>
<tbody>
<tr>
<td>加法</td>
<td><code>新数组 = 原数组 + 数值</code></td>
<td>每个元素加指定数值</td>
<td><code>arr1 + 2</code></td>
<td><code>[3, 4, 5, 6]</code></td>
</tr>
<tr>
<td>减法</td>
<td><code>新数组 = 原数组 - 数值</code></td>
<td>每个元素减指定数值</td>
<td><code>arr1 - 1</code></td>
<td><code>[0, 1, 2, 3]</code></td>
</tr>
<tr>
<td>乘法</td>
<td><code>新数组 = 原数组 * 数值</code></td>
<td>每个元素乘指定数值</td>
<td><code>arr1 * 3</code></td>
<td><code>[3, 6, 9, 12]</code></td>
</tr>
<tr>
<td>除法</td>
<td><code>新数组 = 原数组 / 数值</code></td>
<td>每个元素除以指定数值（结果为浮点）</td>
<td><code>arr1 / 2</code></td>
<td><code>[0.5, 1.0, 1.5, 2.0]</code></td>
</tr>
<tr>
<td>整除</td>
<td><code>新数组 = 原数组 // 数值</code></td>
<td>每个元素整除指定数值（丢弃小数）</td>
<td><code>arr1 // 2</code></td>
<td><code>[0, 1, 1, 2]</code></td>
</tr>
<tr>
<td>取余</td>
<td><code>新数组 = 原数组 % 数值</code></td>
<td>每个元素对指定数值取余</td>
<td><code>arr1 % 2</code></td>
<td><code>[1, 0, 1, 0]</code></td>
</tr>
<tr>
<td>幂运算</td>
<td><code>新数组 = 原数组 ** 数值</code></td>
<td>每个元素的指定次幂</td>
<td><code>arr1 ** 2</code></td>
<td><code>[1, 4, 9, 16]</code></td>
</tr>
<tr>
<td>大于比较（布尔）</td>
<td><code>新数组 = 原数组 &gt; 数值</code></td>
<td>每个元素与数值比较，返回布尔值</td>
<td><code>arr1 &gt; 2</code></td>
<td><code>[False, False, True, True]</code></td>
</tr>
<tr>
<td>小于比较（布尔）</td>
<td><code>新数组 = 原数组 &lt; 数值</code></td>
<td>每个元素与数值比较，返回布尔值</td>
<td><code>arr1 &lt; 3</code></td>
<td><code>[True, True, False, False]</code></td>
</tr>
<tr>
<td>等于比较（布尔）</td>
<td><code>新数组 = 原数组 == 数值</code></td>
<td>每个元素与数值比较，返回布尔值</td>
<td><code>arr1 == 4</code></td>
<td><code>[False, False, False, True]</code></td>
</tr>
<tr>
<td>不等于比较（布尔）</td>
<td><code>新数组 = 原数组 != 数值</code></td>
<td>每个元素与数值比较，返回布尔值</td>
<td><code>arr1 != 2</code></td>
<td><code>[True, False, True, True]</code></td>
</tr>
</tbody>
</table>
<h4>数组与数组的逐元素运算（形状需相同）</h4>
<table>
<thead>
<tr>
<th>运算类型</th>
<th>格式示例</th>
<th>运算逻辑</th>
<th><code>arr1 = np.array([1,2,3]), arr2 = np.array([4,5,6])</code></th>
<th>运算结果（新数组）</th>
</tr>
</thead>
<tbody>
<tr>
<td>数组加法</td>
<td><code>新数组 = 数组A + 数组B</code></td>
<td>对应位置元素相加</td>
<td><code>arr1 + arr2</code></td>
<td><code>[5, 7, 9]</code></td>
</tr>
<tr>
<td>数组减法</td>
<td><code>新数组 = 数组A - 数组B</code></td>
<td>对应位置元素相减</td>
<td><code>arr1 - arr2</code></td>
<td><code>[-3, -3, -3]</code></td>
</tr>
<tr>
<td>数组乘法</td>
<td><code>新数组 = 数组A * 数组B</code></td>
<td>对应位置元素相乘（非矩阵乘法）</td>
<td><code>arr1 * arr2</code></td>
<td><code>[4, 10, 18]</code></td>
</tr>
<tr>
<td>数组除法</td>
<td><code>新数组 = 数组A / 数组B</code></td>
<td>对应位置元素相除（结果为浮点）</td>
<td><code>arr1 / arr2</code></td>
<td><code>[0.25, 0.4, 0.5]</code></td>
</tr>
</tbody>
</table>
<h3><code>np.hstack</code> 横向组合数组</h3>
<ul>
<li>要求待组合的数组在除水平方向外的其他维度上形状一致</li>
<li><code>np.hstack((数组1, 数组2, ...))</code></li>
</ul>
<pre><code>import numpy as np
arr1 = np.array([[1, 2, 3], [4, 5, 6]])
arr2 = np.array([[7, 8, 9], [10, 11, 12]])
print('横向组合后的数组为：\n', np.hstack((arr1, arr2)))
#横向组合后的数组为：
 #[[ 1  2  3  7  8  9]
 #[ 4  5  6 10 11 12]]
</code></pre>
<h3><code>np.vstack</code> 纵向组合数组</h3>
<ul>
<li>要求待组合的数组在除垂直方向外的其他维度上形状一致</li>
<li><code>np.vstack((数组1, 数组2, ...))</code></li>
</ul>
<pre><code>print('纵向组合后的数组为：\n', np.vstack((arr1, arr2)))
#纵向组合后的数组为：
 #[[ 1  2  3]
 #[ 4  5  6]
 #[ 7  8  9]
 #[10 11 12]]
</code></pre>
<h3><code>np.concatenate</code> 组合数组</h3>
<ul>
<li><code>np.concatenate((数组1, 数组2, ...), axis=轴编号)</code></li>
<li>待组合的数组，在除 <code>axis</code> 指定的维度外的其他所有维度上，形状必须完全一致
<ul>
<li><code>axis</code> 用于指定数组组合的方向维度
<ul>
<li><code>axis=1</code> ，<strong>横向组合</strong>（水平方向，列维度方向），与 <code>np.hstack</code> 类似</li>
<li><code>axis=0</code> ，<strong>纵向组合</strong>（垂直方向，行维度方向），与 <code>np.vstack</code> 类似</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code>print('横向组合后的数组为：\n', np.concatenate((arr1, arr2), axis=1))
#横向组合后的数组为：
 #[[ 1  2  3  7  8  9]
 #[ 4  5  6 10 11 12]]
</code></pre>
<pre><code>print('纵向组合后的数组为：\n', np.concatenate((arr1, arr2), axis=0))
#纵向组合后的数组为：
 #[[ 1  2  3]
 #[ 4  5  6]
 #[ 7  8  9]
 #[10 11 12]]
</code></pre>
<h2>数组分割</h2>
<pre><code>arr = np.arange(16).reshape(4, 4)
print('创建的二维数组为：\n', arr)
#创建的二维数组为：
 #[[ 0  1  2  3]
 #[ 4  5  6  7]
 #[ 8  9 10 11]
 #[12 13 14 15]]
</code></pre>
<h3><code>np.hsplit</code> 横向分割数组</h3>
<ul>
<li>按照列的维度，将数组平均分割成指定的份数</li>
<li><code>np.hsplit(数组, 分割份数)</code></li>
</ul>
<pre><code>print('横向分割后的数组为：\n', np.hsplit(arr, 2))
#横向分割后的数组为：
# [array([[ 0,  1],
#       [ 4,  5],
#       [ 8,  9],
#       [12, 13]]), array([[ 2,  3],
#       [ 6,  7],
#       [10, 11],
#       [14, 15]])]
</code></pre>
<h3><code>np.vsplit</code> 纵向分割数组</h3>
<ul>
<li>按照行的维度，将数组平均分割成指定的份数</li>
<li><code>np.vsplit(数组, 分割份数)</code></li>
</ul>
<pre><code>print('纵向分割后的数组为：\n', np.vsplit(arr, 2))
#纵向分割后的数组为：
# [array([[ 0,  1,  2,  3],
#       [ 4,  5,  6,  7]]), array([[ 8,  9, 10, 11],
#       [12, 13, 14, 15]])]
</code></pre>
<h3><code>np.split</code> 分割数组</h3>
<ul>
<li><code>np.split(数组, 分割份数, axis=轴编号)</code>
<ul>
<li><code>axis</code> 用于指定数组分割的方向维度
<ul>
<li><code>axis=1</code> ，<strong>横向分割</strong>（水平方向，列维度方向），与 <code>np.hsplit</code> 类似</li>
<li><code>axis=0</code> ，<strong>纵向分割</strong>（垂直方向，行维度方向），与 <code>np.vsplit</code> 类似</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code>print('横向分割后的数组为：\n', np.split(arr, 2, axis=1))
#横向分割后的数组为：
# [array([[ 0,  1],
#       [ 4,  5],
#       [ 8,  9],
#       [12, 13]]), array([[ 2,  3],
#       [ 6,  7],
#       [10, 11],
#       [14, 15]])]
</code></pre>
<pre><code>print('纵向分割后的数组为：\n', np.split(arr, 2, axis=0))
#纵向分割后的数组为：
# [array([[ 0,  1,  2,  3],
#       [ 4,  5,  6,  7]]), array([[ 8,  9, 10, 11],
#       [12, 13, 14, 15]])]
</code></pre>
<hr />
<p><strong>参考</strong>：</p>
<ul>
<li><a href="https://numpy.com.cn/doc/stable/user/index.html">NumPy 中文文档</a></li>
</ul>
]]>
    </content>
  </entry>
  <entry>
    <id>https://blog.chuwu.top/posts/numpy/numpy1/</id>
    <updated>2025-10-24T00:00:00.000Z</updated>
    <title>Numpy学习笔记(1)</title>
    <author>
      <name>ChuwuYo</name>
    </author>
    <link href="https://blog.chuwu.top/posts/numpy/numpy1/" rel="alternate"/>
    <published>2025-10-24T00:00:00.000Z</published>
    <summary>轴的概念、数组属性、数组创建、数组数据类型、生成随机数</summary>
    <content type="html" xml:base="https://blog.chuwu.top/posts/numpy/numpy1/">
      <![CDATA[<p>:::note
本笔记基于 Jupyter Lab
:::</p>
<ul>
<li>Numpy 是一个开源的 Python 库，它提供了高性能的数组对象，以及数学函数，用于操作这些数组, 广泛应用于科学和工程领域。</li>
</ul>
<p>:::important
导入Numpy
:::</p>
<pre><code>import numpy as np
</code></pre>
<p>这种约定允许使用简短易识别的前缀 (np.) 访问 NumPy 功能，同时将 NumPy 功能与同名其他功能区分开来。</p>
<h2>轴（Axis）的基本概念</h2>
<p>在 NumPy 中，轴（<code>axis</code>）是描述数组维度方向的核心概念，<strong>编号从 0 开始</strong>，从“最外层”到“最内层”。<br />
<strong>规律</strong>：轴编号 = 维度层级，轴 0 为最外层，编号越大，操作越聚焦内层结构。</p>
<h3>一维数组（1D Array）</h3>
<ul>
<li><strong>轴 0（<code>axis=0</code>）</strong>：唯一维度方向（如直线延伸）。
<ul>
<li>示例：<code>[1,2,3,4]</code>，轴 0 表示从第一个到最后一个元素的方向。</li>
</ul>
</li>
</ul>
<h3>二维数组（2D Array）</h3>
<ul>
<li><strong>轴 0（<code>axis=0</code>）</strong>：垂直方向（行维度）。
<ul>
<li>示例：<code>[[1,2],[3,4]]</code>，沿轴 0 求和：<code>[1+3, 2+4] = [4,6]</code>。</li>
</ul>
</li>
<li><strong>轴 1（<code>axis=1</code>）</strong>：水平方向（列维度）。
<ul>
<li>示例：<code>[[1,2],[3,4]]</code>，沿轴 1 求和：<code>[1+2, 3+4] = [3,7]</code>。</li>
</ul>
</li>
</ul>
<h3>三维数组（3D Array）</h3>
<ul>
<li><strong>轴 0（<code>axis=0</code>）</strong>：二维数组的堆叠方向（深度）。
<ul>
<li>示例：<code>[[[1,2],[3,4]], [[5,6],[7,8]]]</code>，沿轴 0 求和：<code>[[1+5,2+6],[3+7,4+8]] = [[6,8],[10,12]]</code>。</li>
</ul>
</li>
<li><strong>轴 1（<code>axis=1</code>）</strong>：二维数组内部的垂直方向（行维度）。
<ul>
<li>示例：上述数组，沿轴 1 求和：<code>[[1+3,2+4], [5+7,6+8]] = [[4,6],[12,14]]</code>。</li>
</ul>
</li>
<li><strong>轴 2（<code>axis=2</code>）</strong>：二维数组内部的水平方向（列维度）。
<ul>
<li>示例：上述数组，沿轴 2 求和：<code>[[1+2,3+4], [5+6,7+8]] = [[3,7],[11,15]]</code>。</li>
</ul>
</li>
</ul>
<h2>数组属性</h2>
<p>NumPy 的数组类名为 ndarray。它也以别名 array 著称。
:::warning
请注意，numpy.array 与标准 Python 库中的 array.array 类不同，后者只处理一维数组，功能较少。
:::</p>
<pre><code>import numpy as np
arr = np.array([[[1,2,3,4,5],[1,2,3,4,5],[1,2,3,4,5]]])
print(arr)          # 输出数组，注意格式：中括号，元素间无逗号（区别于列表）
print(arr.ndim)     # 数组的轴（维度）数量
print(arr.shape)    # 数组的维度，整数元组表示每个维度的大小
print(arr.size)     # 数组的元素总数，等于 shape 中元素的乘积
print(arr.dtype)    # 数组中元素的类型，可为标准 Python 类型或 NumPy 类型
print(arr.itemsize) # 数组中每个元素的字节大小，等同于 dtype.itemsize
print(arr.data)     # 包含数组实际元素的缓冲区，通常通过索引访问元素
</code></pre>
<p>输出结果：
<img src="./images/%E6%95%B0%E7%BB%84%E5%B1%9E%E6%80%A7.webp" alt="alt text" /></p>
<h2>数组创建</h2>
<h3>使用 <code>array</code> 函数创建</h3>
<p>从普通的 Python 列表或元组创建数组：</p>
<pre><code>import numpy as np
a = np.array([2, 3, 4])
print(a)          # array([2, 3, 4])
print(a.dtype)    # dtype('int64')

b = np.array([1.2, 3.5, 5.1])
print(b.dtype)    # dtype('float64')
</code></pre>
<ul>
<li><strong>参数</strong>：<code>np.array(object, dtype=None, copy=True, order='K', subok=False, ndmin=0)</code>
<ul>
<li><code>object</code>：输入数据，可以是列表、元组或其他数组。</li>
<li><code>dtype</code>：数组元素的数据类型，可选。</li>
<li><code>copy</code>：是否复制输入数据，默认为 <code>True</code>。</li>
<li><code>order</code>：存储顺序，<code>C</code> 表示行优先，<code>F</code> 表示列优先。</li>
<li><code>subok</code>：默认返回基础类的数组，若为 <code>True</code> 则返回子类。</li>
<li><code>ndmin</code>：指定返回数组的最小维数。</li>
</ul>
</li>
</ul>
<p>注意：调用 <code>array</code> 时需提供单一序列作为参数：</p>
<pre><code>a = np.array([1, 2, 3, 4])  # 正确
</code></pre>
<p>将序列的序列转换为二维数组：</p>
<pre><code>b = np.array([(1.5, 2, 3), (4, 5, 6)])
print(b)
# array([[1.5, 2. , 3. ],
#        [4. , 5. , 6. ]])
</code></pre>
<p>显式指定数组类型：</p>
<pre><code>c = np.array([[1, 2], [3, 4]], dtype=complex)
print(c)
# array([[1.+0.j, 2.+0.j],
#        [3.+0.j, 4.+0.j]])
</code></pre>
<h3>重设数组的 <code>shape</code> 属性</h3>
<p>可以通过修改数组的 <code>shape</code> 属性来重设数组的形状，而无需创建新数组。</p>
<pre><code>import numpy as np
arr = np.array([1, 2, 3, 4, 5, 6])
arr.shape = (2, 3)  # 将一维数组重设为二维数组
print(arr)
# 输出：
# array([[1, 2, 3],
#        [4, 5, 6]])
</code></pre>
<h3>使用占位符函数创建</h3>
<p>创建具有初始占位符内容的数组：</p>
<pre><code>np.zeros((3, 4))
# array([[0., 0., 0., 0.],
#        [0., 0., 0., 0.],
#        [0., 0., 0., 0.]])

np.ones((2, 3, 4), dtype=np.int16)
# array([[[1, 1, 1, 1],
#         [1, 1, 1, 1],
#         [1, 1, 1, 1]],
#
#        [[1, 1, 1, 1],
#         [1, 1, 1, 1],
#         [1, 1, 1, 1]]], dtype=int16)

np.empty((2, 3))
# array([[...],  # 内容可能因内存状态而异（随机）
#        [...]])

np.eye(3)
# array([[1., 0., 0.],
#        [0., 1., 0.],
#        [0., 0., 1.]])

np.diag([1, 2, 3])
# array([[1, 0, 0],
#        [0, 2, 0],
#        [0, 0, 3]])
</code></pre>
<p>:::tip
其余占位符函数不再列举，请参阅<a href="https://numpy.com.cn/doc/stable/reference/routines.array-creation.html">数组创建例程</a>
:::</p>
<ul>
<li><strong>参数</strong>：
<ul>
<li>
<p><code>np.zeros(shape, dtype=float, order='C')</code></p>
<ul>
<li><code>shape</code>：数组的形状，必须指定。</li>
<li><code>dtype</code>：数组元素的数据类型，默认为 <code>float</code>。</li>
<li><code>order</code>：存储顺序，<code>C</code> 表示行优先，<code>F</code> 表示列优先。</li>
</ul>
</li>
<li>
<p><code>np.ones(shape, dtype=None, order='C')</code></p>
<ul>
<li>参数与 <code>np.zeros</code> 相同。</li>
</ul>
</li>
<li>
<p><code>np.empty(shape, dtype=float, order='C')</code></p>
<ul>
<li>参数与 <code>np.zeros</code> 相同。</li>
</ul>
</li>
<li>
<p><code>np.eye(N, M=None, k=0, dtype=&lt;class 'float'&gt;, order='C')</code></p>
<ul>
<li><code>N</code>：行数。</li>
<li><code>M</code>：列数，默认为 <code>N</code>。</li>
<li><code>k</code>：对角线的索引，默认为 <code>0</code>（主对角线）。</li>
</ul>
</li>
<li>
<p><code>np.diag(v, k=0)</code></p>
<ul>
<li><code>v</code>：输入数组。</li>
<li><code>k</code>：对角线的索引，默认为 <code>0</code>（主对角线）。</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3>创建数字序列的数组</h3>
<h4><code>arange</code> 函数：</h4>
<pre><code>np.arange(10, 30, 5)
# array([10, 15, 20, 25])

np.arange(0, 2, 0.3)
# array([0. , 0.3, 0.6, 0.9, 1.2, 1.5, 1.8])
</code></pre>
<ul>
<li><strong>参数</strong>：<code>np.arange(start, stop, step)</code>
<ul>
<li><code>start</code>：起始值（包含），若省略则默认为 <code>0</code>。</li>
<li><code>stop</code>：终止值（不包含），必须指定。</li>
<li><code>step</code>：步长，若省略则默认为 <code>1</code>；步长可为整数或浮点数，用于控制相邻元素的间隔。</li>
</ul>
</li>
</ul>
<h4><code>linspace</code> 函数：</h4>
<pre><code>from numpy import pi
np.linspace(0, 2, 9)
# array([0.  , 0.25, 0.5 , 0.75, 1.  , 1.25, 1.5 , 1.75, 2.  ])
</code></pre>
<ul>
<li><strong>参数</strong>：<code>np.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None, axis=0)</code>
<ul>
<li><code>start</code>：序列的起始值。</li>
<li><code>stop</code>：序列的终止值。</li>
<li><code>num</code>：生成的样本数，默认为 <code>50</code>。</li>
<li><code>endpoint</code>：是否包含终止值，默认为 <code>True</code>。</li>
<li><code>retstep</code>：是否返回样本间距，默认为 <code>False</code>。</li>
<li><code>dtype</code>：输出数组的数据类型。</li>
<li><code>axis</code>：结果中样本的轴，默认为 <code>0</code>。</li>
</ul>
</li>
</ul>
<h4><code>logspace</code> 函数</h4>
<p>生成对数刻度的数值序列。</p>
<pre><code>arr = np.logspace(1, 3, num=5)
</code></pre>
<ul>
<li><strong>参数</strong>：<code>start, stop, num=50, endpoint=True, base=10.0, dtype=None, axis=0</code>
<ul>
<li><code>start</code>：对数刻度的起始值。</li>
<li><code>stop</code>：对数刻度的终止值。</li>
<li><code>num</code>：生成的样本数，默认为 <code>50</code>。</li>
<li><code>base</code>：对数的底数，默认为 <code>10</code>。</li>
</ul>
</li>
</ul>
<p>:::note
<code>arange</code> 函数和 <code>linspace</code> 函数的区别：</p>
<ul>
<li><code>arange</code> 函数生成的序列包含起始值，不包含终止值，步长可以是整数或浮点数。</li>
<li><code>linspace</code> 函数生成的序列包含起始值，默认也包含终止值，样本数可以自定义。
:::</li>
</ul>
<h2>数组数据类型</h2>
<p>常见的数据类型包括：</p>
<ul>
<li><code>int</code>：整数类型，如 <code>np.int8</code>、<code>np.int16</code>、<code>np.int32</code>、<code>np.int64</code>。</li>
<li><code>float</code>：浮点类型，如 <code>np.float32</code>、<code>np.float64</code>。</li>
<li><code>bool</code>：布尔类型，<code>np.bool_</code>。</li>
<li><code>complex</code>：复数类型，如 <code>np.complex64</code>、<code>np.complex128</code>。</li>
</ul>
<h3>数据类型转换示例</h3>
<pre><code>import numpy as np
print('转换结果为：', np.float64(42))   # 整数转换为浮点数（精确转换）
print('转换结果为：', np.int8(42.0))   # 浮点数转换为整数（截断小数取整）
print('转换结果为：', np.bool_(42))   # 整数转换为布尔值（非零整数均为 True）
print('转换结果为：', np.bool_(0))    # 整数转换为布尔值（0 为 False）
print('转换结果为：', np.float64(True))  # 布尔值转换为浮点数
print('转换结果为：', np.float64(False)) # 布尔值转换为浮点数
</code></pre>
<h3><code>np.dtype</code> 函数</h3>
<p><code>np.dtype</code> 用于定义数组元素的数据类型，支持标准 Python 类型和 NumPy 类型。</p>
<h4><code>np.dtype</code> 的参数</h4>
<ul>
<li><strong><code>dtype</code></strong>：数据类型，可以是 NumPy 类型（如 <code>np.int32</code>）或标准 Python 类型（如 <code>int</code>）。</li>
<li><strong><code>byteorder</code></strong>：字节顺序，可选值为 <code>&gt;</code>（大端）或 <code>&lt;</code>（小端）。</li>
<li><strong><code>align</code></strong>：是否对齐字段，默认为 <code>False</code>。</li>
<li><strong><code>subdtype</code></strong>：子类型，用于描述数组的维度。</li>
</ul>
<h4>创建数据类型</h4>
<pre><code>import numpy as np
# 定义一个数组并指定数据类型
arr = np.array([1, 2, 3], dtype=np.float64)
print(arr)
# array([1., 2., 3.])
print(arr.dtype)
# dtype('float64')
</code></pre>
<h4>查看已有数组类型</h4>
<pre><code>import numpy as np
arr = np.array([1, 2, 3], dtype=np.float32)
print("数组数据类型：", np.dtype(arr.dtype))
# dtype('float32')
</code></pre>
<h4>自定义数据类型</h4>
<pre><code>import numpy as np
# 定义一个自定义数据类型
df = np.dtype([('name', 'U10'), ('quantity', 'i4'), ('price', 'f4')])
itemz = np.array([('tomatoes', 42, 4.14), ('cabbages', 13, 1.72)], dtype=df)
print('数组为：', itemz)
print('自定义数据为：', itemz.dtype)
# array([('tomatoes', 42, 4.14), ('cabbages', 13, 1.72)],
#       dtype=[('name', '&lt;U10'), ('quantity', '&lt;i4'), ('price', '&lt;f4')])
</code></pre>
<h2>生成随机数</h2>
<h3><code>np.random.random</code> 函数</h3>
<p>生成 [0, 1) 区间的均匀分布随机数。</p>
<pre><code>rand_array = np.random.random((3, 2))
</code></pre>
<ul>
<li><strong>参数</strong>：<code>size</code>：指定生成数组的形状，类型为 <code>int</code> 或 <code>tuple</code>，如 <code>(2, 3)</code> 表示 2x3 的数组。</li>
</ul>
<h3><code>np.random.rand</code> 函数</h3>
<p>生成 [0, 1) 区间的均匀分布随机数，支持直接传递形状参数。</p>
<pre><code>rand_array = np.random.rand(3, 2)
</code></pre>
<ul>
<li><strong>参数</strong>：<code>shape</code>：指定生成数组的形状，类型为 <code>int</code> 或 <code>tuple</code>。</li>
</ul>
<h3><code>np.random.randn</code> 函数</h3>
<p>生成标准正态分布（均值为 0，标准差为 1）的随机数。</p>
<pre><code>rand_normal = np.random.randn(3, 2)
</code></pre>
<ul>
<li><strong>参数</strong>：<code>shape</code>：指定生成数组的形状，类型为 <code>int</code> 或 <code>tuple</code>。</li>
</ul>
<h3><code>np.random.randint</code> 函数</h3>
<p>生成指定范围内的随机整数。</p>
<pre><code>rand_int = np.random.randint(10, 50, size=(3, 2), dtype=int)
</code></pre>
<ul>
<li><strong>参数</strong>：<code>low, high=None, size=None</code>
<ul>
<li><code>low</code>：随机整数的下界（包含）。</li>
<li><code>high</code>：随机整数的上界（不包含）。</li>
<li><code>size</code>：生成数组的形状。</li>
<li><code>dtype</code>：输出数组的数据类型，默认为 <code>int</code>。</li>
</ul>
</li>
</ul>
<hr />
<p><strong>参考</strong>：</p>
<ul>
<li><a href="https://numpy.com.cn/doc/stable/user/index.html">NumPy 中文文档</a></li>
</ul>
]]>
    </content>
  </entry>
  <entry>
    <id>https://blog.chuwu.top/posts/2025-10-02/tcp-window-optimization/</id>
    <updated>2025-10-02T00:00:00.000Z</updated>
    <title>浅谈 TCP 窗口优化</title>
    <author>
      <name>ChuwuYo</name>
    </author>
    <link href="https://blog.chuwu.top/posts/2025-10-02/tcp-window-optimization/" rel="alternate"/>
    <published>2025-10-02T00:00:00.000Z</published>
    <summary>谈谈 TCP 窗口优化</summary>
    <content type="html" xml:base="https://blog.chuwu.top/posts/2025-10-02/tcp-window-optimization/">
      <![CDATA[<h1>TCP 窗口优化</h1>
<p>不论宽带速度（例 500Mbps 或 1000Mbps），连接到一些海外服务器或物理距离遥远的数据中心或机房时，下载或传输速度有时特别的慢，与测试结果不符，<br />
很大程度上是高网络延迟（RTT）与 TCP 协议的固有设计共同作用的结果。</p>
<h2>1. 高延迟下的 TCP 吞吐量陷阱</h2>
<p>所有基于 TCP 的网络传输（如网页浏览、文件下载、VPN 连接等）都依赖于一个叫作 TCP 窗口的机制。窗口的作用是限制发送方在收到接收方确认之前可以发送的数据量。</p>
<h3>为什么高延迟是性能杀手？</h3>
<p>根据 TCP 的设计，数据吞吐量受到以下公式的严格限制：</p>
<p>$吞吐量 ≤ 窗口大小 / 往返时间 (RTT)$</p>
<p>在跨越国家或大洲的长距离连接中，RTT 动辄超过 150 毫秒。而原始的 TCP 窗口大小只有 65535 字节（64 KiB - 1）。这意味着我们的发送方在发出少量数据后，必须等待漫长的时间（一个 RTT 周期）才能得到确认并发送下一批数据，导致带宽大量闲置。</p>
<h3>长距离连接对窗口的需求：带宽时延积 (BDP)</h3>
<p>为了完全跑满我们的带宽，TCP 窗口的大小必须能覆盖网络管道的实际容量，这被称为带宽时延积 (BDP)：</p>
<p>$BDP = 带宽 × RTT$</p>
<p><strong>实际计算示例</strong>：假设我们的宽带是 1 Gbps，而连接到北美西海岸服务器的 RTT 是 150 毫秒 (0.15 秒)：</p>
<ul>
<li>原始窗口（65535 字节）的最大理论吞吐量仅约为 3.49 Mbit/秒。</li>
<li>根据 BDP 计算，我们需要的最优 TCP 窗口大小约为 18.75 MB。只有当窗口能扩展到 18.75 MB 时，我们才有机会跑满 1 Gbps 的带宽。</li>
</ul>
<h2>2. Windows 的解法：窗口自动调优</h2>
<p>好消息是，微软早就意识到了这个问题。自 Windows Vista 以来，Windows 系统默认启用了一项名为 <strong>“TCP 自动调优接收窗口”（Receive Window Auto-Tuning）</strong> 的功能。</p>
<h3>自动调优的作用</h3>
<ul>
<li><strong>启用窗口缩放</strong>：它利用 TCP 窗口缩放扩展（Window Scaling），打破了 64KB 的原始限制，允许窗口大小动态扩展到数 MB 甚至更高（最高可达 1 GiB）。</li>
<li><strong>动态匹配 BDP</strong>：Windows 系统会持续监测当前的网络 RTT 和应用程序的数据处理速度，智能地将 TCP 窗口大小调整到尽可能接近 BDP 的最优值。（当然是理论上）</li>
</ul>
<h3>如何检查自动调优状态？</h3>
<p>我们可以通过 PowerShell 或 CMD 运行以下命令，查看我们的 TCP 全局参数。请关注 “Receive Window Auto-Tuning Level” 的值。</p>
<pre><code># 在 Windows 10/11 的 PowerShell 或 CMD 中执行
netsh interface tcp show global
</code></pre>
<p>如果其值为 <code>normal</code>，则表示自动调优已开启并处于正常工作状态。如果是 <code>disabled</code>，执行下面的命令。</p>
<pre><code># 启用自动调优（仅在确认被禁用时使用）
netsh interface tcp set global autotuninglevel=normal
</code></pre>
<h2>3. RFC 1323 TCP 扩展</h2>
<p>RFC 1323（TCP Extensions for High Performance）是 TCP 的经典标准。定义了三个关键扩展，让 TCP 在高带宽、高时延（高 BDP）的情况保持一定的性能。</p>
<h3>窗口缩放（Window Scaling）：</h3>
<p>原始 TCP 的窗口字段只有 16 位，最多 65535 字节，在高 BDP 场景下（如 1Gbps 带宽 + 150ms RTT）根本不够用——窗口太小，数据流动缓慢。RFC 1323 引入窗口缩放选项，将窗口定义扩展到 32 位（约 4GB），通过一个缩放因子（scale factor，0-14，对应 2^0 到 2^14 倍）在 SYN 包中协商。</p>
<ul>
<li><strong>协商机制</strong>：连接建立时（SYN 段），发送方在 TCP 选项中声明 Window Scale（WSOPT，Kind=3，Length=3，Shift.cnt=缩放值）。接收方如果支持，也在 SYN-ACK 中回应相同的选项。</li>
<li><strong>性能益处</strong>：拿我们上面的示例来说（BDP=18.75MB），如果 Shift.cnt=14（16384 倍），16 位字段只需 1146（18.75MB / 16384）就可以覆盖整个Tunnel，避免带宽闲置。</li>
</ul>
<p><strong>Tips</strong>：用 Wireshark 抓包查看 SYN 包的选项字段是否有 WSOPT ，确认缩放是否生效。</p>
<h3>TCP 时间戳（Timestamps）：</h3>
<p>高延迟网络下，RTT 估算不准会导致拥塞控制失灵，窗口调整迟钝。RFC 1323 的时间戳选项（TSOPT，Kind=8）在每个段中嵌入 32 位时间戳（TSval）和回显值（TSecr），用于两个目的：</p>
<ul>
<li><strong>RTT 测量（RTTM）</strong>：接收方回显发送方的 TSval，发送方计算精确 RTT（不受重传干扰），优化窗口增长和重传超时。</li>
<li><strong>序列号包装保护（PAWS）</strong>：在高吞吐量的情况下，32 位序列号容易wrap around，通过时间戳检查，过时的包会被丢弃，保证序列正常。</li>
</ul>
<p><strong>Tips</strong>：时间戳虽然增加了 12 字节的开销，但感觉是利大于弊 —— Red Hat 等系统默认启用，能更好地估算 RTT ， Windows 也默认支持。</p>
<blockquote>
<p>时间戳可能泄露系统时钟，但 RFC 建议随机偏移来防止时序攻击，要自己考量</p>
</blockquote>
<h3>路径 MTU 发现（Path MTU Discovery）</h3>
<p>虽然不是 RFC 1323 核心，但1323更新了 RFC 1191 的 PMTUD，支持了 DF（Don't Fragment）位和 ICMP 反馈，动态寻找链路里最大的传输单元（MTU）。这防止了分片开销，在高延迟链路中减少重传的次数。当我们用 VPN ， MTU 也不会乱序匹配，这确保了包大小自适应，避免“黑洞”路由出现。</p>
<blockquote>
<p>RFC 1323 已于 2015 年被 RFC 7323 更新（obsoleted），但核心机制不变。 在 Windows 上，通过 netsh 命令来调整（比如打开 PMTUD）</p>
</blockquote>
<h2>4. 还有什么要说的……</h2>
<h3>常见瓶颈排查</h3>
<ul>
<li><strong>路由器/ISP 限制</strong>：检查是否启用了 QoS 限制，或 ISP 的国际出口堵塞。可以用 <code>tracert google.com</code> 看看高延迟的节点。</li>
<li><strong>DNS</strong>：测一下延迟，建议用低延迟的非运营商 DNS 。</li>
<li><strong>连接方式</strong>：优先用有线连接，减少 Wi-Fi 信号弱导致的衰减。</li>
<li><strong>MTU</strong>：默认 MTU 1500 适合大多数场景，但如果连续使用 VPN，可以试试修改：<code>netsh interface ipv4 set subinterface "以太网" mtu=1400 store=persistent</code>。</li>
<li><strong>你在哪里</strong>：地区的不同导致出口延迟不同，建议肉身横渡。</li>
</ul>
<h3>额外优化</h3>
<ul>
<li><strong>启用 CTCP（复合 TCP）</strong>：增强拥塞控制：<code>netsh interface tcp set global congestionprovider=ctcp</code>。在高带宽高延迟场景下更高效。</li>
<li><strong>禁用 Nagle 算法</strong>（游戏/Live）：<code>netsh interface tcp set global nagle=disabled</code>。但这会增加小包开销，不适合文件传输，其实不太推荐。</li>
<li><strong>VPN 优化</strong>：如果经常用 VPN，选择 WireGuard 协议，会自动处理窗口缩放。</li>
<li><strong>禁用代理（ProxyEnable）</strong>：如果用不着代理服务器，那开启代理可能会增加额外延迟。通过注册表编辑器（regedit）导航到 <code>HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings</code>，将 <code>ProxyEnable</code> 的值设置为 <code>0</code>（DWORD 类型）。</li>
</ul>
<hr />
<p><strong>参考</strong>：<br />
<a href="https://www.ietf.org/rfc/rfc1323.txt">ietf.org</a><br />
<a href="https://knowledge.broadcom.com/external/article/167353/window-scaling-rfc-1323-support-on-the-e.html">knowledge.broadcom.com</a><br />
<a href="https://www.cisco.com/c/en/us/td/docs/ios-xml/ios/ipapp/configuration/xe-16-12/iap-xe-16-12-book/iap-tcp.html">cisco.com</a><br />
<a href="https://learn.microsoft.com/en-us/troubleshoot/windows-server/networking/description-tcp-features">learn.microsoft.com</a>
<a href="https://www.rfc-editor.org/rfc/rfc7323.html">rfc-editor.org</a></p>
]]>
    </content>
  </entry>
  <entry>
    <id>https://blog.chuwu.top/posts/2025-09-30/2025-national-day/</id>
    <updated>2025-09-30T00:00:00.000Z</updated>
    <title>2025国庆</title>
    <author>
      <name>ChuwuYo</name>
    </author>
    <link href="https://blog.chuwu.top/posts/2025-09-30/2025-national-day/" rel="alternate"/>
    <published>2025-09-30T00:00:00.000Z</published>
    <summary>2025年的国庆假期到了</summary>
    <content type="html" xml:base="https://blog.chuwu.top/posts/2025-09-30/2025-national-day/">
      <![CDATA[<p>又到国庆节了，终于放假了，但还是要学习</p>
<p>出去吃了顿小巷子中的火锅，味道一般，但是每道菜的量好大啊</p>
<p>讨厌调休</p>
]]>
    </content>
  </entry>
  <entry>
    <id>https://blog.chuwu.top/posts/2025-09-29/unveiling/</id>
    <updated>2025-09-29T00:00:00.000Z</updated>
    <title>序章</title>
    <author>
      <name>ChuwuYo</name>
    </author>
    <link href="https://blog.chuwu.top/posts/2025-09-29/unveiling/" rel="alternate"/>
    <published>2025-09-29T00:00:00.000Z</published>
    <summary>ChuwuYo 博客的第一个版本，使用 Charlotte 主题构建</summary>
    <content type="html" xml:base="https://blog.chuwu.top/posts/2025-09-29/unveiling/">
      <![CDATA[<h1>欢迎来到 Chuwu 的博客。</h1>
<h2>这个博客是使用 <a href="https://astro.build/">Astro</a> 框架和基于 <a href="https://github.com/saicaca/fuwari">Fuwari</a> 的 <a href="https://github.com/ChuwuYo/Charlotte">Charlotte主题</a> 构建的。</h2>
<p>关于是否建立一个博客，其实我想了很久，最后还是说服了自己，希望这份热情能持续下去。</p>
<h3>不知道我们能走多久呢……</h3>
<hr />
<p>感谢以下项目与项目开发者们：</p>
<ul>
<li><a href="https://github.com/saicaca/fuwari">saicaca-Fuwari</a></li>
<li><a href="https://github.com/afoim/fuwari">afoim(二叉树树)-Fuwari</a></li>
<li><a href="https://github.com/matsuzaka-yuki/Mizuki">yuki(松坂有希)-Mizuki</a></li>
<li><a href="https://github.com/xingpingcn/enhanced-FaaS-in-China">enhanced-FaaS-in-China</a></li>
<li><a href="https://vercel.cdn.yt-blog.top/">Vercel CDN</a></li>
</ul>
]]>
    </content>
  </entry>
  <entry>
    <id>https://blog.chuwu.top/posts/2025-02-05/moss-veined-folds/</id>
    <updated>2025-02-05T00:00:00.000Z</updated>
    <title>苔痕的褶皱</title>
    <author>
      <name>ChuwuYo</name>
    </author>
    <link href="https://blog.chuwu.top/posts/2025-02-05/moss-veined-folds/" rel="alternate"/>
    <published>2025-02-05T00:00:00.000Z</published>
    <summary>苔痕的褶皱 - 也于是我迷失的记忆</summary>
    <content type="html" xml:base="https://blog.chuwu.top/posts/2025-02-05/moss-veined-folds/">
      <![CDATA[<h1>苔痕的褶皱</h1>
<hr />
<p>虽然村庄陈旧又腐朽，但我依旧嗅闻得到这片土地的生机勃勃。</p>
<p>南方的山永远苍翠，永远湿漉漉，永远带着毛绒绒的云雾。</p>
<p>村子总会在不经意间变得熙熙攘攘。死尸、活人、牲畜、作物、神明、鬼怪，所有的存在或是不存在都揉在一块儿，好似不存在什么界限一般。</p>
<p>我喜欢走在田埂与水泥路的交界处，冬天的时候枯黄的草在单薄衣服上刮着。 空旷却拥塞的田野，牛从不奔跑，狗常对空吠，鸟不知在土堆上捡什么吃。</p>
<p>不觉地走到了村尾，便倚着驳色的墙板就地坐下了。偶能听见老伯的呛咳声伴着羊羔的嘶鸣挤出小屋。若是向屋内俯身探头，会收获那份如一的笑容吧，我想。</p>
<p>老伯并不住在这所小屋，他不在这吃饭睡觉，但这里有他的生活。</p>
<p>这样的村庄是一种复合的味道，硝烟柴火化作烟雾往天上跑。汗津津的动物们、青苔覆盖的砖瓦、陈腐了多年的木头，这种味道，说不上来。</p>
<p>我又去了巷子口那座破败的茅草屋，它在一堆青石砖瓦间显得那么突兀。过去的哪个时代的遗留产物因为一些历史原因无人打扰，便不受干扰地自由地荒废，成为一栋茂盛生长的房屋。</p>
<p>难以想象一个人留在老屋里是怎样的孤寂。</p>
<p>若干年前我站在累起的谷堆上，看见他扬起稻子，笑着问我要不要也试一试。谷粒粘在爷爷未曾被浸染的黑发上。</p>
<p>如今的爷爷，不知何时也会和剥去米粒的稻谷一起飘然化作烟尘，我看不清楚。我能看到的只有他再也挺不起来的腰。</p>
<p>由他保留着的、人家的那串钥匙都去哪里了呢。</p>
<p>我家的房顶已经长满苔藓了。</p>
<hr />
<h1>附录</h1>
<ul>
<li>AI文本检测为0%
<img src="./images/%E6%9C%B1%E9%9B%80AI%E7%94%9F%E6%88%90%E6%A3%80%E6%B5%8B.webp" alt="朱雀AI生成检测" /></li>
</ul>
]]>
    </content>
  </entry>
  <entry>
    <id>https://blog.chuwu.top/posts/2023-11-23/c-access/</id>
    <updated>2023-11-23T00:00:00.000Z</updated>
    <title>C语言考查笔记</title>
    <author>
      <name>ChuwuYo</name>
    </author>
    <link href="https://blog.chuwu.top/posts/2023-11-23/c-access/" rel="alternate"/>
    <published>2023-11-23T00:00:00.000Z</published>
    <summary>初学C语言</summary>
    <content type="html" xml:base="https://blog.chuwu.top/posts/2023-11-23/c-access/">
      <![CDATA[<p>:::note
<strong>考查目标</strong>:</p>
<ol>
<li>掌握 C 语言程序结构</li>
<li>掌握 C 语言的基本数据类型及数据运算</li>
<li>掌握基本语句的使用</li>
<li>掌握选择结构编程</li>
<li>掌握循环结构编程</li>
<li>掌握数组存储与处理批量数据.</li>
<li>了解模块化程序设计思想, 学会使用函数进行简单编程.
:::</li>
</ol>
<hr />
<h2>1、C 语言程序的结构</h2>
<h3>1.1 程序的构成, main 函数和其他函数</h3>
<ul>
<li><strong>程序的构成</strong>: 一个 C 语言源程序由一个或多个源文件组成. 每个源文件可以由一个或多个函数组成.</li>
<li><strong>主函数 (<code>main</code> 函数)</strong>: 一个源程序有且只能有一个 <code>main</code> 函数, 它是主函数. 程序执行从 <code>main</code> 开始, 并在 <code>main</code> 中结束.</li>
<li><strong>函数组成</strong>: 函数由两部分组成: 声明 (Declaration) 和定义 (Definition, 即函数体, 要执行的代码).</li>
</ul>
<p><strong>示例代码: <code>main</code> 函数结构</strong></p>
<pre><code># include &lt;stdio.h&gt; // 预处理命令, 载入头文件
int main() // 主函数
 
{
    printf("Hello World!"); // 标准输出
    return 0;
}
</code></pre>
<h3>1.2 头文件, 数据说明, 程序注释</h3>
<ul>
<li><strong>头文件</strong>: 预处理命令 (以 <code>#</code> 开头) 通常应放在源文件或源程序的最前面.
<ul>
<li>例如, 使用 <code>#include &lt;stdio.h&gt;</code> 来获取标准输入输出函数.</li>
<li>系统库中的头文件使用尖括号 <code>&lt; &gt;</code> 引用; 本地目录中的头文件使用双引号 <code>""</code> 引用.</li>
</ul>
</li>
<li><strong>数据说明（变量定义）</strong>:
<ul>
<li>变量在使用前必须声明. 声明方式为 <code>类型标识符 变量名列表;</code>.</li>
<li>可以在声明时同时初始化变量.</li>
</ul>
</li>
<li><strong>程序注释</strong>:
<ul>
<li><strong>单行注释</strong>: 使用 <code>//</code>.</li>
<li><strong>多行注释（段注释符）</strong>: 使用 <code>/* ... */</code>.</li>
<li><strong>注意事项</strong>: 多行注释不能嵌套, 否则会导致编译错误.</li>
</ul>
</li>
</ul>
<p><strong>示例代码: 变量定义与注释</strong></p>
<pre><code>#include &lt;stdio.h&gt; // 包含头文件

// 这是一个单行注释
/* 
多行注释:
用于注释跨多行的内容 
*/

const int MAX_LENGTH = 100; // 符号常量声明

int main() 
{
    int myNum = 15;   // 定义并初始化变量 myNum
    float myFloatNum; // 声明变量
    myFloatNum = 5.99; // 初始化变量

    return 0;
}
</code></pre>
<h3>1.3 源程序的书写格式</h3>
<ul>
<li><strong>语句结束符</strong>: 每条语句以分号 <code>;</code> 结尾. 但预处理命令、函数头和右花括号 <code>}</code> 之后不需要加分号 (结构除外).</li>
<li><strong>大小写</strong>: C 语言区分大小写.</li>
<li><strong>书写规范</strong>: 常用锯齿形书写格式. 建议花括号 <code>{}</code> 对齐, 一行写一个语句, 使用 TAB 缩进, 有合适的空行, 以提升程序可读性.</li>
</ul>
<h2>2、数据类型及其运算</h2>
<h3>2.1 C 语言的基本数据类型及定义方法</h3>
<p>C 语言的数据类型可分为基本数据类型和派生 (扩展) 数据类型.</p>
<ul>
<li><strong>基本数据类型</strong>:
<ul>
<li><strong>整型</strong>: <code>int</code>, <code>short</code>, <code>long</code>.
<ul>
<li><code>int</code>: 整型, 4 个字节 (32 位), 范围约为 -2,147,483,648 到 2,147,483,647.</li>
<li><code>short</code>: 短整型, 2 个字节 (16 位), 范围约为 -32,768 到 32,767.</li>
<li><code>long</code>: 长整型, 通常为 4 或 8 字节 (取决于系统和编译器). 范围约为 -2,147,483,648 到 2,147,483,647.</li>
</ul>
</li>
<li><strong>实型（浮点型）</strong>: <code>float</code> (单精度浮点型), <code>double</code> (双精度浮点型), <code>long double</code>.
<ul>
<li><code>float</code>: 4 个字节 (32 位), 精度约为 7 位十进制数.</li>
<li><code>double</code>: 8 个字节 (64 位), 精度约为 15-16 位十进制数.</li>
<li><code>long double</code>: 10、12 或 16 个字节, 具体取决于编译器, 精度更高.</li>
</ul>
</li>
<li><strong>字符型</strong>: <code>char</code>.
<ul>
<li><code>char</code>: 1 个字节 (8 位), 用于存储字符 (如字母、数字、标点符号等).</li>
<li>字符型变量可以直接赋值字符常量 (如 <code>'A'</code>, <code>'1'</code>, <code>'!'</code>), 也可以赋值对应的 ASCII 码值 (如 <code>65</code>, <code>49</code>, <code>33</code>).</li>
</ul>
</li>
<li><strong>布尔型</strong>: <code>_Bool</code> (<code>0</code> 表示 <code>false</code>, <code>1</code> 表示 <code>true</code>).
<ul>
<li><code>_Bool</code> 类型占用 1 个字节 (8 位), 只能存储 <code>0</code> 或 <code>1</code>.</li>
<li>布尔型变量通常用于条件判断和循环控制.</li>
</ul>
</li>
</ul>
</li>
<li><strong>派生/扩展类型</strong>: 数组、指针、枚举 (<code>enum</code>), 结构 (<code>struct</code>), 联合 (<code>union</code>) 等.</li>
<li><strong>字符串</strong>: 不是一种独立的数据类型, 通过字符数组或字符指针来实现. 以 \0 (空字符) 为结束标志.</li>
</ul>
<p><strong>示例代码: 基本数据类型定义</strong></p>
<pre><code>// 定义并初始化整数变量
int myNum = 5;
// 定义并初始化浮点数变量
float myFloatNum = 5.99;
// 定义并初始化字符变量
char myLetter = 'D';
// 定义双精度浮点数
double myDouble = 3.2325467;
// 定义布尔型变量
_Bool myBool = 1;
// 定义并初始化字符串变量
char myString[] = "Hello World!";
// 定义并初始化字符数组变量
char myCharArray[] = {'H', 'e', 'l', 'l', 'o', '\0'};
// 定义并初始化指针变量
char *myPointer = "Hello World!";
</code></pre>
<h3>2.2 C 语言运算符的种类、运算优先级和结合性</h3>
<p>C 语言运算符的种类包括:</p>
<ul>
<li><strong>算术运算符</strong>: <code>+</code> (加), <code>-</code> (减), <code>*</code> (乘), <code>/</code> (除), <code>%</code> (取模), <code>++</code> (增量), <code>--</code> (乘量/减量).</li>
<li><strong>赋值运算符</strong>: <code>=</code> (赋值), <code>+=</code>, <code>-=</code>, <code>*=</code>, <code>/=</code>, <code>%=</code> 等. (a += b → a = a + b)
<ul>
<li><strong>自增运算符前置与后置的区别</strong>:</li>
<li><code>++</code> 和 <code>--</code> 运算符放在变量前面 (如 <code>++i</code>, <code>--i</code>) 称为<strong>前置</strong>, 先进行自增/自减操作, 再使用变量的值</li>
<li><code>++</code> 和 <code>--</code> 运算符放在变量后面 (如 <code>i++</code>, <code>i--</code>) 称为<strong>后置</strong>, 先使用变量的当前值, 再进行自增/自减操作</li>
</ul>
</li>
<li><strong>关系运算符（比较运算符）</strong>: <code>==</code> (等于), <code>!=</code> (不等于), <code>&gt;</code> (大于), <code>&lt;</code> (小于), <code>&gt;=</code> (大于或等于), <code>&lt;=</code> (小于或等于).</li>
<li><strong>逻辑运算符</strong>: <code>&amp;&amp;</code> (逻辑与), <code>||</code> (逻辑或), <code>!</code> (逻辑非). 优先级为 <code>!</code> 优于 <code>&amp;&amp;</code> 优于 <code>||</code>.</li>
<li><strong>条件运算符</strong>: <code>? :</code>, 是 C 语言中唯一的三目运算符.</li>
<li><strong>位运算符</strong>: 双目 <code>&amp;</code> (按位与), <code>|</code> (按位或), <code>^</code> (异或), <code>&lt;&lt;</code> (左移), <code>&gt;&gt;</code> (右移); 单目 <code>~</code> (取反).</li>
<li><strong>指针运算符</strong>: 单目 <code>*</code> (取内容/解引用), <code>&amp;</code> (取地址). 注意: <code>*</code> 也可作乘法 (双目), <code>&amp;</code> 也可作按位与 (双目), 以具体用法为准.</li>
<li><strong>求字节数运算符</strong>: <code>sizeof</code>.</li>
</ul>
<p><strong>示例代码: 重要运算符的使用</strong></p>
<pre><code>// 算术运算符和前置/后置
int a = 5, b = 3;
int sum = a + b;        // 加法

// 前置和后置自增
int x = 10;
int y = ++x;  // x 先自增为 11, 然后赋值给 y, y = 11
int z = x++;  // x 的当前值 11 赋值给 z, 然后 x 自增为 12, z = 11

// 关系运算符
int result1 = (a &gt; b);  // result1 = 1 (true)
int result2 = (a == b); // result2 = 0 (false)

// 逻辑运算符
// 可以对表达式、单个数字或变量进行逻辑运算
int result3 = (a &gt; 0 &amp;&amp; b &lt; 10);  // result3 = 1 (true)
int result4 = (a &lt; 0 || b &gt; 10);  // result4 = 0 (false)
int result5 = !(a == b);          // result5 = 1 (true)
int result6 = !a || b;            // result6 = 1 (true), 因为 !a 为 0, 0 || 3 为真

// 条件运算符
int max = (a &gt; b) ? a : b;  // 条件为真返回 a, max = 5

// 位运算符
int bitwise_and = a &amp; b;   // 按位与: 对应位都为 1 时结果为 1, 否则为 0, 这里 5 &amp; 3 = 1 (二进制: 101 &amp; 011 = 001)
int bitwise_or = a | b;    // 按位或: 对应位有一个为 1 时结果为 1, 否则为 0, 这里 5 | 3 = 7 (二进制: 101 | 011 = 111)
int left_shift = a &lt;&lt; 1;   // 左移 1 位: 相当于乘以 2, 例如 5 &lt;&lt; 1 = 10 (二进制: 101 &lt;&lt; 1 = 1010)

// 指针运算符
int *ptr = &amp;a;  // ptr 是一个指针变量, 它存储了变量 a 的内存地址
int value = *ptr;  // 通过指针获取 a 的值, value = 5

// sizeof 运算符
int size = sizeof(int);  // size = 4
</code></pre>
<h3>2.3 不同类型数据间的转换与运算</h3>
<ul>
<li><strong>自动转换（隐式转换）——考试核心原则</strong>:
<ul>
<li><strong>总原则</strong>: 不同类型数据混合运算时, 系统会自动将其转换为同一类型. 转换方向通常是<strong>向精度更高、范围更大</strong>的类型进行 (即“向高转”), 以减少精度损失.</li>
<li><strong>小整数先升级</strong>: <code>char</code>、<code>short</code> 在运算前会先被<strong>提升为 <code>int</code></strong>.</li>
<li><strong>浮点“吞”整数</strong>: <code>float</code> 或 <code>double</code> 与整数运算时, 整数会被转换为浮点型; 其中 <code>float</code> 参与运算通常会进一步提升为 <code>double</code>.</li>
<li><strong>复杂情况</strong>: 有符号（如 <code>int</code>）与无符号整型（如 <code>unsigned int</code>）混合运算, 转换规则<strong>取决于具体数值和编译器实现</strong>, 结果类型无固定顺序, 需特别注意.</li>
<li><strong>浮点转整型</strong>: 任何浮点型赋值给整型时, 会直接<strong>丢弃小数部分 (向零取整)</strong>.</li>
</ul>
</li>
<li><strong>强制转换（显式转换）</strong>:
<ul>
<li>语法: <code>(类型标识符)表达式</code>.</li>
<li>类型转换是临时性的, 不会改变变量本身的数据类型.</li>
<li>运算过程中, 自动类型提升: 低精度数据类型向高精度数据类型转换.</li>
</ul>
</li>
</ul>
<p><strong>示例代码: 类型转换</strong></p>
<pre><code>int a = 2, b = 5;
double x, y, z;

x = b / a;        // 隐式转换: b/a 先进行整型除法 (2), 然后赋值给 double 型 x, x = 2.0
y = (double)b / a; // 强制转换: 先将 b 转化为 double 型 (5.0), 然后进行浮点数除法, y = 2.5
z = (double)(b / a); // 强制转换: 先计算整型除法 (2), 然后将结果 2 转化为 double 型, z = 2.0
</code></pre>
<h3>2.4 C 语言表达式类型和求值规则</h3>
<ul>
<li><strong>赋值表达式</strong>: 将右侧的值赋给左侧的变量, 例如 <code>x = 5</code>. 赋值号具有结合性, 如 <code>x=y=3</code> 等价于 <code>y=3; x=y;</code>.</li>
<li><strong>算术表达式</strong>: 使用算术运算符连接常量和变量, 例如 <code>sum = x + y</code>.</li>
<li><strong>关系表达式</strong>: 使用关系运算符进行比较, 返回 1 (真) 或 0 (假).</li>
<li><strong>逻辑表达式</strong>: 使用逻辑运算符连接表达式.
<ul>
<li><strong>短路求值</strong>: 对于 <code>&amp;&amp;</code>, 如果左侧为假, 则不再计算右侧; 对于 <code>||</code>, 如果左侧为真, 则不再计算右侧.</li>
</ul>
</li>
<li><strong>条件表达式（三元运算符）</strong>:
<ul>
<li>语法: <code>(条件表达式) ? 表达式1 : 表达式2</code>.</li>
<li>求值规则: 如果条件表达式为真, 返回表达式 1 的值, 否则返回表达式 2 的值.</li>
</ul>
</li>
<li><strong>逗号表达式</strong>:
<ul>
<li>语法: <code>表达式1, 表达式2</code>.</li>
<li>求值规则: 先计算表达式 1, 再计算表达式 2, 并将表达式 2 的值作为整个表达式的结果.</li>
</ul>
</li>
</ul>
<p><strong>示例代码: 条件表达式（三元运算符）</strong></p>
<pre><code>int time = 20;

int main() {
    (time &lt; 18) ? printf("再会!") : printf("晚上好!"); // 输出 -&gt; "晚上好!"
    return 0;
}
</code></pre>
<p><strong>示例代码: 逗号表达式</strong></p>
<pre><code>int a = 2, b;

int main() {
    a = 3 * 5, a + 10; // a=15, 整个表达式结果是 15+10=25, 但后面的 a+10 未赋值, 丢弃
    b = (3 * 5, a + 10); // 使用括号, 3*5=15 (未赋值, 丢弃), 然后 a+10=25, b=25
    printf("a = %d, b = %d\n", a, b);
    return 0;
}
</code></pre>
<h2>3、基本语句</h2>
<h3>3.1 表达式语句, 空语句, 复合语句</h3>
<ul>
<li><strong>表达式语句</strong>: 由表达式 (如赋值表达式、函数调用) 后跟分号组成.</li>
<li><strong>空语句</strong>: 只有分号 <code>;</code>.</li>
<li><strong>复合语句</strong>: 将多个语句用花括号 <code>{}</code> 括起来组成的一个语句. 通常用于 <code>if</code>, <code>for</code>, <code>while</code> 等结构中, 作为循环体或条件体.</li>
</ul>
<h3>3.2 输入输出函数的调用, 正确输入数据并正确设计输出格式</h3>
<ul>
<li><strong>输出函数的调用</strong>: 使用 <code>printf()</code>.
<ul>
<li>语法: <code>printf("格式控制字符串", 输出变量列表);</code>.</li>
</ul>
</li>
<li><strong>输入函数的调用</strong>: 使用 <code>scanf()</code>.
<ul>
<li>语法: <code>scanf("格式控制字符串", 输入变量地址列表);</code>. 注意最后一个参数是变量的地址列表.</li>
</ul>
</li>
<li><strong>格式说明符</strong> (作用): 充当变量输出时的占位符, 用于指定变量的输出格式.
<ul>
<li><code>%d</code> 或 <code>%i</code>: <code>int</code> 整数 (10 进制).</li>
<li><code>%u</code>: 无符号十进制整数</li>
<li><code>%f</code>: <code>float</code> 浮点数 (小数形式).</li>
<li><code>%.2f</code>: 浮点数保留2位小数</li>
<li><code>%lf</code>: <code>double</code> 高精度浮点数据.</li>
<li><code>%c</code>: <code>char</code> 字符.</li>
<li><code>%s</code>: 字符串.</li>
<li><code>%.5s</code>: 字符串最多输出5个字符</li>
</ul>
</li>
<li><strong>输出格式设计（宽度控制）</strong>:
<ul>
<li>使用 <code>%md</code> 或 <code>%-md</code> 控制输出宽度 <code>m</code>.
<ul>
<li><code>%md</code>: 最少占 <code>m</code> 个字符宽度, 默认右对齐.</li>
<li><code>%-md</code>: 最少占 <code>m</code> 个字符宽度, 左对齐.</li>
</ul>
</li>
</ul>
</li>
</ul>
<p><strong>示例代码: 输入输出及格式控制</strong></p>
<pre><code>#include &lt;stdio.h&gt;

int main()
{
    int myNum;
    float f = 5.99;
    
    // 输出
    printf("Number = %d\n", (int)f); // 输出整数, 5
    printf("Value = %.2f\n", f); // 输出浮点数, 保留两位小数, 5.99
    
    // 输出格式控制（左对齐, 宽度为 9）
    int a1 = 20;
    printf("%-9d\n", a1); // 输出 20, 左对齐, 宽度为 9

    // 输入
    printf("请输入一个数字: \n");
    scanf("%d", &amp;myNum); // 获取并保存用户输入的号码, 需要使用 &amp; 引用运算符
    printf("您输入的数字: %d\n", myNum);
    
    return 0;
}
</code></pre>
<h2>4、选择结构程序设计</h2>
<h3>4.1 用 if 语句实现选择结构</h3>
<ul>
<li><strong>if 语句</strong>: 用于根据条件执行代码块.</li>
<li><strong>if...else 语句</strong>: 在 <code>if</code> 条件不满足时执行 <code>else</code> 代码块.</li>
<li><strong>else if 语句</strong>: 用于检查多个条件.</li>
</ul>
<p><strong>示例代码: if-else if-else 结构</strong></p>
<pre><code>int time = 22;
if (time &lt; 10) {
    printf("早上好!");
} else if (time &lt; 20) {
    printf("再会!");
} else {
    printf("晚上好!"); // 输出 -&gt; "晚上好!"
}
</code></pre>
<h3>4.2 用 switch 语句实现多分支选择结构</h3>
<ul>
<li><strong>switch 语句</strong>: 根据表达式的值跳转到匹配的 <code>case</code> 标签.</li>
<li><strong>表达式类型</strong>: <code>switch</code> 表达式可以是整型、字符型和枚举型.</li>
<li><strong><code>break</code> 语句</strong>: 匹配到 <code>case</code> 后执行相应代码, 并通过 <code>break</code> 跳出 <code>switch</code>.</li>
<li><strong><code>default</code> 语句</strong>: 如果没有匹配到任何 <code>case</code>, 则执行 <code>default</code> 语句 (如果存在). <code>default</code> 不是必需的.</li>
<li><strong>贯穿现象</strong>: 如果没有 <code>break</code>, 程序会继续执行下一个 <code>case</code> 的代码. 这种特性被称为“贯穿” (fallthrough), 有时被故意用于让多个 <code>case</code> 共享同一段执行代码.</li>
</ul>
<p><strong>示例代码: switch 结构</strong></p>
<pre><code>int day = 4;
switch (day) {
    case 3:
        printf("周三");
        break;
    case 4:
        printf("周四"); // 匹配到 case 4
        break; // 跳出 switch
    default:
        printf("期待周末");
}
// 输出 -&gt; "周四"
</code></pre>
<h3>4.3 选择结构的嵌套</h3>
<ul>
<li><code>if</code> 语句可以嵌套.</li>
<li>嵌套时, 每一层 <code>if</code> 都要和 <code>else</code> 配套, 若没有 <code>else</code>, 则需要将该层 <code>if</code> 语句用 <code>{}</code> 括起来.</li>
</ul>
<p><strong>示例代码: if 嵌套</strong></p>
<pre><code>int time = 10;
if (time &gt; 8) {
    // 嵌套一个 if
    if (time &lt; 12) {
        printf("中午好!"); // 输出 -&gt; "中午好!"
    }
}
</code></pre>
<h2>5、循环结构程序设计</h2>
<h3>5.1 for 循环结构</h3>
<ul>
<li><code>for</code> 循环结构包含三个表达式: 初始化语句 (表达式 1, 只执行一次), 循环条件表达式 (表达式 2, 每次循环前检查), 调整操作 (表达式 3, 循环体执行后执行).</li>
<li>当条件表达式 (表达式 2) 为假时, <code>for</code> 循环结束.</li>
<li>三个表达式都不是必须的, 但分号不能省略. 如果表达式 2 为空, 则表示无限循环.</li>
</ul>
<p><strong>示例代码: for 循环</strong></p>
<pre><code>int i;
for (i = 0; i &lt; 5; i++) { // i=0 (初始化), i&lt;5 (循环条件), i++ (调整操作)
    printf("%d\n", i);
}
// 输出 -&gt; 0 1 2 3 4

// 省略表达式1（初始化在外部进行）
int i = 0;
for (; i &lt; 5; i++) {
    printf("%d\n", i);
}

// 省略表达式3（调整操作在循环体内进行）
for (i = 0; i &lt; 5;) {
    printf("%d\n", i);
    i++;
}

// 省略表达式2（无限循环）
for (i = 0; ; i++) {
    if (i &gt;= 5) break;  // 需要用break来跳出循环
    printf("%d\n", i);
}

// 三个都省略（必须有办法跳出循环）
for (;;) {
    printf("无限循环");
    break;  // 必须有退出条件
}
</code></pre>
<h3>5.2 while 和 do-while 循环结构</h3>
<table>
<thead>
<tr>
<th>特征</th>
<th><code>while</code> 循环</th>
<th><code>do-while</code> 循环</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>控制类型</strong></td>
<td>入口控制循环 (Entry-Controlled)</td>
<td>出口控制循环 (Exit-Controlled)</td>
</tr>
<tr>
<td><strong>条件检查</strong></td>
<td>在循环体执行<strong>前</strong>检查</td>
<td>在循环体执行<strong>后</strong>检查</td>
</tr>
<tr>
<td><strong>执行保证</strong></td>
<td>可能执行 <strong>0</strong> 次或多次</td>
<td><strong>至少</strong>执行 <strong>1</strong> 次</td>
</tr>
<tr>
<td><strong>语法结尾</strong></td>
<td><code>}</code> 后无分号</td>
<td><code>while(条件)</code> 后<strong>有</strong>分号</td>
</tr>
</tbody>
</table>
<p><strong>示例代码: while 循环</strong></p>
<pre><code>int i = 0;
while (i &lt; 5) {
    printf("%d\n", i); // 初始条件为假时，循环体不执行
    i++;
}
// 输出 -&gt; 0 1 2 3 4
</code></pre>
<p><strong>示例代码: do-while 循环</strong></p>
<pre><code>int i = 0;
do {
    printf("%d\n", i);
    i++;
} while (i &lt; 5); // ← 注意：分号不能省略，这是必须的
// 输出 -&gt; 0 1 2 3 4
</code></pre>
<h3>5.3 continue 语句和 break 语句</h3>
<ul>
<li><strong><code>break</code> 语句</strong>: 跳出当前 <code>循环</code> 或 <code>switch</code> 语句, 执行后续代码.</li>
<li><strong><code>continue</code> 语句</strong>: 跳过当前循环的剩余语句, 直接进入下一次循环.</li>
<li>通常与 <code>if</code> 语句配合使用.</li>
</ul>
<p><strong>示例代码: break 语句</strong></p>
<pre><code>int i;
for (i = 0; i &lt; 10; i++) {
    if (i == 4) {
        break; // 当 i=4 时跳出循环
    }
    printf("%d\n", i);
}
// 输出 -&gt; 0 1 2 3
</code></pre>
<p><strong>示例代码: continue 语句</strong></p>
<pre><code>int i;
for (i = 0; i &lt; 10; i++) {
    if (i == 4) {
        continue; // 当 i=4 时跳过本次循环的剩余部分 (即跳过 printf)
    }
    printf("%d\n", i);
}
// 输出 -&gt; 0 1 2 3 5 6 7 8 9
</code></pre>
<h3>5.4 循环的嵌套</h3>
<ul>
<li>循环可以嵌套.</li>
<li>嵌套循环的核心在于执行顺序, 外部循环每执行一次, 内部循环都要完整地执行完所有轮次.</li>
<li>总的循环体执行次数是: 外循环次数 × 内循环次数.</li>
</ul>
<p><strong>示例代码: 循环嵌套 (遍历二维数组)</strong></p>
<pre><code>int N = 5;
// 假设 A 是一个 N x N 的二维数组, 外部循环控制行, 内部循环控制列 
for(int i = 0; i &lt; N; i++) 
{
    for(int j = 0; j &lt; N; j++) 
    {
    printf("%d ", A[i][j]); // 循环体语句, 访问 A[i][j]
    }
}
</code></pre>
<p><strong>示例代码: 循环嵌套 (九九乘法表)</strong></p>
<pre><code>for (int i = 1; i &lt;= 9; i++) { // 外层循环控制行
    for (int j = 1; j &lt;= i; j++) { // 内层循环控制列
        printf("%d*%d=%2d ", j, i, i*j); // %2d 表示右对齐，占两个宽度
    }
    printf("");
}
</code></pre>
<h2>6、数组的定义和引用</h2>
<h3>6.1 一维数组的定义、初始化和数组元素的引用</h3>
<ul>
<li><strong>定义</strong>: 一维数组的声明格式为 <code>类型标识符 数组名[n]</code>, 其中 <code>n</code> 是一个正整数常量, 表示数组的长度. (声明时指定长度不可省略, 但初始化时可省略长度由编译器自动推断.)</li>
<li><strong>引用</strong>: 通过 <code>数组名[k]</code> 引用数组元素, 下标 <code>k</code> 的合法取值范围是从 <code>0</code> 到 <code>n-1</code>.</li>
<li><strong>初始化</strong>: 可以在声明时同时赋初值.
<ul>
<li>当在定义时对<strong>所有</strong>元素都进行初始化时, 可以省略数组长度 <code>[n]</code>, 编译器会根据初始化列表的元素个数自动确定数组长度.</li>
<li>可以只初始化部分元素, 未被初始化的元素将被自动赋值为 0.</li>
</ul>
</li>
</ul>
<p><strong>示例代码: 一维数组的定义和引用</strong></p>
<pre><code>#include &lt;stdio.h&gt;

int main() {
    // 定义并完全初始化
    int nums1[] = {10, 20, 30, 40};  // 自动推断长度为4
    
    // 定义固定长度数组（部分初始化）
    int nums2[5] = {1, 2};           // [1,2,0,0,0]
    
    // 访问元素
    printf("第一个元素: %d\n", nums1[0]);  // 输出: 10 
    // 修改元素
    nums1[0] = 100;
    printf("修改后: %d\n", nums1[0]);     // 输出: 100
    
    // 遍历数组（需获取长度）
    // 注意：此方法仅适用于函数内定义的局部数组，不适用于函数参数（参数会退化为指针）
    int length = sizeof(nums1) / sizeof(nums1[0]);
    printf("数组长度: %d\n", length);     // 输出: 4
    
    printf("遍历数组:");
    for(int i = 0; i &lt; length; i++) {
        printf(" %d", nums1[i]);         // 输出: 100 20 30 40
    }
    
    return 0;
}
</code></pre>
<h3>6.2 二维数组的定义、初始化和数组元素的引用</h3>
<ul>
<li><strong>定义</strong>: 二维数组声明格式为 <code>类型标识符 变量名[m][n]</code>, 声明一个 $m$ 行 $n$ 列的二维数组.</li>
<li><strong>引用</strong>: 通过 <code>变量名[i][j]</code> 引用元素, 其中 $i$ 为行下标 (0 到 $m-1$), $j$ 为列下标 (0 到 $n-1$).</li>
<li><strong>存储</strong>: 二维数组是按行顺序连续存储的. (一行一行)</li>
<li><strong>初始化</strong>: 可以分组初始化, 也可以省略第一维 (行) 的大小 (但不能省略其他维数, 如列数).</li>
</ul>
<p><strong>示例代码: 二维数组的定义和引用</strong></p>
<pre><code>#include &lt;stdio.h&gt;

int main() {
    // 1. 定义并初始化二维数组（2行3列）
    int matrix[2][3] = { 
    // 2. int matrix2[][3] 省略行数, 自动推断为2行
        {1, 3, 5},   // 第一行
        {2, 6, 10}   // 第二行
    };
    
    // 3. 连续初始化方式
    int matrix3[2][3] = {1, 2, 3, 4, 5, 6}; // 等价于{{1,2,3},{4,5,6}}
    
    // 4. 访问元素：获取第二行第一列的元素（值为2）
    printf("matrix[1][0] = %d\n", matrix[1][0]);  // 输出: 2
    
    // 5. 修改元素：将第一行第二列的元素改为100
    matrix[0][1] = 100;
    printf("修改后: matrix[0][1] = %d\n", matrix[0][1]);  // 输出: 100
    
    // 6. 遍历二维数组
    // 注意：此方法仅适用于局部数组，不适用于函数参数
    int rows = sizeof(matrix) / sizeof(matrix[0]);     // 计算行数
    int cols = sizeof(matrix[0]) / sizeof(matrix[0][0]); // 计算列数
    
    printf("遍历matrix数组:");
    for(int i = 0; i &lt; rows; i++) {
        for(int j = 0; j &lt; cols; j++) {
            printf("%d\t", matrix[i][j]); // 使用制表符对齐
        }
        printf(""); // 每行结束后换行
    }
    /* 输出:
        1       100     5
        2       6       10
    */
    
    return 0;
}
</code></pre>
<h3>6.3 字符串与字符数组</h3>
<ul>
<li><strong>字符串本质</strong>: 在 C 语言中, 字符串是以 <code>\0</code> 结尾的字符数组, 而不是一种单独的数据类型. <code>\0</code> 是字符串结束标志符.</li>
<li><strong>定义</strong>: 可以通过字符数组来表示字符串.</li>
</ul>
<p><strong>示例代码: 字符串定义和引用</strong></p>
<pre><code>// 单引号定义并初始化字符
char c = 'a';
printf("%c\n", c); // 输出 'a'

// 双引号定义并初始化字符串 (会自动添加 \0)
char greetings[] = "Hello World!";
printf("%s\n", greetings);

// 字符数组初始化 (手动添加, 显式包含 \0)
char greetings2[] = {'H', 'e', 'l', 'l', '\0'};
printf("%s\n", greetings2); // 输出 "Hell"

// 访问/修改字符串中的字符
char greetings3[] = "Hello";
printf("%c\n", greetings3[0]); // 访问第一个字符 'H'
greetings3[0] = 'J'; // 修改第一个字符为 'J'
printf("%s\n", greetings3); // 输出 "Jello"
</code></pre>
<h3>6.4 使用一维数组解决批量数据查找或排序等问题</h3>
<ul>
<li>
<p>一维数组常用于批量数据的查找和排序</p>
</li>
<li>
<p>若数组无序, 采用顺序查找 ( 从头到尾依次比较 )</p>
</li>
<li>
<p>若数组有序, 采用折半查找 ( 二分查找 ), 效率更高</p>
<p><strong>示例代码: 顺序查找</strong></p>
<pre><code>// a 为数组, n 为长度, num 为待查找元素
int found = 0; // 标志变量, 是否找到
for (int i = 0; i &lt; n; i++) { // 从头到尾遍历数组
    if (a[i] == num) { // 如果找到
        found = 1;
        break; // 跳出循环
    }
}
// found==1 表示找到, 否则未找到
</code></pre>
<p><strong>示例代码: 折半查找 ( 数组有序 )</strong></p>
<pre><code>// a 为有序数组, n 为长度, num 为待查找元素
int low = 0, high = n - 1, found = 0; // 初始化区间和标志
while (low &lt;= high) { // 只要区间不为空
    int mid = (low + high) / 2; // 取中间下标
    if (a[mid] == num) { // 找到
        found = 1;
        break;
    }
    else if (num &gt; a[mid])
        low = mid + 1; // 在右半区间查找
    else
        high = mid - 1; // 在左半区间查找
}
// found==1 表示找到, 否则未找到
</code></pre>
</li>
<li>
<p>排序常用选择排序、冒泡排序、插入排序, 都是基础算法, 适合小规模数据</p>
</li>
<li>
<p>三者核心思想都是通过循环比较和交换, 逐步将数组变为有序</p>
<p><strong>示例代码: 选择排序:</strong></p>
<pre><code>// a 为数组, n 为长度
for (int i = 0; i &lt; n - 1; i++) { // 外层循环, 控制趟数
    for (int j = i + 1; j &lt; n; j++) { // 内层循环, 找最小值
        if (a[j] &lt; a[i]) { // 如果有更小的值
            int t = a[j]; a[j] = a[i]; a[i] = t; // 交换元素
        }
    }
}
// 排序后数组 a 升序排列
</code></pre>
<p><strong>示例代码: 冒泡排序:</strong></p>
<pre><code>// a 为数组, n 为长度
for (int i = 0; i &lt; n - 1; i++) { // 外层循环, 控制趟数
    for (int j = 0; j &lt; n - 1 - i; j++) { // 内层循环, 相邻元素比较
        if (a[j] &gt; a[j + 1]) { // 如果前面的比后面大
            int t = a[j]; a[j] = a[j + 1]; a[j + 1] = t; // 交换元素
        }
    }
}
// 排序后数组 a 升序排列
</code></pre>
<p><strong>示例代码: 插入排序:</strong></p>
<pre><code>// a 为数组, n 为长度
for (int i = 1; i &lt; n; i++) { // 从第二个元素开始
    int k = a[i]; // 待插入元素
    int j = i - 1; // 比较的元素下标
    while (j &gt;= 0 &amp;&amp; k &lt; a[j]) { // 向前查找插入位置
        a[j + 1] = a[j]; // 后移
        j--; // 继续向前比较
    }
    a[j + 1] = k; // 插入到合适位置
}
// 排序后数组 a 升序排列
</code></pre>
</li>
</ul>
<h2>7、函数定义与调用</h2>
<h3>7.1 库函数的正确调用</h3>
<ul>
<li>C 语言的系统库中提供了几百个函数可供程序员直接使用.</li>
<li>使用库函数时, 要包含相应的头文件. 例如:
<ul>
<li><code>&lt;stdio.h&gt;</code>: <code>printf()</code> (格式化输出), <code>scanf()</code> (格式化输入) .</li>
<li><code>&lt;math.h&gt;</code>: <code>sqrt()</code> (平方根), <code>pow()</code> (幂运算), <code>fabs()</code> (绝对值) .</li>
<li><code>&lt;stdlib.h&gt;</code>: <code>rand()</code> (伪随机整数), <code>srand()</code> (设置种子) .</li>
<li><code>&lt;string.h&gt;</code>: <code>strlen()</code> (字符串长度), <code>strcpy()</code> (字符串复制) .</li>
<li><code>&lt;time.h&gt;</code>: <code>time()</code> (获取当前时间), <code>clock()</code> (获取处理器时间) .</li>
</ul>
</li>
</ul>
<p><strong>示例代码: 调用库函数</strong></p>
<pre><code>#include &lt;stdio.h&gt;
#include &lt;math.h&gt; // 需要包含 math.h 头文件

int main()
{
    // 调用库函数 sqrt (平方根)
    printf("%f", sqrt(16)); // 输出 4.000000
    return 0;
}
</code></pre>
<h3>7.2 用户自定义函数的定义、类型与返回值</h3>
<ul>
<li><strong>函数定义</strong>: 由函数头和函数体组成.
<ul>
<li><strong>函数头</strong>: <code>类型标识符 函数名(形式参数列表)</code>.</li>
<li><strong>函数体</strong>: 包含要执行的代码.</li>
</ul>
</li>
<li><strong>函数类型与返回值</strong>:
<ul>
<li>函数的类型 (即返回值的类型) 由函数头前的“类型标识符”指明.</li>
<li>若没有返回值, 则使用 <code>void</code>.</li>
<li>函数返回值通过 <code>return</code> 语句给出. 若函数无返回值, 可以不写 <code>return</code>.</li>
</ul>
</li>
</ul>
<p><strong>示例代码: 自定义函数定义与返回值</strong></p>
<pre><code>// 函数定义: 返回 int 类型, 接受两个 int 参数
int myFunction(int x, int y) 
{
    return x + y; // 返回 x 和 y 的和
}

// 无返回值函数定义 (使用 void)
void greetUser(char name[]) 
{
    printf("Hello %s\n", name);
}
</code></pre>
<h3>7.3 函数的正确调用、参数的值传递</h3>
<ul>
<li><strong>函数调用</strong>: 语法为 <code>函数名(实际参数列表)</code>.</li>
<li><strong>形式参数 (形参)</strong>: 在函数定义时列出的参数, 它们本质上是局部变量, 仅在函数体内有效. 例如 <code>void fun(int k)</code> 中的 <code>k</code>.</li>
<li><strong>实际参数 (实参)</strong>: 在函数调用时传入的值或表达式, 例如 <code>fun(i)</code> 中的 <code>i</code>, 实参可以是局部变量、全局变量、常量/表达式.</li>
<li><strong>值传递</strong>: 将实参的值传递给对应的形参.
<ul>
<li>形参是局部变量, 在函数被调用时分配存储单元, 调用结束即被释放.</li>
<li>对形参的任何改变都不会对实参产生任何影响 (单向传递).</li>
</ul>
</li>
<li><strong>地址传递（数组）</strong>: 将整个数组传递给被调函数时, 传递的是数组的首地址.
<ul>
<li>在函数中对形参数组的任何修改都会影响到实参数组.</li>
</ul>
</li>
</ul>
<p><strong>示例代码: 形参 (局部) 与实参的来源</strong></p>
<pre><code>// fun 的 k 是形参（局部变量）
void fun(int k) {
    printf("fun received %d\n", k);
}

int main() {
    int i = 5; // main 内部的局部变量
    fun(i);    // i 作为实参传入
    fun(10);   // 常量 10 作为实参传入
    return 0;
}
</code></pre>
<p><strong>示例代码: 函数调用与值传递</strong></p>
<pre><code>// 假设 myFunction 已定义为 int myFunction(int x, int y) { return x + y; }
int main() 
{
    // 调用函数并将结果打印
    printf("结果: %d", myFunction(5, 3)); // 传递 5 和 3 的值作为实参

    int result = myFunction(5, 3); // 将结果存储在变量中

    return 0;
}
</code></pre>
<h3>7.4 函数的嵌套调用、递归调用</h3>
<ul>
<li><strong>嵌套调用</strong>: 函数可以嵌套调用, 但不能嵌套定义.</li>
<li><strong>递归调用</strong>: 函数可以直接或间接调用自己.</li>
</ul>
<p><strong>示例代码: 嵌套调用（简单加法）</strong></p>
<pre><code>#include &lt;stdio.h&gt;

int add(int a, int b);

int main() 
{
    printf("结果: %d", add(add(1, 2), 3)); // 嵌套调用 add 函数
    return 0;
}

int add(int a, int b) 
{
    return a + b;
}
</code></pre>
<p><strong>示例代码: 递归调用（简单递归求和）</strong></p>
<pre><code>#include &lt;stdio.h&gt;

int sum(int n);

int main() 
{
    printf("结果: %d", sum(5)); // 计算 1+2+3+4+5
    return 0;
}

int sum(int n) 
{
    if (n == 0) {
        return 0; // 基础情况
    } else {
        return n + sum(n - 1); // 递归调用自身
    }
}
</code></pre>
<h3>7.5 局部变量与全局变量</h3>
<ul>
<li><strong>局部变量</strong>: 在函数 (如形参和函数中定义的变量) 或语句块 (如循环体内定义的变量) 内有效 (可见).</li>
<li><strong>全局变量</strong>: 在所有函数外定义, 其作用域为整个程序; 在它后面定义的函数中均可使用.</li>
<li><strong>同名变量的屏蔽</strong>: 若局部变量与全局变量同名, 则优先使用局部变量 (屏蔽全局变量) .</li>
<li><strong>外部变量声明</strong>: 若要在其他文件中使用未定义的全局变量, 则需声明其为外部变量, 使用 <code>extern 数据类型名 变量名</code>.</li>
</ul>
<p><strong>示例代码: 局部变量与全局变量</strong></p>
<pre><code>#include &lt;stdio.h&gt;

int k_global = 2; // 全局变量

int main() 
{
    int i = 5; // 局部变量
    int x; 
    x = i + k_global; 
    printf("x=%d\n", x); // 使用全局变量 k_global, x = 7

    {
        int k_global = 16; // 语句块内的局部变量 (与全局变量同名)
        x = i + k_global;
        printf("x=%d\n", x); // 优先使用局部变量, x = 5 + 16 = 21
    }
    
    x = i + k_global;
    printf("x=%d\n", x); // 语句块结束后, 恢复使用全局变量 k_global, x = 7

    return 0;
}
</code></pre>
<p><strong>示例代码: 外部变量声明</strong></p>
<pre><code>// 示例：
// file1
int globalVar = 10; // 定义全局变量

// file2
#include &lt;stdio.h&gt;

extern int globalVar; // 声明外部变量
void printGlobalVar() {
    printf("%d\n", globalVar);
}
</code></pre>
]]>
    </content>
  </entry>
  <entry>
    <id>https://blog.chuwu.top/posts/2018-10-30/charlotte-markdown/</id>
    <updated>2018-10-30T00:00:00.000Z</updated>
    <title>Charlotte-Markdown</title>
    <author>
      <name>ChuwuYo</name>
    </author>
    <link href="https://blog.chuwu.top/posts/2018-10-30/charlotte-markdown/" rel="alternate"/>
    <published>2018-10-30T00:00:00.000Z</published>
    <summary>Charlotte-Markdown 是 Charlotte 主题的 Markdown 语法说明文档</summary>
    <content type="html" xml:base="https://blog.chuwu.top/posts/2018-10-30/charlotte-markdown/">
      <![CDATA[<h1>Charlotte-Markdown 使用说明</h1>
<blockquote>
<p>本文直接参考<a href="https://github.com/saicaca/fuwari">Fuwari</a>的相关指南</p>
</blockquote>
<p><s>其实是为了防止未来的自己忘了该怎么写Post</s></p>
<p>封面： <a href="https://www.pixiv.net/artworks/133535742">Collector</a> by <a href="https://www.pixiv.net/users/2642047">Ashima</a></p>
<h1>创建文章</h1>
<h2>文章元数据</h2>
<pre><code>---
title: Charlotte-Markdown
published: 2018-10-30
description: 'Charlotte-Markdown 是 Charlotte 主题的 Markdown 语法说明文档'
image: './Markdown.webp'
tags: [Charlotte, 公告, Markdown]
category: '公告'
draft: false 
lang: ''
---
</code></pre>
<table>
<thead>
<tr>
<th>属性名</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>title</code></td>
<td>文章标题</td>
</tr>
<tr>
<td><code>published</code></td>
<td>文章发表时间</td>
</tr>
<tr>
<td><code>description</code></td>
<td>文章简介。 会显示在主页上</td>
</tr>
<tr>
<td><code>image</code></td>
<td>文章封面图   1. 以 <code>http://</code> or <code>https://</code>开头: 使用网络图片   2. 以 <code>/</code>: 开头来使用 <code>public</code> 中的图片   3. 没有任何前缀：使用相对于Markdown文件的图片</td>
</tr>
<tr>
<td><code>tags</code></td>
<td>文章的标签</td>
</tr>
<tr>
<td><code>category</code></td>
<td>文章的分类</td>
</tr>
<tr>
<td><code>draft</code></td>
<td><code>true</code>或者<code>false</code>。是否为草稿。如果为<code>true</code>，则不会显示在主页上。</td>
</tr>
</tbody>
</table>
<h2>发布文章的存放位置</h2>
<p>你发布的文章应当在 <code>src/content/posts/</code> 目录下. 你也可以创建一个子目录，用来存放文章和文章中的资源。</p>
<pre><code>src/content/posts/
├── post-1.md
└── post-2/
    ├── cover.png
    └── index.md
</code></pre>
<h1>Markdown 语法</h1>
<h1>这是一个 h1 标题</h1>
<h2>这是一个 h2 标题</h2>
<h3>这是一个 h3 标题</h3>
<p>段落之间用空行分隔。（或双空格+换行进行内部换行）</p>
<p>这是第二段 <em>斜体</em>、<strong>粗体</strong>和 <code>代码行</code>。</p>
<h1>无序列表</h1>
<ul>
<li>这是一项</li>
<li>这是一项</li>
<li>这是另一项</li>
</ul>
<h1>有序列表</h1>
<ol>
<li>第一项</li>
<li>第二项</li>
<li>第三项</li>
</ol>
<h1>嵌套列表</h1>
<ol>
<li>
<p>首先有这两个对象</p>
<ul>
<li>你</li>
<li>我</li>
</ul>
</li>
<li>
<p>一定要记住</p>
</li>
<li>
<p>不能忘</p>
<p><code>找到我</code></p>
<p>说晚安</p>
</li>
</ol>
<h1>链接和脚注</h1>
<p>这里有一个链接到 <a href="http://bilibili.com">一个网站</a>、<a href="example.md">本地文档</a> 和 <a href="#%E8%BF%99%E6%98%AF%E4%B8%80%E4%B8%AA-h3-%E6%A0%87%E9%A2%98">当前文档中的某个章节标题</a>。这里有一个脚注<a href="%E8%BF%99%E6%98%AF%E8%84%9A%E6%B3%A8%E7%9A%84%E7%A4%BA%E4%BE%8B%E5%86%85%E5%AE%B9%EF%BC%8C%E7%82%B9%E5%87%BB%E8%84%9A%E6%B3%A8%E5%BC%95%E7%94%A8%E4%BC%9A%E8%B7%B3%E8%BD%AC%E5%88%B0%E8%BF%99%E9%87%8C%E3%80%82%EF%BC%88%E6%98%BE%E7%A4%BA%E5%9C%A8Post%E6%9C%80%E5%90%8E%EF%BC%89">^1</a>。</p>
<h1>图片引用</h1>
<h2>Markdown 语法引入图片</h2>
<p>示例：</p>
<pre><code>![头像图片](/favicon/icon_256.webp "我的头像")
</code></pre>
<p><img src="/favicon/icon_256.webp" alt="头像图片" title="我的头像" /></p>
<h2>HTML 标签引入图片</h2>
<p>示例：</p>
<pre><code>&lt;img src="/favicon/icon_256.webp" alt="头像图片" width="256" height="256" /&gt;
</code></pre>
<p>&lt;img src="/favicon/icon_256.webp" alt="头像图片" width="256" height="256" /&gt;</p>
<h2>路径说明</h2>
<p>图片路径有以下几种方式：</p>
<ol>
<li>
<p><strong>网络图片</strong>：以 <code>http://</code> 或 <code>https://</code> 开头</p>
<pre><code>![网络图片](https://example.com/image.jpg)
</code></pre>
</li>
<li>
<p><strong>公共目录图片</strong>：以 <code>/</code> 开头，相对于 <code>public</code> 目录</p>
<pre><code>![公共图片](/favicon/icon_256.webp)
</code></pre>
</li>
<li>
<p><strong>相对路径图片</strong>：相对于当前 Markdown 文件的位置</p>
<pre><code>![相对图片](./images/photo.jpg)
</code></pre>
</li>
</ol>
<h1>数学公式</h1>
<p>行内数学公式是这样写的：$\omega = d\phi / dt$。</p>
<p>显示数学公式应该独占一行，用双美元符号包裹：</p>
<p>$$I = \int \rho R^{2} dV$$</p>
<p>$$
\begin{equation*}
\pi
=3.1415926535
;8979323846;2643383279;5028841971;\ldots
\end{equation*}
$$</p>
<h1>GitHub 仓库卡片</h1>
<p>你可以添加一个链接到一个 GitHub 仓库的动态卡片。当页面加载时，将从 GitHub API 获取仓库信息。</p>
<p>::github{repo="ChuwuYo/Charlotte"}</p>
<p>创建一个 Github 仓库卡片，只需在 Markdown 文件中添加一行 <code>::github{repo="&lt;owner&gt;/&lt;repo&gt;"}</code>。</p>
<pre><code>::github{repo="ChuwuYo/Charlotte"}
</code></pre>
<h1>警告</h1>
<p>支持这些类型的警告: <code>note</code> <code>tip</code> <code>important</code> <code>warning</code> <code>caution</code></p>
<p>:::note
突出显示用户在浏览时也应考虑的信息。
:::</p>
<p>:::tip
可选的信息，以帮助用户更顺利地理解。
:::</p>
<p>:::important
用户完成任务所必需的关键信息。
:::</p>
<p>:::warning
关键内容，由于潜在风险需要用户立即关注。
:::</p>
<p>:::caution
行动的负面潜在后果。
:::</p>
<h2>基本语法</h2>
<pre><code>:::note
突出显示用户在浏览时也应考虑的信息。
:::

:::tip
可选的信息，以帮助用户更顺利地理解。
:::
</code></pre>
<h2>自定义标题</h2>
<p>警告的标题可以自定义。</p>
<p>:::note[自定义标题]
这是一个带有自定义标题的提示。
:::</p>
<pre><code>:::note[自定义标题]
这是一个带有自定义标题的提示。
:::
</code></pre>
<h2>GitHub 语法</h2>
<blockquote>
<p>[!TIP]
同时也支持 <a href="https://github.com/orgs/community/discussions/16925">GitHub 语法</a>。</p>
</blockquote>
<pre><code>&gt; [!NOTE]
&gt; 也支持 GitHub 语法。

&gt; [!TIP]
&gt; 也支持 GitHub 语法。
</code></pre>
<h1>剧透内容</h1>
<p>你可以为文本添加剧透。文本也支持 <strong>Markdown</strong> 语法。</p>
<p>内容 :spoiler[被隐藏起来了 <strong>哈哈</strong>]！</p>
<pre><code>内容 :spoiler[被隐藏起来了 **哈哈**]！
</code></pre>
<h1>视频卡片</h1>
<p>只需从 YouTube 或其他平台复制嵌入代码，然后将其粘贴到 markdown 文件中。</p>
<h3>YouTube</h3>
<p>只需从 YouTube 复制嵌入代码，然后粘贴到 Markdown 文件中即可。</p>
<p>&lt;iframe width="560" height="315" src="https://www.youtube.com/embed/1MsuXjTlCGE?si=KykaK5Bgvq1W1hXI" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen&gt;&lt;/iframe&gt;</p>
<h1>杂项</h1>
<h2>特殊符号</h2>
<ul>
<li><strong>长破折号</strong>：使用三个破折号 <code>---</code> 表示长破折号（——）</li>
<li><strong>范围符号</strong>：使用两个破折号 <code>--</code> 表示范围（例如，"第 12—14 章"）</li>
<li><strong>省略号</strong>：三个点 <code>...</code> 将被转换为省略号（……）</li>
</ul>
<h2>Unicode 支持</h2>
<p>支持 Unicode 字符，例如表情符号：☺ ★ ♥ ♦ ♣ ♠ ● ○ ◇ ◆ □ ■ △ ▲ ※ → ← ↑ ↓</p>
<h2>转义字符</h2>
<p>如果需要显示特殊字符的原样，可以使用反斜杠 <code>\</code> 进行转义：</p>
<ul>
<li><code>\_</code> 显示为 <code>_</code>（而不是斜体）</li>
<li><code>\*</code> 显示为 <code>*</code>（而不是粗体）</li>
<li><code>\`` 显示为 `` </code>（而不是代码）</li>
</ul>
<h2>缩进</h2>
<p>缩进始终保持为四个空格的距离，你也可以使用制表符（Tab）。</p>
<h2>行块</h2>
<p>| 第一行
| 第二行
| 第三行</p>
]]>
    </content>
  </entry>
</feed>
