Python __future__ 模塊

在Python2.7代碼中經(jīng)常能看到使用__future__模塊丈秩。那么__future__到底是做什么的呢渡处?

簡介

從單詞含義上猜應該是“未來”的模塊穷当。它有下面幾個目的

  1. 避免和現(xiàn)有分析import工具混淆磨取,并得到你期望的模塊
  2. 確保2.1之前的版本導入__future__產(chǎn)生運行時異常,因為2.1之前沒有這個模塊
  3. 文檔化不兼容的改變畔乙,通常這些改變會在新版中強制執(zhí)行。這類文檔以可執(zhí)行的形式組織翩概,通過導入__future__進行可編程式的檢查牲距。

以上是對官方解釋的粗略翻譯,翻譯起來感覺有些拗口钥庇。我是這么理解的牍鞠,某個版本中出現(xiàn)了某個新的功能特性,而且這個特性和當前版本中使用的不兼容评姨,也就是它在該版本中不是語言標準难述,那么我如果想要使用的話就需要從__future__模塊導入。在2.1版本之前并沒有__future__吐句,所以使用它會引發(fā)異常胁后。當然,在以后的某個版本中嗦枢,比如說3中攀芯,某個特性已經(jīng)成為標準的一部分,那么使用該特性就不用從__future__導入了文虏。

下面說一下__future__是如何實現(xiàn)新特性的侣诺。

_Feature類

__future__.py中有形如下面的語句:

FeatureName = _Feature(OptionalRelease, MandatoryRelease, CompilerFlag)

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))

OptionalRelease參數(shù)

通常OptionalRelease版本小于MandatoryRelease,每個都是5個元素的元組氧秘,類似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
)

# 例如: (2, 1, 0, "alpha", 3)表示2.1.0a3版

OptionalRelease版本中開始通過下列方式使用某個特性:

from __future__ import FeatureName

MandatoryRelease參數(shù)

在MandatoryRelease版本中該特性變成Python標準的一部分。此外MandatoryRelease版本后不需要上面的導入語句就能使用該特性丸相。MandatoryRelease可能是None搔确,表示一個計劃中的特性被放棄了。

CompilerFlag參數(shù)

CompilerFlag編譯器標志,它是一個位域標志妥箕,傳給內(nèi)建函數(shù)compile()做第四個參數(shù)滥酥,用來在動態(tài)編譯代碼的時候允許新的特性。

CompilerFlag值等價于Include/compile.h的預定義的CO_xxx標志畦幢。

Python2 __future__模塊的features

一共是以下7種坎吻,其對應的CompilerFlag:

all_feature_names = [
    "nested_scopes",
    "generators",
    "division",
    "absolute_import",
    "with_statement",
    "print_function",
    "unicode_literals",
]

CO_NESTED            = 0x0010   # nested_scopes
CO_GENERATOR_ALLOWED = 0        # generators (obsolete, was 0x1000)
CO_FUTURE_DIVISION   = 0x2000   # division
CO_FUTURE_ABSOLUTE_IMPORT = 0x4000 # perform absolute imports by default
CO_FUTURE_WITH_STATEMENT  = 0x8000   # with statement
CO_FUTURE_PRINT_FUNCTION  = 0x10000   # print function
CO_FUTURE_UNICODE_LITERALS = 0x20000 # unicode string literals

nested_scopes

nested_scopes = _Feature((2, 1, 0, "beta",  1),
                         (2, 2, 0, "alpha", 0),
                         CO_NESTED)

nested_scopes指的是嵌套作用域,2.1.0b1中出現(xiàn)宇葱,2.2.0a0中成為標準瘦真。

提到作用域,那么就不得不說命名空間黍瞧。

命名空間的定義

Python命名空間是名稱到對象的映射诸尽,目前是用字典實現(xiàn),鍵名是變量名印颤,值是變量的值您机。比如:

>>> x = 3
>>> globals()
{'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', '__doc__': None, 'x': 3, '__package__': None}

可以看到變量x,3以字典的形式存放在globals空間內(nèi)年局。以之對應的名稱空間還有:locals()际看。

