- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
你能在 Python 的语法中添加新的语句(比如 print
、 raise
、 with
)吗?
说,让..
mystatement "Something"
new_if True:
print "example"
最佳答案
您可能会发现这很有用 - Python internals: adding a new statement to Python ,这里引用:
本文试图更好地理解 Python 的前端是如何工作的。只是阅读文档和源代码可能有点无聊,所以我在这里采取动手的方法:我将添加一个 until
对 Python 的声明。
本文的所有编码都是针对 Python Mercurial repository mirror 中的尖端 Py3k 分支完成的。 .until
陈述
一些语言,比如 Ruby,有一个 until
声明,这是对 while
的补充( until num == 0
等价于 while num != 0
)。在 Ruby 中,我可以这样写:
num = 3
until num == 0 do
puts num
num -= 1
end
3
2
1
num = 3
until num == 0:
print(num)
num -= 1
until
对 Python 的声明。虽然我认为这样的声明会使一些代码更清晰,并且本文展示了添加是多么容易,但我完全尊重 Python 的极简主义哲学。我在这里真正想做的就是深入了解 Python 的内部工作原理。
pgen
的自定义解析器生成器.这是一个 LL(1) 解析器,可将 Python 源代码转换为解析树。解析器生成器的输入是文件
Grammar/Grammar
[1] .这是一个简单的文本文件,指定了 Python 的语法。
until
添加定义陈述。我找到了
while
语句已定义(
while_stmt
),并添加
until_stmt
下面
[2] :
compound_stmt: if_stmt | while_stmt | until_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated
if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
while_stmt: 'while' test ':' suite ['else' ':' suite]
until_stmt: 'until' test ':' suite
while
做的事情也必须为
until
做,它是一个很好的指导方针。
else
条款来自我对
until
的定义,只是为了让它有点不同(坦率地说,我不喜欢循环的
else
子句,并且认为它不太适合 Python 的 Zen)。
compound_stmt
的规则包括
until_stmt
,正如您在上面的代码段中看到的那样。就在
while_stmt
之后,再次。
make
修改后
Grammar/Grammar
,请注意
pgen
程序运行重新生成
Include/graminit.h
和
Python/graminit.c
,然后重新编译几个文件。
Parser/Python.asdl
它定义了 Python AST 的结构,并为我们的新
until
添加了一个 AST 节点。声明,再次在
while
正下方:
| While(expr test, stmt* body, stmt* orelse)
| Until(expr test, stmt* body)
make
,注意在编译一堆文件之前,
Parser/asdl_c.py
运行以从 AST 定义文件生成 C 代码。这(如
Grammar/Grammar
)是另一个使用迷你语言(换句话说,DSL)来简化编程的 Python 源代码示例。另请注意,由于
Parser/asdl_c.py
是一个Python脚本,这是一种
bootstrapping - 要从头开始构建 Python,Python 必须可用。
Parser/asdl_c.py
生成了管理我们新定义的 AST 节点的代码(到文件
Include/Python-ast.h
和
Python/Python-ast.c
中),我们仍然需要手动编写将相关解析树节点转换为它的代码。这是在文件
Python/ast.c
中完成的.在那里,一个名为
ast_for_stmt
的函数将语句的解析树节点转换为 AST 节点。再次在老 friend 的指导下
while
,我们直接跳进大
switch
用于处理复合语句并为
until_stmt
添加子句:
case while_stmt:
return ast_for_while_stmt(c, ch);
case until_stmt:
return ast_for_until_stmt(c, ch);
ast_for_until_stmt
.这是:
static stmt_ty
ast_for_until_stmt(struct compiling *c, const node *n)
{
/* until_stmt: 'until' test ':' suite */
REQ(n, until_stmt);
if (NCH(n) == 4) {
expr_ty expression;
asdl_seq *suite_seq;
expression = ast_for_expr(c, CHILD(n, 1));
if (!expression)
return NULL;
suite_seq = ast_for_suite(c, CHILD(n, 3));
if (!suite_seq)
return NULL;
return Until(expression, suite_seq, LINENO(n), n->n_col_offset, c->c_arena);
}
PyErr_Format(PyExc_SystemError,
"wrong number of tokens for 'until' statement: %d",
NCH(n));
return NULL;
}
ast_for_while_stmt
的同时编码的。 ,区别在于
until
我决定不支持
else
条款。正如预期的那样,AST 是递归创建的,使用其他 AST 创建函数,如
ast_for_expr
用于条件表达式和
ast_for_suite
为
until
的 body 陈述。最后,一个名为
Until
的新节点被退回。
n
使用一些宏,如
NCH
和
CHILD
.这些都值得理解——他们的代码在
Include/node.h
.
until
创建一种新类型的 AST声明,但实际上这不是必需的。我本可以节省一些工作并使用现有 AST 节点的组合来实现新功能,因为:
until condition:
# do stuff
while not condition:
# do stuff
Until
节点在
ast_for_until_stmt
,我本来可以创建一个
Not
带有
While
的节点作为 child 的节点。由于 AST 编译器已经知道如何处理这些节点,因此可以跳过该过程的后续步骤。
Python/compile.c
.跟随
while
的带领,我们找到函数
compiler_visit_stmt
,负责将语句编译成字节码。我们为
Until
添加一个子句:
case While_kind:
return compiler_while(c, s);
case Until_kind:
return compiler_until(c, s);
Until_kind
也就是说,它是一个从 AST 定义文件自动生成到
_stmt_kind
中的常量(实际上是
Include/Python-ast.h
枚举的值)。 .不管怎样,我们打电话
compiler_until
当然,这仍然不存在。我一会儿说。
compiler_visit_stmt
是奇特的。没有金额
grep
-ping 源树显示它被调用的位置。在这种情况下,只剩下一个选项 - C macro-fu。事实上,一个简短的调查让我们找到了
VISIT
在
Python/compile.c
中定义的宏:
#define VISIT(C, TYPE, V) {\
if (!compiler_visit_ ## TYPE((C), (V))) \
return 0; \
compiler_visit_stmt
在
compiler_body
.回到我们的业务,但是...
compiler_until
:
static int
compiler_until(struct compiler *c, stmt_ty s)
{
basicblock *loop, *end, *anchor = NULL;
int constant = expr_constant(s->v.Until.test);
if (constant == 1) {
return 1;
}
loop = compiler_new_block(c);
end = compiler_new_block(c);
if (constant == -1) {
anchor = compiler_new_block(c);
if (anchor == NULL)
return 0;
}
if (loop == NULL || end == NULL)
return 0;
ADDOP_JREL(c, SETUP_LOOP, end);
compiler_use_next_block(c, loop);
if (!compiler_push_fblock(c, LOOP, loop))
return 0;
if (constant == -1) {
VISIT(c, expr, s->v.Until.test);
ADDOP_JABS(c, POP_JUMP_IF_TRUE, anchor);
}
VISIT_SEQ(c, stmt, s->v.Until.body);
ADDOP_JABS(c, JUMP_ABSOLUTE, loop);
if (constant == -1) {
compiler_use_next_block(c, anchor);
ADDOP(c, POP_BLOCK);
}
compiler_pop_fblock(c, LOOP, loop);
compiler_use_next_block(c, end);
return 1;
}
compiler_while
功能。然而,通过仔细阅读,请记住 Python VM 是基于堆栈的,并查看
dis
的文档。模块,其中有
a list of Python bytecodes通过描述,可以理解发生了什么。
make
,我们可以运行新编译的 Python 并尝试我们的新
until
声明:
>>> until num == 0:
... print(num)
... num -= 1
...
3
2
1
dis
为新语句创建的字节码模块如下:
import dis
def myfoo(num):
until num == 0:
print(num)
num -= 1
dis.dis(myfoo)
4 0 SETUP_LOOP 36 (to 39)
>> 3 LOAD_FAST 0 (num)
6 LOAD_CONST 1 (0)
9 COMPARE_OP 2 (==)
12 POP_JUMP_IF_TRUE 38
5 15 LOAD_NAME 0 (print)
18 LOAD_FAST 0 (num)
21 CALL_FUNCTION 1
24 POP_TOP
6 25 LOAD_FAST 0 (num)
28 LOAD_CONST 2 (1)
31 INPLACE_SUBTRACT
32 STORE_FAST 0 (num)
35 JUMP_ABSOLUTE 3
>> 38 POP_BLOCK
>> 39 LOAD_CONST 0 (None)
42 RETURN_VALUE
until
的正确语义.如果没有执行跳转,循环体将继续运行,直到它跳回到操作 35 处的条件。
myfoo(3)
)而不是显示其字节码。结果并不令人鼓舞:
Traceback (most recent call last):
File "zy.py", line 9, in
myfoo(3)
File "zy.py", line 5, in myfoo
print(num)
SystemError: no locals when loading 'print'
PySymtable_Build
在
PyAST_Compile
调用符号表模块 (
Python/symtable.c
),它以类似于代码生成函数的方式遍历 AST。每个作用域都有一个符号表有助于编译器找出一些关键信息,例如哪些变量是全局变量,哪些是作用域的局部变量。
symtable_visit_stmt
函数在
Python/symtable.c
, 添加处理代码
until
语句,在
while
的类似代码之后声明
[3] :
case While_kind:
VISIT(st, expr, s->v.While.test);
VISIT_SEQ(st, stmt, s->v.While.body);
if (s->v.While.orelse)
VISIT_SEQ(st, stmt, s->v.While.orelse);
break;
case Until_kind:
VISIT(st, expr, s->v.Until.test);
VISIT_SEQ(st, stmt, s->v.Until.body);
break;
Python/symtable.c
的编译器警告.编译器注意到
Until_kind
symtable_visit_stmt
的switch语句中没有处理枚举值并提示。检查编译器警告总是很重要的!
myfoo(3)
按预期工作。
关于python - 你能在 Python 的语法中添加新的语句吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/214881/
在此处回答的另一个问题中,我发现了以下 JavaScript代码: function _dom_trackActiveElement(evt) { if (evt && evt.target)
if (A == 0) OR (B == 0) 怎么说? 最佳答案 只是为了讽刺: if (A === 0 || B === 0) 关于语法,我们在Stack Overflow上找到一个类似的问题:
var ret = [] ,xresult = document.evaluate(exp, rootEl, null, X
我一直在寻找一些类似于下例的 JavaScript。有人可以解释一下吗,因为我以前从未见过这样编写的 JavaScript。 “SomethingHere”和冒号代表什么?我习惯于看到函数 myFun
这是我的程序: delimiter // drop procedure if exists migContactToActor; create procedure migContactToActor(
我遇到了一个问题。我一直在使用 gcc 编译/汇编我的 C 代码一段时间,并且习惯了阅读 Intel 汇编语法。我在生成程序集文件时使用了 -masm=intel 标志。 但是最近因为公司迁移,拿到了
自上而下和自下而上语法有什么区别?举个例子就太好了。 最佳答案 首先,语法本身不是自上而下或自下而上的,解析器是(尽管有些语法可以被其中一个解析,但不能被另一个解析)。 从实践的角度来看,主要区别在于
我知道这是草率的代码,但它是: display dialog ("Start Screensaver. Please type: matrix, coffee, waffles, star, wate
这个问题已经有答案了: Giving name to a loop (6 个回答) 已关闭 8 年前。 我见过这个字符在 C# 中使用,就像 Java 中的扩展一样,但最近我在代码中发现了这个 loo
我正在尝试编写一个函数来检查字符串是否为回文,但我认为在使用字符串指针时存在一些错误。这段代码有什么问题? #include #include #define MAX 1000 int IsPalin
所以在this question我询问了一些 Javascript 是如何被压缩的。问题已得到解答,但以下片段让我非常困惑,以至于我不得不问另一个问题。在这里: for (Y = 0; $ = 'zx
假设我有一个接受这些参数的函数。 int create(Ptr * p,void * (*insert)(void *, void *)) { //return something later } 结
这个问题已经有答案了: Bitwise '&' operator (6 个回答) 已关闭 5 年前。 我在代码中找到了这个,但我从未遇到过像 & 这样的事情,仅 && if ((code & 1) =
我在处理继承类及其中的构造函数和方法的语法时遇到了问题。 我想实现一个类日期和一个子类 date_ISO,它们将按特定顺序设置给定的日、月、年,并通过一种方法将其写入字符串。我觉得我的基类日期工作正常
我正在尝试通过存储过程填充表,如下所示: SET @resultsCount = (SELECT COUNT(*) FROM tableA); SET @i = 0; WHILE @i THEN
谁能解释一下下面代码中的“<<”? mysql test<
刚刚开始学习 MySQL,这是一个菜鸟问题,也是我在 StackOverflow 上的第一个问题。 假设我有 12 个订单状态,我想从其中的 5 个中选择总计。我会使用: SELECT SUM(tot
我的编程背景是在学校学过一点Java。由于某些原因,JavaScript 语法往往让我感到困惑。下面的 JavaScript 代码是一种我不知道如何构成的语法模式: foo.ready = funct
我正在阅读 javascript 源代码,并且我以前没有编写过 javascript。我对它的一些语法感到困惑。 $(function () { window.onload=function
我什至不知道如何命名我想要的东西。那么让我举个例子来解释一下。 虽然火狐使用textContent,但其他浏览器支持innerText属性。顺便说一句,如果我使用了错误的术语,请纠正我。无论如何,到目
我是一名优秀的程序员,十分优秀!