目录
-
背景
-
第一部分 源起
-
第二部分 原理
-
第三部分 总结
-
参考文献及资料
背景
在阅读开源项目源码的时候,经常会有下面的代码片段:
from __future__ import print_function
或者:
from __future__ import annotations
那么__future__
包的作用是啥。其实自己写代码的时候很少会用到它(正常人写代码谁用这呀)。也许你会查一下官网介绍,苦涩难懂,然后又查了别人写的介绍材料,仍然是难以理解。遂放弃。
那我来试图教会您。
本文验证环境说明:
- 验证环境都是
CPython
编译器;
第一部分 源起
1.1 兼容问题
我们知道Python
语言演化过程中并没有很好的去兼容低版本的语法(真任性,其他语言可周到呢)。最常见的例子就是print
,在Python 2
中一些编译器版本中,语法是:
print "test"
而在Python 3
版本中,语法是:
print("test")
这种差异就会导致新的代码(python 3
)在旧的编译器(python 2.6.1
)上无法运行。
通常由于版本更新代价较大,所以生产环境的
Python
编译器版本更新往往是滞后的。生产环境嘛,稳定是头等大事。但是咱们又想玩点新东西,提升性能,使用的新版本的特性,但是不能再旧版本上运行。
为了解决这个问题,使得新的代码能安全的(用极少的代价)在旧的版本上编译运行,就引入了__future__
包。
最初的PEP提议:https://peps.python.org/pep-0236/
1.2 解决案例
那么如何使用__future__
包呢?例如下面的代码,在Python 3
上能顺利执行:
if __name__ == '__main__':
print("test")
那么如果需要再Python 2.6.1
上运行,那么就需要在代码中添加下面的内容:
from __future__ import print_function
if __name__ == '__main__':
print("test")
这时候代码也能顺利编译解释运行。
第二部分 原理
那么自然有几个问题:
__future__
的实现原理是啥?- 为什么新的语法特性能在旧的版本编译器上执行了,难道旧的编译器穿越到未来了?
2.1 案例说明
对于第二个问题,其实并不是旧版本的编译器穿越了,而是旧版本的编译器本身已经具备了解释新语法特性的能力,只不过这个能力有个开关,默认是关闭的。
而__future__
包的功能本质是开关,就是将被关闭封印的功能进行解封。
2.2 源码
以Python 3.8.8
(Anconda
版本)为例,下面是__future__.py
文件的源码。
"""Record of phased-in incompatible language changes.
Each line is of the form:
FeatureName = "_Feature(" OptionalRelease "," MandatoryRelease ","
CompilerFlag ")"
where, normally, OptionalRelease < MandatoryRelease, and both are 5-tuples
of the same form as sys.version_info:
(PY_MAJOR_VERSION, # the 2 in 2.1.0a3; an int
PY_MINOR_VERSION, # the 1; an int
PY_MICRO_VERSION, # the 0; an int
PY_RELEASE_LEVEL, # "alpha", "beta", "candidate" or "final"; string
PY_RELEASE_SERIAL # the 3; an int
)
OptionalRelease records the first release in which
from __future__ import FeatureName
was accepted.
In the case of MandatoryReleases that have not yet occurred,
MandatoryRelease predicts the release in which the feature will become part
of the language.
Else MandatoryRelease records when the feature became part of the language;
in releases at or after that, modules no longer need
from __future__ import FeatureName
to use the feature in question, but may continue to use such imports.
MandatoryRelease may also be None, meaning that a planned feature got
dropped.
Instances of class _Feature have two corresponding methods,
.getOptionalRelease() and .getMandatoryRelease().
CompilerFlag is the (bitfield) flag that should be passed in the fourth
argument to the builtin function compile() to enable the feature in
dynamically compiled code. This flag is stored in the .compiler_flag
attribute on _Future instances. These values must match the appropriate
#defines of CO_xxx flags in Include/compile.h.
No feature line is ever to be deleted from this file.
"""
all_feature_names = [
"nested_scopes",
"generators",
"division",
"absolute_import",
"with_statement",
"print_function",
"unicode_literals",
"barry_as_FLUFL",
"generator_stop",
"annotations",
]
__all__ = ["all_feature_names"] + all_feature_names
# The CO_xxx symbols are defined here under the same names defined in
# code.h and used by compile.h, so that an editor search will find them here.
# However, they're not exported in __all__, because they don't really belong to
# this module.
CO_NESTED = 0x0010 # nested_scopes
CO_GENERATOR_ALLOWED = 0 # generators (obsolete, was 0x1000)
CO_FUTURE_DIVISION = 0x20000 # division
CO_FUTURE_ABSOLUTE_IMPORT = 0x40000 # perform absolute imports by default
CO_FUTURE_WITH_STATEMENT = 0x80000 # with statement
CO_FUTURE_PRINT_FUNCTION = 0x100000 # print function
CO_FUTURE_UNICODE_LITERALS = 0x200000 # unicode string literals
CO_FUTURE_BARRY_AS_BDFL = 0x400000
CO_FUTURE_GENERATOR_STOP = 0x800000 # StopIteration becomes RuntimeError in generators
CO_FUTURE_ANNOTATIONS = 0x1000000 # annotations become strings at runtime
class _Feature:
def __init__(self, optionalRelease, mandatoryRelease, compiler_flag):
self.optional = optionalRelease
self.mandatory = mandatoryRelease
self.compiler_flag = compiler_flag
def getOptionalRelease(self):
"""Return first release in which this feature was recognized.
This is a 5-tuple, of the same form as sys.version_info.
"""
return self.optional
def getMandatoryRelease(self):
"""Return release in which this feature will become mandatory.
This is a 5-tuple, of the same form as sys.version_info, or, if
the feature was dropped, is None.
"""
return self.mandatory
def __repr__(self):
return "_Feature" + repr((self.optional,
self.mandatory,
self.compiler_flag))
nested_scopes = _Feature((2, 1, 0, "beta", 1),
(2, 2, 0, "alpha", 0),
CO_NESTED)
generators = _Feature((2, 2, 0, "alpha", 1),
(2, 3, 0, "final", 0),
CO_GENERATOR_ALLOWED)
division = _Feature((2, 2, 0, "alpha", 2),
(3, 0, 0, "alpha", 0),
CO_FUTURE_DIVISION)
absolute_import = _Feature((2, 5, 0, "alpha", 1),
(3, 0, 0, "alpha", 0),
CO_FUTURE_ABSOLUTE_IMPORT)
with_statement = _Feature((2, 5, 0, "alpha", 1),
(2, 6, 0, "alpha", 0),
CO_FUTURE_WITH_STATEMENT)
print_function = _Feature((2, 6, 0, "alpha", 2),
(3, 0, 0, "alpha", 0),
CO_FUTURE_PRINT_FUNCTION)
unicode_literals = _Feature((2, 6, 0, "alpha", 2),
(3, 0, 0, "alpha", 0),
CO_FUTURE_UNICODE_LITERALS)
barry_as_FLUFL = _Feature((3, 1, 0, "alpha", 2),
(4, 0, 0, "alpha", 0),
CO_FUTURE_BARRY_AS_BDFL)
generator_stop = _Feature((3, 5, 0, "beta", 1),
(3, 7, 0, "alpha", 0),
CO_FUTURE_GENERATOR_STOP)
annotations = _Feature((3, 7, 0, "beta", 1),
(3, 10, 0, "alpha", 0),
CO_FUTURE_ANNOTATIONS)
代码中定义了_Feature
类,有三个初始化参数:
-
optionalRelease
表示该特性实际加入的编译器版本,特性首次发布版本。
-
mandatoryRelease
表示该特性默认被应用的版本。从该版本往后,使用该特性将不需要
future
语句。 -
compiler_flag
二进制的码,真实传给编译器的参数,作为特性开关。例如
print_function
特性对于的参数为:CO_FUTURE_PRINT_FUNCTION = 0x100000
。最后将其作为第4个参数传给内置函数
compile()
以启用相应的特性。
说完了源码,再来看之前的案例。对于print_function
特性,在源码有定义:
print_function = _Feature((2, 6, 0, "alpha", 2),
(3, 0, 0, "alpha", 0),
CO_FUTURE_PRINT_FUNCTION)
其中:
optionalRelease=(2, 6, 0, "alpha", 2)
,说明该特性已经在Python
版本2.6.0 a2
版本。但是默认未生效。mandatoryRelease=(3, 0, 0, "alpha", 0)
,表示该特性在Python
版本3.0.0 a0
版本正式生效。
2.3 常见特性
另外还有其他的特性,常见的主要有:
-
精确除法
当没有在程序中导入该特征时,"/“操作符执行的是截断除法(
Truncating Division
),即整除。导入精确除法之后,”/"执行的是精确除法,如下所示:from __future__ import division if __name__ == '__main__': print(1/3) # 0.3333333333333333
-
with 语句
# python 2.x try: with open('test.csv', 'w') as f: f.write('test') finally: f.close() # 用with替代上述异常检测代码: from __future__ import with_statement with open('test.csv', 'w') as f: f.write('test')
-
print 语句
不再赘述。
-
字符串
from __future__ import unicode_literals
从
Python 2.7
到Python 3.x
就有不兼容的一些改动,比如2.x
里的字符串用'xxx'
表示str,Unicode字符串用u'xxx'
表示unicode
,而在3.x
中,所有字符串都被视为unicode
,因此,写u'xxx'
和'xxx'
是完全一致的,而在2.x
中以'xxx'
表示的str就必须写成b'xxx'
,以此表示“二进制字符串”。
2.4 全部特性
截止于Python 3.8.8
,future
中出现的特性清单,如下:
特性 | 预加入版本 | 强制生效版本 | 效果 |
---|---|---|---|
nested_scopes | 2.1.1 beta 1 | 2.2.0 alpha 0 | PEP 227: Statically Nested Scopes |
generators | 2.2.0 alpha 1 | 2.3.0 final 0 | PEP 255: Simple Generators |
division | 2.2.0 alpha 2 | 3.0.0 beta 0 | PEP 238: Changing the Division Operator |
absolute_import | 2.5.0 alpha 1 | 3.0.0 alpha 0 | PEP 328: Imports: Multi-Line and Absolute/Relative |
with_statement | 2.5.0 alpha 1 | 2.6.0 alpha 0 | PEP 343: The “with” Statement |
print_function | 2.6.0 alpha 2 | 3.0.0 alpha 0 | PEP 3105: Make print a function |
unicode_literals | 2.6.0 alpha 2 | 3.0.0 alpha 0 | PEP 3112: Bytes literals in Python 3000 |
barry_as_FLUFL | 3.1.0 alpha 2 | 4.0.0 alpha 0 | PEP 401: BDFL Retirement |
generator_stop | 3.5.0 beta 1 | 3.7.0 alpha 0 | PEP 479: StopIteration handling inside generators |
annotations | 3.7.0 beta 1 | 3.10.0 alpha 0 | PEP 563: Postponed evaluation of annotations |
第三部分 总结
相信现在应该明白了__future__
包的原理了。使用特性的时候,需要检查新旧解释器的兼容范围。
另外对于Python
版本的兼容性,还有个小工具2to3
,可以把Python2
语法代码转换成Python3
语法。命令如下:
# 2to3 example.py
但是将在 Python 3.13
中被移除。Python 2
即将逐步被3取代,成为历史。
参考文献及资料
1、官方文档:https://docs.python.org/zh-cn/3/library/future.html
更多关注公众号: