Python 标准库笔记 —— __future__
- |
- #Python
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
__future__ 模块可以让一些老版本的 Python 使用新版本中的一些语法特性.
例如在 2.5 版本中是不支持 with…as… 这样的语法进行上下文管理的,
但是如果在代码中加入 from __future__ import with_statement
, 就可以让 2.5
版本的 Python 也支持这个特性.
打开 __future__.py 文件, 可以看到其中支持 7 种这样的关键字特性, 分别是 nested_scopes, generators, division, absolute_import, with_statement, print_function, unicode_literals.
1. nested_scopes
从 PEP 227 的解释来看, nested_scopes 特性允许从代码外层的命名空间中引用变量. 举例子而言, 有下面这段代码:
# from __future__ import nested_scopes
def main():
a = 1
print 'outer\nglobals: %s\nlocals: %s\na=%s\n' % (globals(), locals(), a)
func = lambda: 'inner\nglobals: %s\nlocals: %s\na=%s\n' % (
globals(), locals(), a)
print func()
if __name__ == '__main__':
main()
使用 2.1 版本的 Python 运行, 会返回
test_nested_scopes.py:3: SyntaxWarning: local name 'a' in 'main' shadows use of 'a' as global in nested scope 'lambda'
def main():
outer
globals: {'__doc__': None, 'main': <function main at 0271351C>, '__name__': '__main__', '__builtins__': <module '__builtin__' (built-in)>}
locals: {}
a=1
Traceback (most recent call last):
File "test_nested_scopes.py", line 11, in ?
main()
File "test_nested_scopes.py", line 8, in main
print func()
File "test_nested_scopes.py", line 6, in <lambda>
func = lambda: 'inner\nglobals: %s\nlocals: %s\na=%s\n' % (
NameError: global name 'a' is not defined
执行时首先会报一个 SyntaxWarning, 并且在执行到 lambda 函数时, 会因为找不到变量 a 而抛出 NameError.
而把第一行的注释取消掉后, 再次执行, 会返回
outer
globals: {'nested_scopes': Feature((2, 1, 0, 'beta', 1), (2, 2, 0, 'final', 0)), '__doc__': None, 'main': <function main at 02693F5C>, '__name__': '__main__', '__builtins__': <module '__builtin__' (built-in)>}
locals: {'a': 1}
a=1
inner
globals: {'nested_scopes': Feature((2, 1, 0, 'beta', 1), (2, 2, 0, 'final', 0)), '__doc__': None, 'main': <function main at 02693F5C>, '__name__': '__main__', '__builtins__': <module '__builtin__' (built-in)>}
locals: {}
a=1
可以看到, 在 lambda 函数内部, globals 和 locals 中都找不到变量 a, 然而却能取到 a 的值, 这就是因为从外层命名空间中引用了变量.
有一个奇怪的问题是, 在 Python 2.7 中执行同样的代码, 返回的内容为
outer
globals: {'nested_scopes': _Feature((2, 1, 0, 'beta', 1), (2, 2, 0, 'alpha', 0), 16), '__builtins__': <module '__builtin__' (built-in)>, '__file__': 'test_nested_scopes.py', '__package__': None, '__name__': '__main__', 'main': <function main at 0x02A60C70>, '__doc__': None}
locals: {'a': 1}
a=1
inner
globals: {'nested_scopes': _Feature((2, 1, 0, 'beta', 1), (2, 2, 0, 'alpha', 0), 16), '__builtins__': <module '__builtin__' (built-in)>, '__file__': 'test_nested_scopes.py', '__package__': None, '__name__': '__main__', 'main': <function main at 0x02A60C70>, '__doc__': None}
locals: {'a': 1}
a=1
可以看到 lambda 函数内的 locals 中是有 a 的, 这与 2.1 版本的表现并不一致, 不清楚是在哪个版本开始做的改动. 不过毕竟 nested_scopes 是一个老的特性, 就不去深究了…
2. generators
这个很简单, 加入了使用 yield 创建生成器的语法. 一个简单的例子:
def fab(n):
a = 1
b = 1
i = 0
while i < n:
yield b
a, b = b, a + b
i += 1
def main():
result = fab(5)
print type(result)
for i in result:
print i
if __name__ == '__main__':
main()
运行它, 会输出
<type 'generator'>
1
2
3
5
8
可以看到, 使用了 yield 关键词的函数, 会成为一个生成器.
3. division
这个也很简单. 2.x 版本的 Python 中, 除号 /
计算出的结果会是被除数和除数中精度更高的那一个类型,
举一些例子:
>>> 10 / 3
3
>>> 10L / 3
3L
>>> 10.0 / 3
3.3333333333333335
因此, 除法结果的类型往往是难以预期的. 为了解决这个问题, 引入了新的计算符 //
.
使用 /
得出的结果始终是精确的, 而使用 //
得出的结果始终是向下取整的(但类型仍然是两个数中相对高的那一个),
这样就很明确了. 还是举一些例子:
>>> from \_\_future\_\_ import division
>>> 10 / 3
3.3333333333333335
>>> 10 // 3
3
>>> 10L // 3
3L
>>> 10.0 // 3
3.0
>>> -10.0 // 3
-4.0
4. absolute_import
这个功能也很容易理解. 简单地说, 它是为了解决这样一个问题:当我们在代码中写下
import sys
时, 怎样确定实际导入的是标准库中的 sys 模块, 或是当前文件夹下的
sys.py 文件呢? 虽然几乎不会有人会起这样一个与标准库产生冲突的文件名,
但也不得不承认, 这种 import 方式是会产生混淆的. absolute_import 绝对引用即是为了解决这个问题.
还是用一个简单的例子试一下. 创建一个自定义的 package, 结构如下:
package
|-- __init__.py
|-- main.py
`-- sys.py
其中两个 Python 文件的内容都很简单:
# filename: sys.py
print 'Just imported a custom sys module.'
# filename: main.py
# from \_\_future\_\_ import absolute_import
import sys
然后运行 main.py:
$ python -m package.main
Just imported custom sys module.
可以看到实际 import 的是自定义的 sys.py. 而当取消 main.py 中第一行的注释后再次执行
$ python -m package.main
$
并没有输出任何内容, 说明导入的是标准库中的 sys 模块.
那么, 使用 absolute_import 后, 怎么样在 main.py 中导入自定义的 sys.py 呢? 有两种方法:
from . import sys
, 这种引用方法就是所谓的相对引用(relative import), 在之前这种引用方法是被强烈不建议使用的, 但现在相对宽容一点, 因为相对引用还是有适合使用的场景, 当 package 更名时, 不需要大量修改代码.from package import sys
, 这种引用方法是新的绝对引用, 即 import 路径必须以 sys.path 里的路径为根路径开始寻找.
5. with_statement
这个是让 Python 2.6 中正式加入的 with 语法在 2.5 中也能够使用. with 语法的作用就是可以让对象自动完成一些初始化和清理工作. PEP 343 中已经有很直白的解释了.
with EXPR as VAR:
BLOCK
相当于
VAR = EXPR
VAR.__enter__()
try:
BLOCK
finally:
VAR.__exit__()
也就是在 BLOCK 执行前后分别自动执行了 __enter__ 和 __exit__ 方法.
另外, 标准库中的 contextlib 模块提供了 contextmanager 装饰器, 能够让函数也支持 with 语法.
6. print_function
将 print 语句变为 Python 3 中的函数形式. 这个没有什么好说的, 不过将 print
语句改为函数形式调用这一点很赞, 因为 print 语句的语法很奇怪, 有时候很容易出问题.
比如需要在 print 后不换行, 就要在 print 语句后加一个逗号 (print 'one',; print 'line'
);
而使用 print 函数就可以很明确地使用 end 参数来设置 (print('one', end=' '); print('line')
).
7. unicode_literals
同样是为了与 Python 3 的语法兼容而产生. 在 Python 2 中, 字符串默认为 str, 而在字符串前加 u 才会是 unicode; 而在 Python 3 中, 字符串默认为 unicode, 而在字符串前加 b 才会是 str. unicode_literals 作用就是在 Python 2 中使用 Python 3 的这种风格.
小小吐槽一句, 在 Python 2 中 bytes is str
返回的是 True! bytes 类型就是 str…