Python中__future__包说明

目录

  • 背景

  • 第一部分 源起

  • 第二部分 原理

  • 第三部分 总结

  • 参考文献及资料

背景

在阅读开源项目源码的时候,经常会有下面的代码片段:

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.8Anconda版本)为例,下面是__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.7Python 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.8future中出现的特性清单,如下:

特性预加入版本强制生效版本效果
nested_scopes2.1.1 beta 12.2.0 alpha 0PEP 227: Statically Nested Scopes
generators2.2.0 alpha 12.3.0 final 0PEP 255: Simple Generators
division2.2.0 alpha 23.0.0 beta 0PEP 238: Changing the Division Operator
absolute_import2.5.0 alpha 13.0.0 alpha 0PEP 328: Imports: Multi-Line and Absolute/Relative
with_statement2.5.0 alpha 12.6.0 alpha 0PEP 343: The “with” Statement
print_function2.6.0 alpha 23.0.0 alpha 0PEP 3105: Make print a function
unicode_literals2.6.0 alpha 23.0.0 alpha 0PEP 3112: Bytes literals in Python 3000
barry_as_FLUFL3.1.0 alpha 24.0.0 alpha 0PEP 401: BDFL Retirement
generator_stop3.5.0 beta 13.7.0 alpha 0PEP 479: StopIteration handling inside generators
annotations3.7.0 beta 13.10.0 alpha 0PEP 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

更多关注公众号:
在这里插入图片描述

  • 8
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值