tokenize — Python 源代码的 Tokenizer — Python 文档

来自菜鸟教程
Python/docs/3.10/library/tokenize
跳转至:导航、​搜索

tokenize — Python 源代码的分词器

源代码: :source:`Lib/tokenize.py`



tokenize 模块为 Python 源代码提供了一个词法扫描器,用 Python 实现。 此模块中的扫描仪也将注释作为标记返回,这使其可用于实现“漂亮的打印机”,包括用于屏幕显示的着色器。

为了简化令牌流处理,使用通用 OP 令牌类型返回所有 operatordelimiter 令牌以及 Ellipsis。 可以通过检查从 tokenize.tokenize() 返回的 命名元组 上的 exact_type 属性来确定确切的类型。

标记输入

主要入口点是 生成器

tokenize.tokenize(readline)

tokenize() 生成器需要一个参数,readline,它必须是一个可调用对象,提供与 io.IOBase.readline() 方法相同的接口文件对象。 对函数的每次调用都应以字节形式返回一行输入。

生成器生成具有以下成员的 5 元组:令牌类型; 令牌字符串; 一个 2 元组 (srow, scol) 整数,指定标记在源中开始的行和列; 一个 2 元组 (erow, ecol) 整数,指定标记在源中结束的行和列; 以及找到令牌的行。 传递的行(最后一个元组项)是 physical 行。 5 元组作为 命名的元组 返回,字段名称为:type string start end line

返回的 命名元组 有一个名为 exact_type 的附加属性,其中包含 OP 标记的确切运算符类型。 对于所有其他令牌类型 exact_type 等于命名元组 type 字段。

3.1 版更改: 添加了对命名元组的支持。

3.3 版更改: 增加了对 exact_type 的支持。

tokenize() 根据 PEP 263,通过查找 UTF-8 BOM 或编码 cookie 来确定文件的源编码。

tokenize.generate_tokens(readline)

标记读取 unicode 字符串而不是字节的源。

tokenize() 一样,readline 参数是一个可调用的返回单行输入的参数。 然而,generate_tokens() 期望 readline 返回一个 str 对象而不是字节。

结果是一个迭代器产生命名元组,就像 tokenize()。 它不会产生 ENCODING 令牌。

token 模块中的所有常量也从 tokenize 导出。

提供了另一个功能来反转标记化过程。 这对于创建标记脚本、修改令牌流和写回修改后的脚本的工具很有用。

tokenize.untokenize(iterable)

将令牌转换回 Python 源代码。 iterable 必须返回包含至少两个元素的序列,标记类型和标记字符串。 任何附加的序列元素都将被忽略。

重建的脚本作为单个字符串返回。 结果保证标记回以匹配输入,以便转换是无损的并确保往返。 该保证仅适用于令牌类型和令牌字符串,因为令牌之间的间距(列位置)可能会发生变化。

它返回字节,使用 ENCODING 标记编码,这是 tokenize() 输出的第一个标记序列。 如果输入中没有编码标记,则返回一个 str 。

tokenize() 需要检测它标记的源文件的编码。 它用于执行此操作的函数可用:

tokenize.detect_encoding(readline)

detect_encoding() 函数用于检测应该用于解码 Python 源文件的编码。 它需要一个参数 readline,与 tokenize() 生成器的方式相同。

它最多会调用 readline 两次,并返回使用的编码(作为字符串)和它读入的任何行(不是从字节解码)的列表。

它根据 PEP 263 中指定的 UTF-8 BOM 或编码 cookie 的存在来检测编码。 如果 BOM 和 cookie 都存在,但不同意,则会引发 SyntaxError。 请注意,如果找到 BOM,'utf-8-sig' 将作为编码返回。

如果未指定编码,则返回默认值 'utf-8'

使用 open() 打开 Python 源文件:它使用 detect_encoding() 检测文件编码。

tokenize.open(filename)

使用 detect_encoding() 检测到的编码以只读模式打开文件。

3.2 版中的新功能。

exception tokenize.TokenError

当文件中的任何地方都没有完成可能被分成几行的文档字符串或表达式时引发,例如:

