Python encoding 原理及应用
初学 Python 时可能不适应需要手动注释文件编码的写法,根据 PEP 263 所定义, 必须在 Python 文件的前两行,按照下面的写法注明文件的编码:
# coding=<encoding name>
或者:
# -*- coding: <encoding name> -*-
或者:
# vim: set fileencoding=<encoding name> :
如果不定义文件的编码 Python 解释器会将其视为 ASCII 编码的文件,通常都建议将文件标注为 UTF-8 编码。 像这样:
# coding=utf8
如果不使用 UTF-8 编码会怎样呢?这篇文章 中介绍了一种有趣的用法(不一定支持 Python 3):
# -*- coding: rot_13 -*-
cevag 'uryyb jbeyq!'.rapbqr('rot_13')
相当于
# -*- coding: utf-8 -*-
print 'uryyb jbeyq!'.encode('rot_13')
会打印出 hello world!
。
Dropbox 很久之前开源的 pyxl 提供了一种更实用的用法:
# coding: pyxl
print <html><body>Hello World!</body></html>
相当于
import html
print (
html.head().appendChild(
html.body().appendChild(
html.text("Hello World!"))))
这里通过自己定义的编码格式 pyxl (需要安装 pyxl Python 库),在 Python 代码中直接编写 HTML。
此外,还有一个 interpy 库同样非常实用,它能提供 Ruby 中一样的字符串拼接语法:
# coding: interpy
word = 'world'
print "Hello, #{word}!"
interpy 的作者 Syrus 在 StackOverflow 上介绍了其实现原理,整理如下:
Python 如何将源码文件编译为比特码
加载源文件
Python 解释器首先会阅读并存储源文件内容,由于文件的编码格式可能千奇百怪,所以 Python 首先会对它进行解码,“魔法”就发生在这里:
如果没有找到编码声明,Python 会将其当作 ASCII(Python 2 及以下版本)或者 UTF-8 (Python 3)编码。Python 2 环境下,如果文件内容中存在 Unicode 字符,你首先需要在文件头部 声明编码类型,否则 Python 将文件当作 ASCII 编码格式进行解码就会发生错误。
解码文件内容
在这个阶段我们可以注册一个定制的 codec
(包括编码器和解码器),如 interpy 中所做, 并(通过 # coding: codec_name
)告诉 Python 解释器应该用哪个 codec
。
当然,我们还需要注册 codec
,而且需要在 import 发生之前运行。可以通过路径设置文件 (.pth) 达到这一目的,它会先于所有非 __main__
模块执行。
转换文件内容
Python 调用了我们定制的 codec 之后,输出就任由我们控制了。
不过怎样理解 Python 文件语法(token)呢?可以调用 Python tokenizer 并按照我们需要修改 token。
interpy 的例子非常简单,只是简单地替换掉了文本中的字符串。
发送解码后的内容
解码完成后,就可以再将其返回给 Python 编译器,编译器会将其编译为字节码。