【问题标题】:Add new statements to Python without customizing the compiler在不自定义编译器的情况下向 Python 添加新语句
【发布时间】:2018-07-09 11:55:13
【问题描述】:

我想为 Python 添加一个新关键字,@EliBendersky 的 wonderful answer 解释了如何通过更改代码和重新分发 Python 编译器来做到这一点。

是否可以引入新的关键字而不更改编译器代码?也许通过图书馆介绍它?

编辑:

例如,我想通过添加像matches 这样的关键字来添加正则表达式匹配的简写,可以像这样使用:

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

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

【问题讨论】:

  • 出于好奇,为什么?
  • 新关键字,嗯,似乎很难。但您可以使用 from xxx import yyy: 将 yyy 定义为“关键字”。
  • 没有。词法分析器内置关键字,需要重新编译。关键字是官方语法定义的一部分。
  • XY 问题。你想使用你的图书馆吗?坚持使用官方python :)
  • 添加关键字的门槛非常高,因为根据定义编译器需要识别它,这使得它不能被用作任何地方的标识符。

标签: python python-3.x syntax keyword


【解决方案1】:

不改变语言就不能引入新的关键字

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

解析器依赖于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 类,它的行为就像一个正则表达式。

import re

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

现在,我希望能够将 @ 运算符与此类一起使用,并与字符串一起使用,在字符串和模式之间具有 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 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...

【讨论】:

  • 好主意。而且我什至不需要添加新课程。我可以使用 AST 对其进行转换
【解决方案2】:

对于您的特定“问题”(缩短匹配正则表达式的方式),解决方案是创建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

因此,从字符串对象中“替换”正则表达式会返回匹配对象。

缺点是现在您必须编写包装在新对象中的字符串文字(无法将这个新运算符定义为 str 本身)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-12-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-20
    • 2023-03-27
    相关资源
    最近更新 更多