為什么 Python 3 把 print 改為函數?
這篇 PEP 是關于在 Python 3 中把 print 改為函數,發布時間是 2006 年。我學 Python 時用的是 3,相信大多數讀者也是如此,但是這篇東西還沒有完全過時。
一方面,還有很多 Python 2 用戶需要了解這項內容(雖然并不難),另一方面則是更主要的,這篇 PEP 記錄了一種變遷過程,閱讀它可以知道一個函數的來龍去脈,可以知道一個設計的細節與背后考量。每個 PEP 都是多個核心開發者經過眾多次討論才確定下來的,因此是濃縮的成果,閱讀它,也許能給我們些許收益呢。
摘要
標題已說明了一切——本 PEP 提議使用新的內置函數 print() 來替代 print 語句,并建議給此新函數使用特殊的簽名(signature )。
原理闡述
print 語句 早就被列在了不可靠的語言特性列表中,例如 Guido 的“Python 之悔”(Python Regrets)演講【1】,并計劃在 Python 3000 版本移除。因此,本 PEP 的目的并不新鮮,盡管它可能會在 Python 開發人員中引起較大爭議。
以下對 print() 函數的爭議是提取自 Guido 本人的 Python-3000 消息【2】:
- print 是唯一的應用程序級功能,并擁有專屬的語句。在 Python 的世界里,當某些任務在不通過編譯器的幫助就無法完成的情況下,語法(syntax)通常會被用作最后的手段。在這種異常情況下,print 并不合適。
- 在開發應用程序的時候,人們經常需要用更復雜的東西來代替 print 輸出,例如調用 logging,或者調用其它的 I/O 庫。至于 print() 函數,這是個直截了當的字符替換,如今它混搭了所有那些括號,還可能會轉換 >>stream 樣式的語法。
- 為 print 設置特殊的語法只會給進化帶來一個更加巨大的屏障,例如這有個猜想,一個新的 printf() 函數不用多久就會出現,跟 print() 函數共存。
- 當需要一個不同的分隔符(不是空格,或者沒有分隔符)時,沒有簡單的方法可以將 print 語句轉換成另一個調用。同樣地,使用其它一些分隔符而非空格時,根本無法方便地打印對象。
- 如果 print() 是個函數,就可以非常容易地在一個模塊內替換它(僅需 def print(*args):…),甚至可以在整個程序內替換(例如放一個不同的方法進 __builtin__.print)。實際上,要做到這點,還可以寫一個帶 write() 方法的類,然后定向給 sys.stdout ,這想法不錯,但無疑是一個非常巨大的概念飛躍,而且跟 print 相比,它工作在不同的層級。
設計規格
print() 的書寫方式取自各種郵件,最近發布在 python-3000 列表里的是【3】:
- def print(*args, sep=' ', end='\n', file=None)
調用像:
- print(a, b, c, file=sys.stderr)
相當于當前的:
- print >>sys.stderr, a, b, c
可選的 sep 與 end 參數相應地指定了每個打印參數之間及之后的內容。
softspace 功能(當前在文件上的半秘密屬性,用于告訴 print 是否要在第一個條目前插入空格)會被刪除。因此,當前版本的以下寫法不能被直接轉換:
- print "a",
它不會在“a”與換行符之間打印一個空格。
(譯注:在 3.3 版本,print() 函數又做了改動,增加了默認參數 flush=False)
向后兼容性
本 PEP 中提出的改動將致使如今的 print 語句失效。只有那些恰好用括號包圍了所有參數的寫法才能在 Python 3 版本中生效,至于其它,只有加上了括號的值才能保持原樣打印。例如,在 2.x 中:
- >>> print ("Hello")
- Hello
- >>> print ("Hello", "world")
- ('Hello', 'world')
而在 3.0 中:
- >>> print ("Hello")
- Hello
- >>> print ("Hello", "world")
- Hello world
幸運的是,因為 print 是 Python 2 中的一個語句,所以它可以被通過自動化工具而檢測到,并可靠而精確地替換掉,因此應該沒有重大的移植問題(如果有人來寫這個工具的話)。
實現
更改將在 Python 3000 分支中實現(修訂版從 53685 到 53704)。大多數在維庫代碼(legacy code)已經做轉換了,但要抓出發行版本中的每個 print 語句,還需要持續不斷地努力。