在不自定义编译器的情况下向 Python 添加新语句

Add new statements to Python without customizing the compiler

我想向 Python 添加一个新关键字,@EliBendersky 的 wonderful answer 解释了如何通过更改代码和重新分发 Python 编译器来完成此操作。

是否可以引入一个新的关键字而不改变编译器代码?或许通过图书馆介绍一下?

编辑:

例如,我想添加一个 shorthand 用于正则表达式匹配,方法是添加一个像 matches 这样的关键字,可以像这样使用:

"You can't take the sky from me" matches '.+sky.+'

我可以使用 AST 转换添加新的自定义行为,但上述情况会因语法错误而失败。

不改变语言就无法引入新关键字

解析器是 tool/program,它通读代码,并决定什么有意义什么没有。 虽然这是一个相当粗略的定义,但结果是该语言由其解析器定义

解析器依赖于 ast module documentation 中指定的语言(正式)语法。

虽然仅仅定义一个函数只是引入了一个新特性而没有修改语言,但添加关键字无异于引入了一种新语法,这反过来又改变了语言的语法。

因此,如果不更改语法的语言,就不能在向语言添加新语法的意义上添加新关键字,这需要编辑编译和执行链。

然而...

可能有一些聪明的方法来引入新功能,看起来像新语法但实际上只使用现有语法。 例如,goto module 依赖于一种不太为人所知的 属性 语言,即忽略合格标识符中点周围的空格。

你可以自己试试:

>>> l = [1, 2, 3]
>>> l    .append(4)
>>> l
[1, 2, 3, 4]
>>> l.    append(5)
>>> l
[1, 2, 3, 4, 5]

这允许使用以下看起来像新语法但实际上不是的语法:

label .myLabel
goto .myLabel

现在,goto 模块使用解释器内部工作的方式来执行从一个 goto 到给定的 label... 但这是另一个问题。


我想补充一点,Python 是一种非常开放的语言。 它提供了大量很少使用的运算符,例如 @。 这个运算符从 Python 3.5 引入,主要用于矩阵乘法,然后回退到对 __matmul__ 的调用。 我不得不说,我从未在代码中见过它。 那么,为什么不将它用于您的目的呢?

让我们一步步来吧。 我建议定义一个 r class,它将作为一个正则表达式。

import re

class r:
    def __init__(self, pattern):
        self.regex = re.compile(pattern)

现在,我希望能够将 @ 运算符与此 class 以及字符串一起使用,并在字符串和模式之间使用 match 的语义. 我将定义 __matmul__ 方法,如下所示:

class r:
    def __matmul__(self, string):
        return bool(self.regex.match(string))

现在,我可以执行以下操作:

>>> r("hello") @ "hello"
True
>>> r("hello"] @ "world"
False

非常好,但还不是那么好。 我还将定义 __rmatmul__ 方法,因此它只是回退到对 __matmul__ 的调用。 最后,r class 看起来像这样:

class r:
    def __init__(self, pattern):
        self.regex = re.compile(pattern)

    def __matmul__(self, string):
        return bool(self.regex.match(string))

    def __rmatmul__(self, string):
        return self @ string

现在,反向操作也可以了:

>>> "hello" @ r("hello")
True
>>> "123456" @ r("\d+")
True
>>> "abc def" @ r("\S+$")
False

这与您的尝试非常接近,只是我没有引入新关键字! 当然,现在 r 标识符必须被保护,就像 strlist...

对于您的特定 "problem"(缩短匹配正则表达式的方式),解决方案是创建 str 的子类并使用未使用的二元运算符(例如:减号,也许是可以做更好的选择,不幸的是我们不能使用 ~ 因为它是一元的)

示例:

import re

class MyStr(str):
    def __sub__(self,other):
        return re.match(other,self)

a = MyStr("You can't take the sky from me")
print(a - '.+sky.+')
print(a - '.+xxx.+')

结果:

<_sre.SRE_Match object; span=(0, 30), match="You can't take the sky from me">
None

因此 "subbing" 来自字符串对象的正则表达式 returns 匹配对象。

缺点是现在你必须编写包裹在新对象中的字符串文字(不可能将这个新运算符定义到 str 本身)