Python 07:分支和推导式怎样保留业务含义
一批三个任务都成功,结果依次是 {}、{} 和 {"word": 1}。写成 [result for result in results if result],报告里却只剩最后一项。任务并未失败,也不是推导式少遍历了两次;两个空字典是假值,被错误的筛选条件排除了。 02 已经用空文本证明空词频是合法成功结果。现在 tasklab 再收束提交、执行、查询、取消四种操作到一个分派入口,明确未知操作和无效状态的去向,而不是让布尔表达式悄悄代替业务规则。 空结果不能等于未完成 123results = [{}, {}, {"word": 1}]wrong = [result for result in results if result]correct = [result for result in results if result is not None] 实测 wrong 只有 {"word"...
Python 06:闭包记住的是哪一次值
为三个任务分别准备 red、blue、green 后缀,调用三个回调时却都得到 "hello green"。循环没有跳过前两项,任务也没有覆盖已经成功的结果;三个函数在调用时查找的是同一个已经走到末尾的循环变量。 05 的可变默认参数实验区分了“函数定义时得到什么”和“函数调用时再查找什么”。本章沿用同一个 tasklab,由可信的本地回调先转换合成文本,再以原来的 submit → run → query 路径执行词频;任务输入不能携带代码或模块名。 三个函数,一次晚到的名称查找 1234def late_callbacks(suffixes): return [lambda text: f"{text} {suffix}" for suffix in suffixes]callbacks = late_callbacks(["red", "blue", "green"]) 推导时要画时间轴,而不是把 lambda 视作立即运行。循...
Python 05:函数签名如何表达调用约束
两个任务的标签本应独立:第一次提交 "first",第二次提交 "second"。把默认参数写成 labels=[],第二个任务却得到 ["first", "second"]。03 明明已经在 submit 里复制了列表,为什么仍会串进前次的标签?因为污染发生在调用 submit 之前:包装函数两次使用了同一份默认列表。 tasklab 的公开入口仍是 submit(store, kind, text, *, metadata=None)。本章不重写任务应用,而在它前面增加一层故意写错和一层修好的提交函数,比较同一业务动作的输入。 默认值不是每次调用时重新求值 123def unsafe_submit(store, label, labels=[]): labels.append(label) return submit(store, "word_count", label, metadata={"labels": labels}...
Python 04:字符串怎样变成可靠输入
"你好 世界" 是五个 Python 字符,却编码成十三个 UTF-8 字节。如果限制的是接收端的字节数,却用 len(text) 检查,边界会放错位置;如果把错误字节悄悄替换成别的字符,词频结果即使“成功”,也不再对应原始输入。 tasklab 现在增加 submit_bytes,接收生成的 UTF-8 数据,仍转入同一个 submit → run → query 业务路径。前一章处理任务元数据的复制边界;这里处理数据进入应用前的编码与大小边界。 字节数不是字符数 Python 的 str 是文本序列,bytes 是字节序列;二者在应用边界处需要明确转换。官方 Unicode HOWTO解释了编码、解码以及错误策略。本实验固定 UTF-8;不会从一串任意字节“猜”原本的编码。 对实际输入 "你好 世界",len(text) 为 5,len(text.encode("utf-8")) 为 13。submit_bytes 先要求输入是 bytes,再检查原始字节长度是否超过 1024,随后调用 raw_bytes.de...
Python 03:一次复制为何还会串改
把任务元数据 {"labels": ["short"]} 复制一份,再向复制品里的 labels 追加 "shared"。如果两份元数据都变了,问题不在词频计算,而在复制发生的层级:字典的外壳换了,里面那份列表没有换。01 的名称与对象引用图在这里多出一层;只看 metadata is not shallow 会漏掉真正的共享点。 前两章已固定 输入的名称绑定和空文本的合法性。现在 tasklab 的 submit 增加一个可选元数据字段,仍只接受白名单任务和人工生成的字符串,不引入第二套工程。 新字典,旧列表 123metadata = {"labels": ["short"]}shallow = metadata.copy()shallow["labels"].append("shared") 执行后 metadata["labels"] == ["short&quo...
Python 02:相等的值为何不一定是同一对象
词频任务可以接收空文本,得到空的结果字典。若把 if not text 当作“未提交文本”,合法的 "" 会被拒绝;若把 0 == False 当作“二者是同一个对象”,又会把两种类型混为一谈。输入边界需要区分三个问题:值是否相等、是否指向同一对象、是否在条件语句里视为真。 本章在 00 的顺序任务和01 的引用图上继续实验,解释器仍是已记录的 CPython 3.12.3。 一个空字符串不是“没有参数” 目前的 submit(store, "word_count", text) 要求 text 是字符串,允许空字符串;输入 None、整数 0 或布尔值 False 均报 TypeError,且不增加任务。与三种拒绝输入相比,空字符串会创建正常的 queued 任务,run 后状态为 succeeded、结果为 {}。 这是业务约定,不是语言替应用自动定义的“空值语义”。在 Python 中,空字符串、数字零、False 和 None 都是假值;标准类型文档的真值测试列出了这类行为。因而 if not text: 无...
Python 01:赋值究竟改变了什么
同一份任务输入交给两个函数:一个把形参设为新列表,调用者看不到变化;另一个在形参上调用 append,调用者的列表却多了一个词。要判断提交的任务会处理哪份输入,不能只看变量名是否相同,得先找名称所指向的对象,再看是重新绑定了名称,还是修改了对象。 本章延续 00:从哪个 Python 开始复现 冻结的 CPython 3.12.3 和同一个 tasklab。输入仍是人工生成的文本,执行器仍是进程内顺序版本。 两个名称,一份列表 12words = ["hello"]alias = words words 与 alias 是两个名称;alias is words 的结果为 True。不是执行了“把列表复制一份”,而是两个名称目前引用同一个列表对象。官方数据模型区分对象的身份、类型和值,还说明列表是可变对象;执行模型的名称绑定章节说明赋值如何绑定名称。这两项规则不能倒过来理解成“名称本身就是存放列表内容的盒子”。 图左侧是调用 rebind(alias) 前的关系。函数形参 incoming 起初也引用原列表;函数内 incoming = ["dif...
Python 00:从哪个 Python 开始复现
一段词频脚本在终端运行成功,换一个终端却提示 No module named tasklab。代码没有变,执行它的命令、工作目录和模块搜索路径可能已经变了。还没讨论对象或并发,实验的第一条输入就应当是“由哪个解释器、在什么条件下运行”,而不是仅仅保存一段源码。 本系列使用一个累计扩展的任务应用 tasklab:提交人工生成的文本、执行白名单词频任务、查询状态、请求取消。00 只做进程内顺序执行。数据库、安装包与服务接口在后续章节加入,不能用此处的一次运行推断它们已经可用。 运行的起点不在源文件里 下面这条命令在仓库的 examples/python-language/ 目录执行: 1PYTHONPATH=src /usr/bin/python3.12 -m tasklab.demo /usr/bin/python3.12 是本次明确调用的可执行文件;-m tasklab.demo 要求解释器按模块搜索路径找到并运行模块。PYTHONPATH=src 临时加入尚未安装的包所在目录,并非应用交付时推荐的安装方式。从其他目录执行却沿用相对的 src,搜索位置就会不同。官方命令行文档...
Kotlin 01:val 与对象可变性不是一回事
任务标签写在 val labels 里,就不会被改了吗?如果另一个函数仍持有同一个列表,答案相反。另一方面,把重试次数从 Int 赋给 Long,也不意味着原来那次加法没有溢出。两个问题都来自同一种混淆:把“变量所在的边界”当成了“它指向或计算的值的性质”。 本篇在 00 的命令行任务 上增加一个尚未接入 CLI 的任务配置实验:观察 val 引用、var 重新绑定和 Int 运算。当前 submit 仍只接收一条合成输入,不会因为有了重试参数就自动执行任务;配置接口也还没有并发访问保证。 val 锁住的是哪一层 01 新增的 TaskSettings 很小,恰好把容易说错的边界暴露出来: 123456789class TaskSettings(taskLabel: String) { val labels: MutableList<String> = mutableListOf(taskLabel) var retryLimit: Int = 0 fun maxAttempts(): Long { require...
Kotlin 00:从源码到 JVM 的第一条任务
终端打印出“任务已接收”,能证明什么?只能证明这次进程走到输出语句。若下一次启动还要查到它,就需要另一套保存状态的机制;若想确认它确实执行了,就还需要执行者和结果记录。把这几个结果混在一行成功提示里,后面讨论取消、事务时就很容易把“请求返回”误当“任务完成”。 本系列从一条不联网、不依赖真实数据的文本任务开始。00 只让命令行接受合法输入,并对错误输入明确失败;先回答更靠前的问题:究竟是哪一套编译器、依赖与 JVM 运行了这些代码?累计工程在 examples/kotlin-language/,本篇对应的完整快照和运行记录随文章素材一起交付。 一条任务走过哪些边界 工程入口是 src/main/kotlin/example/tasks/Main.kt。这一阶段没有任务队列,submit 只是一个语法约定:第一个参数是动词,第二个参数是合成任务文本。 12345678910fun submitTask(args: Array<String>): String { require(args.size == 2 && args[0] == ...