>>> locals()
{'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', '__doc__': None, 'x': 3, '__package__'

實際上,你可以通過向名字添加鍵名和值矢否,然后就可以直接使用名稱了:

>>> globals()['y'] = 5
>>> y
5

通常仲闽,我們用屬性來稱呼'.'點號之后的名稱為屬性。比如僵朗,在z.real中赖欣,real是z的一個屬性。嚴格來說验庙,模塊中的名稱引用就是屬性引用顶吮。modname.funcname中,modname是一個模塊對象壶谒,而funcname是它的一個屬性云矫。模塊屬性和全局名稱有映射關(guān)系,它們共享全局命名空間汗菜。上面代碼中的x和y都是模塊main的屬性让禀。

屬性可能是只讀的,也可能是可寫的陨界。模塊屬性是可寫的巡揍,你可以這么做,modname.the_answer = 42菌瘪∪校可寫的屬性能夠用del語句來刪除阱当。比如,del modname.the_answer將會從模塊modname中刪除the_answer屬性糜工。

>>> x = 3
>>> globals()
{'__builtins__': <module '__builtin__' (built-in)>, '__package__': None, 'func': <function func at 0x029FA270>, 'x': 3, '__name__': '__main__', '__doc__': None}
>>> del x
>>> globals()
{'__builtins__': <module '__builtin__' (built-in)>, '__package__': None, 'func': <function func at 0x029FA270>, '__name__': '__main__', '__doc__': None}

del做的事實際上是刪除了全局名稱字典中的x鍵值弊添。

命名空間的種類

Python中有三種命名空間:

a) 局部,函數(shù)內(nèi)的命名空間就是局部的捌木,它記錄了函數(shù)的變量油坝,包括函數(shù)的參數(shù)和局部定義的變量。

b) 全局刨裆,模塊內(nèi)的命名空間就是全局的澈圈,它記錄了模塊的變量,包括函數(shù)帆啃、類瞬女、其它導入的模塊、模塊級的變量和常量努潘。

c) 內(nèi)置诽偷,包括異常類型、內(nèi)建函數(shù)和特殊方法慈俯,可以代碼中任意地方調(diào)用渤刃;

上面提到的globals是全局命名空間,locals是局部命名空間贴膘。

命名空間會在不同的時間創(chuàng)建,并有不同的生命周期略号。包含內(nèi)置名稱的命名空間是在python解釋器啟動的時候創(chuàng)建的刑峡,并且不會被刪除。一個模塊的全局命名空間是在模塊定義代碼被讀入的時候創(chuàng)建的玄柠,一般情況下突梦,模塊命名空間會持續(xù)到解釋器結(jié)束。在解釋器最上層調(diào)用的代碼羽利,不管是從腳本中讀入的還是在交互式界面中宫患,都會被認為是屬于一個叫做main模塊的,所以它們有自己的全局命名空間。(內(nèi)置名稱實際上也放置在一個模塊中这弧,稱為builtins)娃闲。

一個函數(shù)的局部命名空間在函數(shù)被調(diào)用的時候創(chuàng)建,在函數(shù)返回或者引發(fā)一個不在函數(shù)內(nèi)部處理的異常時被刪除匾浪。(實際上用遺忘來描述這個刪除比較好皇帮。)當然了,遞歸調(diào)用的函數(shù)每個都有它們自己的命名空間蛋辈。

命名空間的可見性(作用域)

作用域是一個Python程序中命名空間直接可見的代碼區(qū)域属拾,也就是說這個區(qū)域內(nèi)可以直接使用命名空間內(nèi)的名稱。

a) 內(nèi)置命名空間在代碼所有位置都是可見的,所以可以隨時被調(diào)用渐白;

b) 全局命名空間和局部命名空間中尊浓, 如果有同名變量,在全局命名空間處纯衍,局部命名空間內(nèi)的同名變量是不可見的栋齿;

c) 在局部命名空間處,全局命名空間的同名變量是不可見的(只有變量不同名的情況下托酸,可使用 global關(guān)鍵字讓其可見)褒颈。

命名空間的查找順序

a) 如果在函數(shù)內(nèi)調(diào)用一個變量,先在函數(shù)內(nèi)(局部命名空間)查找励堡,如果找到則停止查找谷丸。否則在函數(shù)外部(全局命名空間)查找,如果還是沒找到应结,則查找內(nèi)置命名空間刨疼。如果以上三個命名都未找到,則拋出NameError 的異常錯誤鹅龄。

b) 如果在函數(shù)外調(diào)用一個變量揩慕,則在函數(shù)外查找(全局命名空間,局部命名空間此時不可見)扮休,如果找到則停止查找迎卤,否則到內(nèi)置命名空間中查找。如果兩者都找不到玷坠,則拋出異常蜗搔。只有當局部命名空間內(nèi),使用global 關(guān)鍵字聲明了一個變量時八堡,查找順序則是 a) 的查找順序樟凄。

nested_scopes說明

Python2.2引入了一種略有不同但重要的改變,它會影響命名空間的搜索順序:嵌套的作用域兄渺。在2.0中缝龄,當你在一個嵌套函數(shù)或 lambda 函數(shù)中引用一個變量時,Python會在當前(嵌套的或 lambda)函數(shù)的名稱空間中搜索挂谍,然后在模塊的名稱空間叔壤。2.2將支在當前(嵌套的或 lambda)函數(shù)的名稱空間中搜索,然后是在父函數(shù)的名稱空間凳兵,接著是模塊的名稱空間百新。2.1可以兩種方式工作,缺省地庐扫,按n2.0的方式工作饭望,如果想像2.2中那樣工作仗哨,使用下面的導入語句:

