Python2與Python3兼容
python3寫的代碼如何也能在pyhon2上跑?請無論如何加上這一句,python3沒有啥影響
from __future__ import absolute_import, unicode_literals, division, print_function
__future__模塊提供某些將要引入的特性,python 2.7.5的__future__基本上是python3中的特性
開始
使用我們的jupyter交互式工具進行探討,以下皆為python2
有以下內容
In [1]: import __future__
In [2]: __future__.
__future__.CO_FUTURE_ABSOLUTE_IMPORT __future__.all_feature_names
__future__.CO_FUTURE_DIVISION __future__.division
__future__.CO_FUTURE_PRINT_FUNCTION __future__.generators
__future__.CO_FUTURE_UNICODE_LITERALS __future__.nested_scopes
__future__.CO_FUTURE_WITH_STATEMENT __future__.print_function
__future__.CO_GENERATOR_ALLOWED __future__.unicode_literals
__future__.CO_NESTED __future__.with_statement
__future__.absolute_import
In [2]: __future__.
可導入的功能有哪些?
In [3]: import __future__
In [4]: __future__.all_feature_names
Out[4]:
['nested_scopes',
'generators',
'division',
'absolute_import',
'with_statement',
'print_function',
'unicode_literals']
對應功能如下
division
division 新的除法特性,本來的除號/對於分子分母是整數的情況會取整,但新特性中在此情況下的除法不會取整,取整的使用//。如下可見,只有分子分母都是整數時結果不同。
In [1]: 3 / 5
Out[1]: 0
In [2]: 3 // 5
Out[2]: 0
In [3]: 3.0 / 5.0
Out[3]: 0.6
In [4]: 3.0 // 5.0
Out[4]: 0.0
In [5]: from __future__ import division
In [6]: 3 / 5
Out[6]: 0.6
In [7]: 3 // 5
Out[7]: 0
In [8]: 3.0 / 5.0
Out[8]: 0.6
In [9]: 3.0 // 5.0
Out[9]: 0.0
print_function
print_function 新的print是一個函數,如果導入此特性,之前的print語句就不能用了。
In [1]: print 'test __future__'
test __future__
In [2]: from __future__ import print_function
In [3]: print('test')
test
In [4]: print 'test'
File "<ipython-input-4-ed4b06bfff9f>", line 1
print 'test'
^
SyntaxError: invalid syntax
unicode_literals
unicode_literals 這個是對字符串使用unicode字符
In [1]: print '目錄'
鐩綍
In [2]: from __future__ import unicode_literals
In [3]: print '目錄'
目錄
在python 2.x中, 對於漢字字符串, 默認還不是采用unicode編碼的, 除非在字符串前加上前綴u. 比如:
x='中國'
x
'\xd6\xd0\xb9\xfa' 這不是unicode編碼
print(x)
中國
x=u'中國'
u'\u4e2d\u56fd'
print(x)
中國
在python3中默認的編碼采用了unicode, 並取消了前綴u. 如果代碼要兼容python2/3, 就很麻煩了. 下面的兩個選擇都不方便:
- 字符串前面不加
u. 這種處理方式多數情況下沒有問題, 比如print輸出, 但因為漢字在py2和py3的編碼方式不一樣, 如果進行編碼轉換就麻煩了. - 加
python版本判斷,if sys.version < '3', 字符串不加前綴u, 如果是py2, 加上前綴u. 這樣代碼顯得很拖沓. - 現在有第3種, 比較好的方法是引入
unicode_literals, from __future__ import unicode_literals, 這樣在py2下,'中國'這樣的字符串不用家前綴u, 也是unicode編碼.
absolute_import
字面理解好像是僅僅允許絕對引用, 其實不然, 真實意思是禁用隱式相對引用:implicit relative import, 但並不會禁掉顯式相對引用:explicit relative import.
舉個例子, 目錄結構如下,
-cake
|- __init__.py
|- icing.py
|- sponge.py
-drink
|- __init__.py
|- water.py
在 sponge.py 引用 icing , 有多種方法:
import icing隱式相對引用, py2已強烈不推薦使用, py3已經不可用了from . import icing顯式相對引用, python.org 官方雖不推薦, 但這卻是事實標准from cake import icing絕對引用 , python 官方推薦.
使用 __future__ absolute_import之后, 常遇到的一個問題
PackageA
|- module1.py
|- module2.py
|- __init__.py
在module1.py中,
from __future__ import absolute_impact
from . import module2 #引入同包下的另一個module
if __name__=="__main__":
print("module2 was imported in module1.")
運行會報錯, ValueError: Attempted relative import in non-package.
原因分析: from . import module2 這樣的寫法是顯式相對引用, 這種引用方式只能用於package中, 而不能用於主模塊中.
因為主module的name總是為main, 並沒有層次結構, 也就無從談起相對引用了.
換句話, if __name__=="__main__": 和相對引用是不能並存的.
解決方法:
- 在
module1中使用絕對引用, 這個最簡單了, 但相對引用的好處也沒了. - 使用
python -m來啟動你的module1.py, 這個也不推薦. - (推薦,我覺得還是和第一個差不多,只不過測試換在了另外的地方)在
module1中, 加個main()函數, 然后再新建一個PackageA/entry.py做為主程序, 在entry.py中使用絕對引用來引用module1, 並調用module1.main(), 這一辦法雖不完美, 但我覺得是最好的方法了.
nested_scopes
這個是修改嵌套函數或lambda函數中變量的搜索順序,從當前函數命名空間->模塊命名空間的順序更改為了當前函數命名空間->父函數命名空間->模塊命名空間,python2.7.5中默認使用
generators
生成器,對應yield的語法,python2.7.5中默認使用
with_statement
使用with關鍵字,python2.7.5是默認使用
運用
首先是可以做個性化的用法,比如你喜歡用print()而不是print
更重要的是基本用以下幾句就可以讓python2和python3有良好的兼容性了
from __future__ import print_function
from __future__ import unicode_literals
from __future__ import division
from __future__ import absolute_import
