Go 05:defer 何时关闭任务输入
04 的顺序批量入口统计了直接传入的文本。现在允许 --file path 读取一次任务输入:文件打开成功后,读取失败、统计超限和正常返回分别由谁负责关闭文件?如果把 defer file.Close() 写在批量循环内部,为什么进程最终关闭文件,循环期间仍可能占着大量描述符? 同一累计工程本章保留 --text 与 --batch,新增互斥的 --file。文件只作为一条任务的文本来源;读完和关闭以后,才进入原有的字段、字节和码点统计。它不是长期打开的后台任务,也没有把文件放进并发队列。本章完整工程和证据包可在不携带仓库根目录 examples/ 的站点下载,校验值见SHA256SUMS。 独立复跑步骤列出本章正反例、源码校验及运行命令。 先确定文件属于哪个函数 入口检查三个选项恰好选中一个,然后调用 readInputFile(path)。这个函数完成 os.Open 后立即把得到的 *os.File 交给 readAndClose(input io.ReadCloser): 123456789func readAndClose(input io.ReadCloser) (t...
Go 03:byte、rune 和字段数怎样区分
02 的 CLI 把 Go 写 Go 计为 3 个“词”。如果还要报告文本大小,是否能直接把这个 3 用作长度?不能。三个用空白分隔的字段、UTF-8 编码占用的字节、字符串解码得到的码点,回答的是三种不同问题。中文输入让混用立即露馅:本次实验中它们分别是 3、9、7。 本篇只给同一个任务工程的统计结果加两列,不更换 CLI,也不把“码点数”改名叫“字符数”。完整累计工程与原始证据保留到 03 的源码、RUN/VERSIONS 和 00–03 实验;前章独立快照仍可复跑历史输出。 文本如何经过三把不同的尺子 本版 stats.Analyze 返回三个带名字的整数: 12345678910111213type Counts struct { Words int Bytes int Runes int}func Analyze(text string) Counts { return Counts{ Words: CountWords(text), Bytes: len(text), ...
Go 04:循环变量为什么会影响批量任务
把 03 的单次文本统计改为顺序批量执行后,for 循环已经出现。假如为了延迟读取任务标签,先在循环里保存几个函数,等循环结束再调用:它们会各自读到对应标签,还是都读到最后一个?Go 1.27.1 下,答案取决于 range 后面写的是声明 :=,还是向预先存在的变量赋值 =。无需启动 goroutine,这个差异就能稳定复现。 本篇仍沿用系列唯一累计工程。--batch 'one two|Go 写 Go' 暂时把 | 用作教学输入分隔符,按顺序统计两项;--text 原入口保留。此时没有 worker、排队、任务 ID 或持久化。本篇源码与证据归档包含截至 04 的完整工程与各章原始实验,旧章的历史输出不随新代码改写。 批量输入怎样保持可判断的结果 两个 flag 只能选其一。代码用 FlagSet.Visit 判断调用者实际提供了哪些选项,而不只看 --batch 对应字符串是不是空:显式传 --batch '' 与根本没传是两种输入,都不能变成一次看似成功的单任务。选择批量模式后,以 strings.Split(*batch, "|") 切出任务,在一个...
Python 21:任务状态与结果如何一起落盘
一次任务已在数据库里改成 running,写入结果之前人为抛出错误。重新打开同一个 SQLite 文件查询,状态仍为 queued,结果仍为 null;随后正常运行,另一个 Python 进程查询到 succeeded 和 {"one": 2, "two": 1}。这不是断言“错误从没发生”,而是事务撤销了未完成的状态变更。状态与结果若分开提交,查询者就可能看见成功却没有结果,或旧状态配上新结果。 20 的版本化 JSON为任务字段提供了校验入口;本篇第一次把状态、结果和错误放进 SQLite 的实际记录。单次进程内词频运算仍委托给相同的 app.run,没有复制一套计算规则。 先确定读写边界 tasklab.database.SQLiteTaskGateway 提供提交、执行、查询和取消四个方法。提交用既有 app.submit 验证文本和任务种类,在 SQLite 表内插入 queued 记录,由数据库分配连续 ID;task-0001 是持久化表中的第一条任务,不再是每次启动都从 1 开始的进程内序号。查询读回标...
Go 02:零值和作用域怎样改变任务配置
01 把统计函数移出了 main 包,但任何非空文本都可以被统计。现在给任务增加“最多允许几个字段”的配置:默认允许 3 个,调用者也能通过 --max-words 指定上限。一个易错的写法是把“没有写上限”和“上限等于零”混为一谈;另一个是以为代码在内部作用域给同名变量重新赋值,实际上创建了新变量。两类问题都会让程序执行与调用者预期不符。 这仍是同一个顺序 CLI 的递进版本,没有任务队列或数据库。02 累计工程与证据包含此前工程、当前配置实现和 00–02 原始实验输出。00 和 01 的原代码也各自在当章素材包中保留,不用最新的实现覆盖历史结果。 零值不能暗中充当默认配置 Config 目前只含一个整数: 123type Config struct { MaxWords int} Go 的整型字段未经赋值时是 0,因此 Config{} 的 MaxWords 是 0。这个值在本任务里不是“无限制”,也不是“自动用 3”:0 表示无效配置。validateConfig(Config{}) 返回 max-words must be positive;C...
Go 01:package、module 与依赖分别解决什么
00 中的计数函数与 main 同属一个包。把函数移到 stats/ 后,CLI 的输入和输出没有变,却必须回答三个不同的问题:编译器该把哪几份源码合成一个包,main 用什么路径找到计数函数,以及从新目录取到工程时还需要哪些外部依赖。把 package、module 和依赖混成一个词,会误以为“多一个目录就得下载一个模块”。 这次变更只移动原有函数和对应测试,不引入接口或第二套服务。整个系列仍只有 examples/go-language/ 一个累计工程;本章素材包保存此时完整的工程与 00、01 的原始证据,00 的原版另有独立快照。系列导航列明各篇验收状态。 一个目录改变了什么 count.go 从工程根目录移到 stats/count.go,包声明由 package main 改为 package stats,调用方改用该包导出的函数: 1234567package statsimport "strings"func CountWords(text string) int { return len(strings.Fields(text...
Python 20:JSON 保存了对象的哪些部分
词频任务 "一 二 一" 的执行结果是 {"一": 2, "二": 1}。将任务快照写成 JSON,再解析回来,任务 ID、文本、标签、状态、结果和错误字段保持一致;但原来的 Task 类实例、property、方法与可变对象身份并不在 JSON 中。把“能把字典还原”误当作“复原了整个运行进程”,会让后续持久化设计建立在错误假设上。 上一章已经把模块导入与实际运行分开。本章仅在相同 tasklab 的查询快照外建立版本 1 的交换格式,不更换计算规则,也不宣称数据库或跨进程查询已实现。 交换格式是明示的字段合同 examples/python-language/src/tasklab/serialization.py 接受 task_id 与 query(store, task_id) 的快照,编码为含 version、id、kind、text、metadata、state、result、error 的 JSON 对象。version 必须恰为整数 1,布尔值 true 不等于合格的版本号;未知版...
Python 19:import 为什么会执行代码
两个文件各自从对方导入一个尚未定义的名称,python -c 'import alpha' 报 cannot import name 'ready' from partially initialized module 'alpha'。import 不是“把文件内容粘贴到这里”,它要执行模块顶层代码;执行到一半就请求另一个模块,另一个模块再回头读取尚未绑定的名称,就得到这种失败。 第18 篇的任务接口已经存在。本篇把内存适配器放进 tasklab.storage,把接口声明留在 tasklab.typed,让 CLI、业务和存储职责可分开阅读;这不是 SQLite 存储,每次启动仍创建独立内存任务。数据库以及跨进程查询在 21 验证。 包初始化也属于执行路径 在源码目录用 PYTHONPATH=src /usr/bin/python3.12 -m tasklab --text 'One two one' 启动,解释器先找到 tasklab 包,执行 __init__.py,再执行 __main__.py 调用 cli.main()。同一个进程里,已经加载的模块记录在 sys.mo...
Go 00:从一个任务看懂 Go 工程
一段程序打印了 words=3,能说明什么?它至少可能来自三种不同路径:源码被解释为包后构建成可执行文件,再由操作系统启动;go run 帮忙完成了构建和启动;或者某个测试直接调用了统计函数。三条路径的输入、失败出口、留下的证据都不相同。如果连“函数返回错误”和“进程以什么状态退出”都没有分开,后续任务取消、HTTP 应答和数据库提交就容易被混成一件事。 系列将逐步构建同一个文本统计任务服务。当前只有同步 CLI:接收一段文本、计算字段数、输出结果。暂时没有任务 ID、队列、goroutine 或持久化;它们到各自负责的章节才加入。系列导航列出篇序和未验收项目。 先约定输入和成功条件 输入通过 --text 传给程序。这里的“词”有一个刻意收窄的定义:strings.Fields 按 Unicode 空白把字符串切成非空字段,再取字段数量。one two 是两个字段,Go 写 Go 是三个字段。没有空白的 Go语言 只有一个字段;这个数字不代表自然语言分词,更不是字节数、码点数或屏幕上的字符数。不同长度单位留到 03 验证。 一条成功命令必须同时满足:标准输出恰有 words=数...
Python 18:类型提示能发现什么错误
在源码里写 service.submit("word_count", 42),Pyright 1.1.404 指出第二个参数是整数,预期是字符串;把相同数字藏在 json.loads 的返回值中,静态检查却没有报错,真正执行时由原来的 submit 抛出 TypeError: text must be a string。不能把“类型检查通过”写成“输入已经安全”。两次判断发生在不同的边界。 第17 篇的 CLI对解码和输入长度的检查仍保留。本篇加一层类型明确的任务接口,并不重写任务引擎或重新实现校验。示例仍使用 tasklab 的进程内 store,提交、执行、查询、取消遵守原状态契约。 接口是可检查的承诺,不是运行时门卫 examples/python-language/src/tasklab/typed.py 定义 TaskGateway(Protocol),列出四个操作及其参数和返回值;MemoryTaskGateway 把这些操作委托给已经存在的 app。变量标为 TaskGateway 时,检查器只需看方法的形状,不必让任务对象继承这个类。这是 P...