from __future__ import nested_scopes

當然現(xiàn)在一般都用2.7或者3了,所以已經(jīng)是嵌套作用域了铅辞。

來看下面一段代碼:

>>> x = 3
>>> globals()
{'__builtins__': <module '__builtin__' (built-in)>, '__package__': None, 'func': <function func at 0x028BA2B0>, 'x': 3, '__name__': '__main__', '__doc__': None}
>>> def func():
        x = 2
          print locals()

>>> func()
{'x': 2}
>>> globals()
{'__builtins__': <module '__builtin__' (built-in)>, '__package__': None, 'func': <function func at 0x028BA2B0>, 'x': 3, '__name__': '__main__', '__doc__': None}

全局命名空間中x的值前后并沒有改變厌漂,反而在func函數(shù)的局部命名空間中產(chǎn)生了一個新的名稱x。由此可以看出斟珊,外層作用域的命名空間對于內(nèi)層來說是只讀的苇倡,當寫一個同名的名稱時,只會在內(nèi)層生成一個新的名稱囤踩。但是如果一個名稱被聲明為global旨椒,對其引用和復制都會直接作用域全局名稱。

>>> x = 2
>>> def func():
        global x
          x = 3
          print locals()

>>> globals()
{'__builtins__': <module '__builtin__' (built-in)>, '__package__': None, 'func': <function func at 0x029FA270>, 'x': 2, '__name__': '__main__', '__doc__': None
>>> func()
{}
>>> globals()
{'__builtins__': <module '__builtin__' (built-in)>, '__package__': None, 'func': <function func at 0x029FA270>, 'x': 3, '__name__': '__main__', '__doc__': None}

x的值前后改變了堵漱,而且func函數(shù)中也沒用增加x综慎。

import module和from module import func

import module將模塊自身導入到當前命名空間,所以如果要使用module的某個函數(shù)或?qū)傩郧诼荒躮odule.func這么用示惊。

而使用from module import func,則是將函數(shù)func導入當前的名稱空間愉镰,這時候使用這個函數(shù)就不需要模塊名稱而是直接使用func米罚。

我們通過一段代碼來描述:

>>> globals()
{'__builtins__': <module '__builtin__' (built-in)>, '__package__': None, '__name__': '__main__', '__doc__': None}
>>> import os
>>> globals()
{'__builtins__': <module '__builtin__' (built-in)>, '__package__': None, '__name__': '__main__', 'os': <module 'os' from 'C:\Python27\lib\os.pyc'>, '__doc__': None}
>>> del os
>>> from os import sys
>>> globals()
{'__builtins__': <module '__builtin__' (built-in)>, '__package__': None, 'sys': <module 'sys' (built-in)>, '__name__': '__main__', '__doc__': None}

是不是很清晰,擔任sys也是一個模塊丈探,如果要使用sys模塊的屬性录择,也必須要使用sys模塊名了。這也是嵌套作用域的一個例子碗降。

額糊肠。。遗锣。貌似本文的正題是__future__,哈哈嗤形,扯遠了精偿,我們繼續(xù)來看下面一個feauture。

generators

generators = _Feature((2, 2, 0, "alpha", 1),
                      (2, 3, 0, "final", 0),
                      CO_GENERATOR_ALLOWED)

generators生成器起于2.2.0a1版赋兵,在2.3.0f0中成為標準笔咽。

簡介

生成器是這樣一個函數(shù),它記住上一次返回時在函數(shù)體中的位置霹期。對生成器函數(shù)的第二次(或第 n 次)調(diào)用跳轉(zhuǎn)至該函數(shù)中間叶组,而上次調(diào)用的所有局部變量都保持不變。Python中yeild就是一個生成器历造。

yield 生成器的運行機制

當你問生成器要一個數(shù)時甩十,生成器會執(zhí)行船庇,直至出現(xiàn) yield 語句,生成器把 yield 的參數(shù)給你侣监,之后生成器就不會往下繼續(xù)運行鸭轮。 當你問他要下一個數(shù)時,他會從上次的狀態(tài)橄霉。開始運行窃爷,直至出現(xiàn)yield語句,把參數(shù)給你姓蜂,之后停下按厘。如此反復直至退出函數(shù)。

示例

>>> def my_generator():
          yield 1
          yield 2
          yield 3

