一、王姐的抱怨:"每次改报表都要排队找程序员"
周一早上,王姐把老周堵在仓库门口:"周哥,不是我说你。上个月我提了 11 个报表需求,每个都得等你们排期。我就想问问——有没有办法,让我自己改报表? 我就想点几个选项、写一句话,报表就出来了。"
小陈在旁边小声嘀咕:"她又不是程序员,怎么自己改……"
老周却笑了:"王姐,给我一周。我给你造一门'属于你的小语言'。"
小陈懵了:"给老板造一门语言?"
"对。"老周说,"这一章的主题就两个词:元编程(让代码写代码)和 DSL(为特定场景造语言)。王姐的需求,正好把这两样都逼出来了。"
二、第十层:元编程(代码生成代码)
2.1 反射:运行时"看穿"自己的结构
"先从反射(reflection)说起。"老周打开代码,"假设我们要写一个通用的'把任何对象变成报表表头'的工具——不管传进来的是订单、商品还是顾客,它都能自动读出所有字段名。"
class Order:
def __init__(self, id, book, price, qty):
self.id = id
self.book = book
self.price = price
self.qty = qty
def dump_headers(obj):
"""不写死字段名,运行时'照镜子',看看这个对象有什么字段。"""
return [name for name in vars(obj)] # 反射:列出对象的所有属性名
def dump_row(obj):
return [value for value in vars(obj).values()]
order = Order("A001", "三体", 29.9, 2)
print(dump_headers(order)) # ['id', 'book', 'price', 'qty']
print(dump_row(order)) # ['A001', '三体', 29.9, 2]
"vars(obj) 能在运行时探查对象的结构——这叫反射:程序在运行的时候,检查甚至修改自己的结构。好处:写一套通用代码,能处理任何对象;代价:性能开销 + 牺牲了编译期检查(字段拼错了运行时才炸)。"
"运行时检查/修改自身结构——这就是反射的定义。著名的反射场景:ORM 框架根据类的字段自动生成 SQL、调试器显示对象的内部状态。"
2.2 宏:编译期生成代码
"反射是'运行时',宏(macro)是'编译期'。"老周说,"宏的意思:在代码还没编译之前,先按规则'改写'代码。"
// C 语言宏:写代码时是"模板",编译前被替换成真正的代码
#define MAX(a, b) ((a) > (b) ? (a) : (b))
int x = MAX(3, 5);
// 编译前被替换成:int x = ((3) > (5) ? (3) : (5));
// Rust 宏:更强大,甚至能做语法层面的展开
println!("库存剩 {} 本,单价 {} 元", stock, price);
// println! 是一个宏 —— 编译期展开成一堆真正的代码
"C 的宏是'文本替换',简单但容易踩坑(括号写漏一个就出 bug);Lisp 的宏是'代码就是数据',想怎么写怎么写;Rust 的宏是'类型安全的代码生成'。共同点:编译期生成代码,减少重复劳动。"
2.3 注解 / 装饰器:给代码贴标签
"第三个:注解(annotation)/ 装饰器(decorator)。"老周说,"它的思路很妙:不改变代码本身,只给代码'贴个标签',让工具或框架读取标签后自动干活。"
# Python 装饰器:给函数"加装备"
def retry(times):
def decorator(func):
def wrapper(*args, **kwargs):
for i in range(times):
try:
return func(*args, **kwargs)
except Exception:
if i == times - 1:
raise
time.sleep(1) # 失败重试
return None
return wrapper
return decorator
@retry(times=3) # 贴标签:这个函数调用失败自动重试 3 次
def call_payment_api(order):
return payment_gateway.charge(order) # 网络偶发失败,重试就好
"@retry(times=3) 就是给 call_payment_api 贴了张标签,调用逻辑一行没改,重试能力就自动加上了。Java 的 @Override、@Transactional、Spring 的 @Autowired 都是这个套路——给代码加标记,工具或框架读取标记来干活。"
小陈感叹:"所以元编程,就是让代码'自己会写代码'、'自己会改自己'……"
"对。宏在编译期改写,反射在运行时自省,注解给代码加说明。它们都是'程序处理程序'。"
三、第十一层:领域专用语言
3.1 王姐的 DSL:一门给店长用的小语言
一周后,老周给了王姐一个"报表规则编辑器"。王姐在输入框里写下一行字:
本月的畅销书排行,按销售额排序,取前5名,只看销售额>500的
系统立刻生成报表。小陈在旁边看呆了:"她真的'写代码'了?"
"这就是 DSL(Domain-Specific Language,领域特定语言)。"老周说,"我们给'报表'这个领域,造了一门只有几句话、非程序员也能写的小语言。它在背后被翻译成真正的程序。"
# 王姐写的 DSL 文本
rule_text = "本月畅销书排行 按销售额排序 取前5名 只看销售额>500"
# 解析器:把 DSL 翻译成真正的查询
def parse_rule(text):
parts = text.split()
query = {
"period": "本月",
"sort_by": "销售额" if "销售额" in parts else "销量",
"top_n": 5,
"filter": {"field": "销售额", "op": ">", "value": 500},
}
return query
query = parse_rule(rule_text)
report = run_report(query) # 真正执行查询的代码
"DSL 的哲学:为特定场景造语言,让非程序员也能写——配置、规则、流水线。你天天用的那些'小语言',其实到处都是:"
3.2 你早已在用的一堆 DSL
"咱们系统里,其实已经躺着好几门'小语言'了。"老周把屏幕切过来:
-- ① SQL:数据库查询语言
SELECT book, SUM(price * qty) AS revenue
FROM orders
WHERE created_at >= '2026-09-01'
GROUP BY book
ORDER BY revenue DESC
LIMIT 5;
<!-- ② HTML/CSS:网页结构和样式 -->
<div class="book-card">
<h2>三体</h2>
<p class="price">¥29.9</p>
</div>
<style>
.book-card { border: 1px solid #ccc; padding: 12px; }
.price { color: #c0392b; }
</style>
# ③ RegExp:文本模式匹配 —— 校验 ISBN
import re
isbn_pattern = r"^\d{3}-\d-\d{3}-\d{5}-\d$" # 13 位 ISBN 标准格式
if re.match(isbn_pattern, "978-7-5369-8517-3"):
print("ISBN 格式正确")
# ④ Shell:系统命令语言 —— 每天自动备份数据库
# backup.sh
#!/bin/bash
mysqldump -u root bookstore > /backup/bookstore_$(date +%Y%m%d).sql
echo "备份完成"
"SQL、HTML/CSS、RegExp、Shell——它们全是为特定领域定制的语言:一个管数据查询,一个管网页,一个管文本匹配,一个管操作系统。通用语言(Python、Java)什么都能干,但'什么都能干'意味着'没有一件事最顺手'。DSL 则是'专精一门':SQL 写查询比 Python 优雅一百倍。"
"王姐的报表 DSL,就是把 SQL 又包了一层,让不懂 SQL 的人也能用。配置太复杂 → DSL / 注解——这就是它们存在的意义。"
四、章末:老周的"第十、十一层"总结
白板:
第十层:元编程(代码生成代码)
纯文本
代码
├── 宏 → 编译期生成代码(C 宏、Rust 宏、Lisp 宏)
├── 反射 → 运行时检查/修改自身结构
└── 注解 / 装饰器 → 给代码加标记,工具或框架读取
第十一层:领域专用(为特定场景造语言)
纯文本
通用语言
├── SQL(数据库查询)
├── HTML/CSS(网页结构/样式)
├── RegExp(文本模式)
├── Shell(系统命令)
└── DSL(领域特定语言)→ 让非程序员也能写(配置、规则、流水线)
"王姐用 DSL 玩得很开心,一周自己生成了 20 份报表,再也不堵我们了。"
正说着,王姐又风风火火跑进来:"周哥!现在顾客总问我'有什么书推荐',店员答不上来。你能不能做个'AI 荐书'?我看人家书店都有智能助手了!"
老周看着小陈,露出意味深长的笑:"小陈,还记得开场白我画的图吗?最上面那层——AI / 大模型时代。你学了十年的'让机器干活',现在要学最后一样:让机器'听懂人话'。走,这一章,我带你看看编程语言家族树的树冠。"