普贤教程 › 生态包
标识与校验
生成唯一 ID、解析 ID、比较版本号——这 6 个包都不需要数据库、不需要中心服务,编译进二进制就能用。本页所有输出为本机实测。
四种 ID,四种取舍
| 包 | 样例 | 长度 | 有序 | 适用 |
|---|---|---|---|---|
uuid | bace88f9-b996-457d-9399-6349c7afa702 | 36 | 否 | 跨系统标识、幂等键 |
ulid | 01M6NHWAXKWRY7ZQRGDSVDT40M | 26 | 是(时间前缀) | 主键(按时间自然排序) |
nanoid | KTN1f86tgiuAqO4MYJdIE | 21 | 否 | 短链接、分享码 |
snowflake | 1986024021816274944 | 19 位数字 | 是(毫秒前缀) | 数据库 bigint 主键 |
px
import uuid import ulid import nanoid import snowflake print(uuid_v4()) print(uuid_valid(uuid_v4())) # true print(uuid_format(uuid_parse("bace88f9b996457d93996349c7afa702").ok())) # 32 hex → 标准 36 位 print(ul_new()) # 01M6NHWAXKWRY7ZQRGDSVDT40M print(ul_ts(ul_new()).ok()) # 1793872243636 ← 毫秒时间戳 print(nd_generate(21).ok()) # KTN1f86tgiuAqO4MYJdIE var sf = sf_new(7).ok() # machine_id 0..1023 print(sf_next(sf, 1762340000000).ok()) # 1986024021816274944 print(sf_parse(1986024021816274944)) # {machine: 7, ms: 1762340000000, seq: 1}
snowflake 的坑:
sf_next(sf) 不带时间会报错
sf_next(state, now_ms=null) 的参数名 now_ms 遮蔽了内建函数 now_ms(),函数体里再调 now_ms() 时拿到的是 null:
运行时错误 [sf_next 行24]: R1004: 值不可调用: null。
必须显式传毫秒时间戳:sf_next(sf, now_ms())(在外面先算好后传进去)。这与「自定义 def env(...) 会段错误」是同一类问题——参数名不要与内建函数同名。ID 选型的经验值要「按时间排序又不想暴露业务量」→ ULID;要「数据库一列 long、索引友好」→ Snowflake;要「短、可放 URL」→ NanoID;要「跟外部系统对接」→ UUID。
版本号比较
px
import semver print(sv_cmp("1.2.3", "1.2.10")) # -1 ← 语义化比较,不是字符串比较 print(sv_satisfies("1.2.5", "^1.2.0")) # true print(sv_best(["1.2.0", "1.2.5", "2.0.0"], "^1.2.0")) # 1.2.5 print(sv_parse("1.2.3-alpha.1+build5")) # {major: 1, minor: 2, patch: 3, pre: alpha.1, build: build5, raw: …}
范围语法是 cargo/npm 风格:^ / ~ / 精确 / * / x 通配。pxpkg 自己的版本解析用的就是它。
别用字符串比较版本
"1.2.10" > "1.2.9" 在字符串比较里是 false("1" < "9")。这类 bug 只会在 1.2.10 发布那天爆出来。校验位
px
import luhn print(luhn_valid("4539148803436467")) # 银行卡号格式校验 print(luhn_append("453914880343646")) # 补出校验位 print(luhn_card_type("4539148803436467")) # visa import checksum print(crc32_hex("hello")) # 3610a686 ← 与 zlib.crc32 一致 print(adler32("hello")) # 103547413 print(crc32_hex("")) # 00000000
checksum 是「一致性校验」不是「防篡改」:CRC32 用来发现传输损坏,要防篡改用 SHA-256(sha256_hex 内建,或 secure_random 同族的哈希)。
编码
px
import base58 print(base58_encode("Hello PuXian")) # 2NEpo7TZRNNpwXZD7 print(base58_decode("2NEpo7TZRNNpwXZD7").ok())# Hello PuXian print(base58check_encode("abc")) # 4h3c6RH52R ← 带 4 字节双 SHA-256 校验 import punycode print(pd_encode("中文.cn")) # xn--fiq228c.cn
Base58 去掉了 0OIl 四个易混字符,适合人工抄录;Base58Check 加了校验位,抄错能立刻发现——比特币地址就是这么做的。
字段校验(与 auth 页互补)
px
import validator print(val_validate({"email": "a@b.com", "age": 20}, {"email": "required|email", "age": "int_range=18,60"})) # Ok(null)
规则见 认证与安全页。validator 管格式,luhn / idcard 管校验位,两者组合能挡住绝大多数「手滑输入」。