和C語言一樣亮垫,python的print功能通過內(nèi)建函數(shù)實現(xiàn),而不是通過statement實現(xiàn)伟骨。這意味著饮潦,名稱print可以被重新定義,和一般函數(shù)無異携狭。
例如继蜡,我們想設置print的默認參數(shù)為sep=', ',end='.\n'逛腿,可做如下操作:
pprint = print # 保存內(nèi)建print的指針
print = lambda *objects: pprint(*objects, sep=', ', end='.\n') # 令print以所需參數(shù)調(diào)用pprint
print(1, 2) # 測試
>> 1, 2.
成功稀并。
為什么非要先把print保存在pprint里?為什么不能這樣寫:
print = lambda *object: print(*object, sep=', ', end='.\n')
print(1, 2)
畢竟单默,類似 L = L + [3]的語句都沒問題碘举。
這是怎么回事?
首先搁廓,這牽扯到函數(shù)中變量解引用的搜索順序引颈。即,local境蜕、nonlocal(若干層)蝙场、global、built-in汽摹。執(zhí)行print(1, 2)時李丰,lambda的local中沒有print,global中有print逼泣。但global空間中的print是我們自己定義的趴泌,只接受一個*object參數(shù)的print,不是built-in print拉庶。
但疑云未散嗜憔,按列表操作 L = L + [3] 的精神,名稱L替換為所引用的列表氏仗,和 [3] 拼接吉捶,結(jié)果命名為L。類似地皆尔,定義時呐舔,將lambda中的print替換為built-in print,生成的函數(shù)命名為print慷蠕,錯在何處珊拼?原因在于,函數(shù)體不在定義時執(zhí)行流炕。而解引用發(fā)生在語句執(zhí)行時澎现,也就是函數(shù)被調(diào)用的時候仅胞。調(diào)用時(不是定義時)print指什么,才決定函數(shù)執(zhí)行的效果剑辫。本例中干旧,函數(shù)執(zhí)行時名稱print已經(jīng)被自己的定義語句覆蓋,意外地產(chǎn)生了調(diào)用自身的結(jié)果妹蔽。
如此椎眯,python函數(shù)對象就像一個記錄著幾行代碼的清單,只有在調(diào)用時才會執(zhí)行所持有的代碼胳岂,代碼中解引用的操作是在調(diào)用時發(fā)生的盅视。閉包 函數(shù)工廠中介紹了一個更微妙的例子。
另外旦万,按照錯誤提示改變lambda的參數(shù)列表闹击,使之接收sep和end,也不能達到目的(會產(chǎn)生無限遞歸)成艘。原因還是赏半,此時global空間中的print遮蔽了built-in空間中的print,lambda中調(diào)用built-in中print的意圖無法實現(xiàn)淆两。
覆蓋了內(nèi)建print的print需要調(diào)用內(nèi)建print断箫,所以內(nèi)建print必須用某種方式保存下來。問題轉(zhuǎn)化為如何進行函數(shù)狀態(tài)保存秋冰,那么閉包仲义、默認參數(shù)、函數(shù)屬性剑勾、類埃撵,都是可以嘗試的方法。
函數(shù)閉包:
def makeopen():
pprint = print
return lambda *objects: pprint(*objects, sep=', ', end='.\n')
open = makeopen()
open(1, 2, 4)
>> 1, 2, 4.
其他方法請君自行探索虽另。
就這個具體問題而言暂刘,不使用內(nèi)建print函數(shù)或許是最好的方法:
import sys
def print(*args, sep=', ', end='.\n', file=sys.stdout):
file.write(sep.join(str(arg) for arg in args) + end)