>>> gen = my_generator()
>>> gen.next()
1
>>> gen.next()
2
>>> gen.next()
3
>>> gen.next()

Traceback (most recent call last):
  File "<pyshell#92>", line 1, in <module>
    gen.next()
StopIteration
>>> for n in my_generator:
        print n
1
2
3

yield在被next調(diào)用之前并沒有執(zhí)行(for循環(huán)內(nèi)部也是使用next)钱慢,在執(zhí)行完最后一個yield之后再繼續(xù)調(diào)用next逮京,那么就好遇到StopIteration異常了。這里涉及到迭代器了滩字,不再進行詳細的描述了造虏,后面會單開一章來講Python的三大器:迭代器、生成器麦箍、裝飾器漓藕。

division

division = _Feature((2, 2, 0, "alpha", 2),
                    (3, 0, 0, "alpha", 0),
                    CO_FUTURE_DIVISION)

這個很簡單,舉例說明一下大家就懂了挟裂。

# python2.7中享钞,不導入__future__
>>> 10/3
3

# 導入__future__
>>> from __future__ import division
>>> 10/3
3.3333333333333335

很容易看出來,2.7中默認的整數(shù)除法是結(jié)果向下取整诀蓉,而導入了__future__之后除法就是真正的除法了栗竖。這也是python2和python3的一個重要區(qū)別。

absolute_import

absolute_import = _Feature((2, 5, 0, "alpha", 1),
                           (3, 0, 0, "alpha", 0),
                           CO_FUTURE_ABSOLUTE_IMPORT)

python2.7中渠啤,在默認情況下狐肢,導入模塊是相對導入的(relative import),比如說

from . import json
from .json import json_dump

這些以'.'點導入的是相對導入沥曹,而絕對導入(absolute import)則是指從系統(tǒng)路徑sys.path最底層的模塊導入份名。比如:

import os
from os import sys

with_statement

with_statement = _Feature((2, 5, 0, "alpha", 1),
                          (2, 6, 0, "alpha", 0),
                          CO_FUTURE_WITH_STATEMENT)

with語句也不詳細講了,看這篇淺談Python的with語句

print_function

print_function = _Feature((2, 6, 0, "alpha", 2),
                          (3, 0, 0, "alpha", 0),
                          CO_FUTURE_PRINT_FUNCTION)

這個就是最經(jīng)典的python2和python3的區(qū)別了妓美,python2中print不需要括號僵腺,而在python3中則需要。

# python2.7
print "Hello world"

# python3
print("Hello world")

unicode_literals

unicode_literals = _Feature((2, 6, 0, "alpha", 2),
                            (3, 0, 0, "alpha", 0),
                            CO_FUTURE_UNICODE_LITERALS)

這是unicode的問題壶栋,講起來又是長篇大論了辰如,容我偷個懶,后面再講吧贵试。

至此琉兜,__future__模塊的幾個特性凯正,算是說完了。好多內(nèi)容都是參照官方文檔呕童,所以大家還是多看文檔吧漆际。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市夺饲,隨后出現(xiàn)的幾起案子奸汇,更是在濱河造成了極大的恐慌,老刑警劉巖往声,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件擂找,死亡現(xiàn)場離奇詭異,居然都是意外死亡浩销,警方通過查閱死者的電腦和手機贯涎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來慢洋,“玉大人塘雳,你說我怎么就攤上這事∑粘铮” “怎么了败明?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長太防。 經(jīng)常有香客問我妻顶,道長,這世上最難降的妖魔是什么蜒车? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任讳嘱,我火速辦了婚禮,結(jié)果婚禮上酿愧,老公的妹妹穿的比我還像新娘沥潭。我一直安慰自己,他們只是感情好嬉挡,可當我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布叛氨。 她就那樣靜靜地躺著,像睡著了一般棘伴。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上屁置,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天焊夸,我揣著相機與錄音,去河邊找鬼蓝角。 笑死阱穗,一個胖子當著我的面吹牛饭冬,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播揪阶,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼昌抠,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了鲁僚?” 一聲冷哼從身側(cè)響起炊苫,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎冰沙,沒想到半個月后侨艾,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡拓挥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年唠梨,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片侥啤。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡当叭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出盖灸,到底是詐尸還是另有隱情蚁鳖,我是刑警寧澤,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布糠雨,位于F島的核電站才睹,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏甘邀。R本人自食惡果不足惜琅攘,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望松邪。 院中可真熱鬧坞琴,春花似錦、人聲如沸逗抑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽荧关。三九已至,卻和暖如春褂傀,著一層夾襖步出監(jiān)牢的瞬間忍啤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工仙辟, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留同波,地道東北人鳄梅。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像未檩,于是被迫代替她去往敵國和親戴尸。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內(nèi)容