无需修改Python编译器,能否通过库新增自定义关键字?
答案是:没办法直接添加真正的新关键字——Python的语法解析器(比如CPython的pgen)是把关键字列表硬编码进去的,只要代码里出现不在官方列表里的"关键字",比如你说的matches,解释器一碰到就会抛出语法错误,这一步在AST转换之前就发生了,所以单纯靠AST转换救不了。
不过,我们可以用一些技巧模拟出类似关键字的语法效果,满足你想要的正则匹配简写需求,下面几个方案供你参考:
1. 用运算符重载模拟中缀语法
Python允许重载运算符,我们可以利用这一点实现接近X matches Y的写法。比如用@运算符(因为它的优先级和可读性都不错):
import re class ReMatcher: def __init__(self, pattern): self.pattern = pattern def __rmatmul__(self, string): # __rmatmul__处理 string @ self 的情况 return re.fullmatch(self.pattern, string) is not None # 把ReMatcher实例绑定到matches变量,用法就接近关键字了 matches = ReMatcher # 测试你的例子 if "You can't take the sky from me" @ matches('.+sky.+'): print("匹配成功!")
这种写法不需要任何预编译或额外工具,完全是合法的Python代码,只是把matches变成了一个可调用的对象,用@来连接字符串和正则模式。
2. 代码预处理+AST转换
如果你坚持想要X matches Y的写法,可以先对代码做预处理,把这个非法语法转换成合法的Python代码,再执行。比如用tokenize模块来识别并替换matches关键字:
import tokenize import io import re def preprocess_matches(code): # 把代码转换成token流 tokens = list(tokenize.tokenize(io.BytesIO(code.encode('utf-8')).readline)) new_tokens = [] i = 0 while i < len(tokens): tok_type, tok_str, _, _, _ = tokens[i] # 识别"matches"这个"伪关键字" if tok_type == tokenize.NAME and tok_str == 'matches': # 确保它前后都是合法的表达式(这里做简单判断) if i > 0 and i < len(tokens)-1: # 把 X matches Y 转换成 re.fullmatch(Y, X) new_tokens.extend([ tokens[i-1], # 保留左边的字符串 (tokenize.NAME, 're.fullmatch'), (tokenize.OP, '('), tokens[i+1], # 右边的正则模式 (tokenize.OP, ','), tokens[i-1], # 再放一次左边的字符串 (tokenize.OP, ')') ]) i += 2 # 跳过matches和后面的表达式 continue # 其他token直接保留 new_tokens.append(tokens[i]) i += 1 # 把处理后的token流还原成代码 return tokenize.untokenize(new_tokens).decode('utf-8') # 测试用例 raw_code = ''' test_str = "You can't take the sky from me" if test_str matches '.+sky.+': print("正则匹配成功!") ''' # 预处理后执行 processed_code = preprocess_matches(raw_code) exec(processed_code)
这个方案需要先处理代码再运行,适合自己写脚本或者做一个小工具来批量处理。如果是在IDE里写代码,可能需要配合插件来实时预处理。
3. 用宏库实现自定义语法
有一些第三方宏库(比如macropy)可以在编译阶段修改AST,从而实现类似自定义语法的功能。虽然不是真正的关键字,但可以让写法更接近你的需求:
from macropy.core.macros import macros, expr from macropy.core.quotes import ast_literal import re @expr def matches(tree): # 把 X matches Y 的AST节点转换成 re.fullmatch(Y, X) left_expr = tree.left right_expr = tree.comparators[0] return ast_literal[re.fullmatch(ast_literal[right_expr], ast_literal[left_expr])] # 启用宏后就可以用matches语法了 with macros: if "You can't take the sky from me" matches '.+sky.+': print("匹配成功!")
不过要注意,macropy的安装和使用需要一些额外配置,而且这个库的维护活跃度不算太高,适合对语法有特殊需求的场景。
总结
Python的语法解析是编译阶段的第一步,硬编码的关键字列表意味着我们没办法在不修改编译器的情况下添加真正的新关键字。但通过运算符重载、代码预处理或者宏库,我们可以模拟出类似的语法效果,满足实际的使用需求。
内容的提问来源于stack exchange,提问作者noamt

