运行以下行时:

>>> [0xfor x in (1, 2, 3)]

我以为Python会返回一个错误.

相反,REPL返回:

[15]

原因可能是什么?

推荐答案

TL;博士

Python将表达式读取为[0xf or (x in (1, 2, 3))],因为:

  1. Python tokenizer人.
  2. 运算符优先级

多亏了short-circuit evaluation,它永远不会提升NameError——如果or操作符左边的表达式是真实值,Python永远不会try 计算它的右边.

解析十六进制数

首先,我们必须了解Python如何读取十六进制数.

tokenizer.c的巨大tok_get功能中,我们:

  1. Find第一个0x.
  2. Keep reading the next characters,只要它们在0-f范围内.

解析后的标记0xf(因为"o"不在0-f的范围内)最终将被传递给PEG解析器,PEG解析器将其转换为十进制值15(见附录A).

我们仍然需要解析剩下的代码or x in (1, 2, 3)],剩下的代码如下:

[15 or x in (1, 2, 3)]

运算符优先级

因为inoperator precedenceor高,所以我们可能希望x in (1, 2, 3)先进行判断.

这是一个麻烦的情况,因为x并不存在,而且会产生NameError.

or is lazy

幸运的是,Python支持Short-circuit evaluation,因为or是一个惰性运算符:如果左操作数等同于True,Python就不会费心计算右操作数.

我们可以通过ast模块看到它:

parsed = ast.parse('0xfor x in (1, 2, 3)', mode='eval')
ast.dump(parsed)

输出:


    Expression(
        body=BoolOp(
            op=Or(),
            values=[
                Constant(value=15),   # <-- Truthy value, so the next operand won't be evaluated.
                Compare(
                    left=Name(id='x', ctx=Load()),
                    ops=[In()],
                    comparators=[
                        Tuple(elts=[Constant(value=1), Constant(value=2), Constant(value=3)], ctx=Load())
                    ]
                )
            ]
        )
    )

最后的表达式等于[15].


附录A:PEG解析器

pegen.cparsenumber_raw函数中,我们可以找到Python如何处理前导零:

    if (s[0] == '0') {
        x = (long)PyOS_strtoul(s, (char **)&end, 0);
        if (x < 0 && errno == 0) {
            return PyLong_FromString(s, (char **)0, 0);
        }
    }

PyOS_strtoul等于Python/mystrtoul.c.

在mystrtoul里面.c、 解析器查看one character after the 0x.如果是十六进制字符,Python会将数字的基数设置为16:

            if (*str == 'x' || *str == 'X') {
                /* there must be at least one digit after 0x */
                if (_PyLong_DigitValue[Py_CHARMASK(str[1])] >= 16) {
                    if (ptr)
                        *ptr = (char *)str;
                    return 0;
                }
                ++str;
                base = 16;
            } ...

然后,只要字符在0-f的范围内,它就会将数字的其余部分替换为parses:

    while ((c = _PyLong_DigitValue[Py_CHARMASK(*str)]) < base) {
        if (ovlimit > 0) /* no overflow check required */
            result = result * base + c;
        ...
        ++str;
        --ovlimit;
    }

Eventually,它将指针设置为指向扫描的最后一个字符,即超过最后一个十六进制字符一个字符:

    if (ptr)
        *ptr = (char *)str;

谢谢

Python-3.x相关问答推荐

如何在python中有效地使用多处理和pytube库来加快下载速度?

我的SELECT函数搜索的是列,而不是列中的数据.我怎么才能让它搜索数据呢?

将字符串转换为python日期时间时出错

命名空间前缀无效

基于组/ID从原始数据框中创建两个子数据框

在 pytest 中,如何测试 sys.exit('some error message')?

如何计算Pandas 列中每列唯一项目的出现次数?

如果值超出上下限(异常值处理),则将值的数据框替换为 np.nan

BeautifulSoup 和 pd.read_html - 如何将链接保存到最终数据框中的单独列中?

类型提示和链式赋值以及多重赋值

无法在 Windows Python 3.5 上安装 Levenshtein 距离包

Python - For 循环数百万行

python total_ordering:为什么使用 __lt__ 和 __eq__ 而不是 __le__?

pysftp vs. Paramiko

pip install dryscrape 失败并显示错误:[Errno 2] 没有这样的文件或目录:'src/webkit_server'?

如何将文档字符串放在 Enums 上?

三个参数的reduce函数

如何将发音相似的词放在一起

有效地判断一个元素是否在列表中至少出现 n 次

在 Meta 中创建具有动态模型的通用序列化程序