"""Beginning of
docstring

or:

[1,
 2,
 3

Note that unclosed single-quoted strings do not cause an error to be raised. They are tokenized as ERRORTOKEN, followed by the tokenization of their contents.


Command-Line Usage

New in version 3.3.


The tokenize module can be executed as a script from the command line. It is as simple as:

python -m tokenize [-e] [filename.py]

The following options are accepted:

-h, --help
show this help message and exit
-e, --exact
display token names using the exact type

If filename.py is specified its contents are tokenized to stdout. Otherwise, tokenization is performed on stdin.


Examples

Example of a script rewriter that transforms float literals into Decimal objects:

from tokenize import tokenize, untokenize, NUMBER, STRING, NAME, OP
from io import BytesIO

def decistmt(s):
    """Substitute Decimals for floats in a string of statements.

    >>> from decimal import Decimal
    >>> s = 'print(+21.3e-5*-.1234/81.7)'
    >>> decistmt(s)
    "print (+Decimal ('21.3e-5')*-Decimal ('.1234')/Decimal ('81.7'))"

    The format of the exponent is inherited from the platform C library.
    Known cases are "e-007" (Windows) and "e-07" (not Windows).  Since
    we're only showing 12 digits, and the 13th isn't close to 5, the
    rest of the output should be platform-independent.

    >>> exec(s)  #doctest: +ELLIPSIS
    -3.21716034272e-0...7

    Output from calculations with Decimal should be identical across all
    platforms.

    >>> exec(decistmt(s))
    -3.217160342717258261933904529E-7
    """
    result = []
    g = tokenize(BytesIO(s.encode('utf-8')).readline)  # tokenize the string
    for toknum, tokval, _, _, _ in g:
        if toknum == NUMBER and '.' in tokval:  # replace NUMBER tokens
            result.extend([
                (NAME, 'Decimal'),
                (OP, '('),
                (STRING, repr(tokval)),
                (OP, ')')
            ])
        else:
            result.append((toknum, tokval))
    return untokenize(result).decode('utf-8')

Example of tokenizing from the command line. The script:

def say_hello():
    print("Hello, World!")

say_hello()

will be tokenized to the following output where the first column is the range of the line/column coordinates where the token is found, the second column is the name of the token, and the final column is the value of the token (if any)

$ python -m tokenize hello.py
0,0-0,0:            ENCODING       'utf-8'
1,0-1,3:            NAME           'def'
1,4-1,13:           NAME           'say_hello'
1,13-1,14:          OP             '('
1,14-1,15:          OP             ')'
1,15-1,16:          OP             ':'
1,16-1,17:          NEWLINE        '\n'
2,0-2,4:            INDENT         '    '
2,4-2,9:            NAME           'print'
2,9-2,10:           OP             '('
2,10-2,25:          STRING         '"Hello, World!"'
2,25-2,26:          OP             ')'
2,26-2,27:          NEWLINE        '\n'
3,0-3,1:            NL             '\n'
4,0-4,0:            DEDENT         ''
4,0-4,9:            NAME           'say_hello'
4,9-4,10:           OP             '('
4,10-4,11:          OP             ')'
4,11-4,12:          NEWLINE        '\n'
5,0-5,0:            ENDMARKER      ''

The exact token type names can be displayed using the -e option:

$ python -m tokenize -e hello.py
0,0-0,0:            ENCODING       'utf-8'
1,0-1,3:            NAME           'def'
1,4-1,13:           NAME           'say_hello'
1,13-1,14:          LPAR           '('
1,14-1,15:          RPAR           ')'
1,15-1,16:          COLON          ':'
1,16-1,17:          NEWLINE        '\n'
2,0-2,4:            INDENT         '    '
2,4-2,9:            NAME           'print'
2,9-2,10:           LPAR           '('
2,10-2,25:          STRING         '"Hello, World!"'
2,25-2,26:          RPAR           ')'
2,26-2,27:          NEWLINE        '\n'
3,0-3,1:            NL             '\n'
4,0-4,0:            DEDENT         ''
4,0-4,9:            NAME           'say_hello'
4,9-4,10:           LPAR           '('
4,10-4,11:          RPAR           ')'
4,11-4,12:          NEWLINE        '\n'
5,0-5,0:            ENDMARKER      ''

Example of tokenizing a file programmatically, reading unicode strings instead of bytes with generate_tokens():

import tokenize

with tokenize.open('hello.py') as f:
    tokens = tokenize.generate_tokens(f.readline)
    for token in tokens:
        print(token)

Or reading bytes directly with tokenize():

import tokenize

with open('hello.py', 'rb') as f:
    tokens = tokenize.tokenize(f.readline)
    for token in tokens:
        print(token)