在Python2.7代碼中經(jīng)常能看到使用__future__模塊丈秩。那么__future__到底是做什么的呢渡处?
簡介
從單詞含義上猜應該是“未來”的模塊穷当。它有下面幾個目的:
- 避免和現(xiàn)有分析import工具混淆磨取,并得到你期望的模塊
- 確保2.1之前的版本導入__future__產(chǎn)生運行時異常,因為2.1之前沒有這個模塊
- 文檔化不兼容的改變畔乙,通常這些改變會在新版中強制執(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)容都是參照官方文檔呕童,所以大家還是多看文檔吧漆际。