Python 輸入輸出學習

參見


輸入輸出

更漂亮的輸出格式

到目前為止,我們遇到了兩種寫入值的方法:表達式語句print() 函數(shù)后室。(第三種是使用文件對象的 write() 方法;標準輸出文件可以作為 sys.stdout 引用混狠。更多相關(guān)信息可參考標準庫指南咧擂。)

通常,你需要更多地控制輸出的格式檀蹋,而不僅僅是打印空格分隔的值松申。有幾種格式化輸出的方法。

要使用 格式化字符串字面值 俯逾,請在字符串的開始引號或三引號之前加上一個 fF 贸桶。在此字符串中,你可以在 {} 字符之間寫可以引用的變量或字面值的 Python 表達式桌肴。

>>> year = 2016
>>> event = 'Referendum'
>>> f'Results of the {year} {event}'
'Results of the 2016 Referendum'

字符串的 str.format() 方法需要更多的手動操作皇筛。你仍將使用 {} 來標記變量將被替換的位置,并且可以提供詳細的格式化指令坠七,但你還需要提供要格式化的信息水醋。

>>> yes_votes = 42_572_654
>>> no_votes = 43_132_495
>>> percentage = yes_votes / (yes_votes + no_votes)
>>> '{:-9} YES votes  {:2.2%}'.format(yes_votes, percentage)
' 42572654 YES votes  49.67%'

當你不需要花哨的輸出而只是想快速顯示某些變量以進行調(diào)試時旗笔,可以使用 repr() or str() 函數(shù)將任何值轉(zhuǎn)化為字符串。

str() 函數(shù)是用于返回人類可讀的值的表示拄踪,而 repr() 是用于生成解釋器可讀的表示(如果沒有等效的語法蝇恶,則會強制執(zhí)行 SyntaxError)對于沒有人類可讀性的表示的對象, str() 將返回和 repr() 一樣的值惶桐。

幾個例子:

>>> s = 'Hello, world.'
>>> str(s)
'Hello, world.'
>>> repr(s)
"'Hello, world.'"
>>> str(1/7)
'0.14285714285714285'
>>> x = 10 * 3.25
>>> y = 200 * 200
>>> s = 'The value of x is ' + repr(x) + ', and y is ' + repr(y) + '...'
>>> print(s)
The value of x is 32.5, and y is 40000...
>>> # The repr() of a string adds string quotes and backslashes:
... hello = 'hello, world\n'
>>> hellos = repr(hello)
>>> print(hellos)
'hello, world\n'
>>> # The argument to repr() may be any Python object:
... repr((x, y, ('spam', 'eggs')))
"(32.5, 40000, ('spam', 'eggs'))"

string 模塊包含一個 Template 類撮弧,它提供了另一種將值替換為字符串的方法,使用類似 $x 的占位符并用字典中的值替換它們姚糊,但對格式的控制要少的多贿衍。

格式化字符串文字

格式化字符串字面值 (常簡稱為 f-字符串)能讓你在字符串前加上 fF 并將表達式寫成 {expression} 來在字符串中包含 Python 表達式的值。

可選的格式說明符可以跟在表達式后面救恨。這樣可以更好地控制值的格式化方式贸辈。以下示例將pi舍入到小數(shù)點后三位:

>>> import math
>>> print(f'The value of pi is approximately {math.pi:.3f}.')
The value of pi is approximately 3.142.

':' 后傳遞一個整數(shù)可以讓該字段成為最小字符寬度。這在使列對齊時很有用肠槽。:

>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 7678}
>>> for name, phone in table.items():
...     print(f'{name:10} ==> {phone:10d}')
...
Sjoerd     ==>       4127
Jack       ==>       4098
Dcab       ==>       7678

其他的修飾符可用于在格式化之前轉(zhuǎn)化值擎淤。 '!a' 應(yīng)用 ascii()'!s' 應(yīng)用 str()署浩,還有 '!r' 應(yīng)用 repr():

>>> animals = 'eels'
>>> print(f'My hovercraft is full of {animals}.')
My hovercraft is full of eels.
>>> print(f'My hovercraft is full of {animals!r}.')
My hovercraft is full of 'eels'.

有關(guān)這些格式規(guī)范的參考,請參閱參考指南 格式規(guī)格迷你語言扫尺。

字符串的 format() 方法

str.format() 方法的基本用法如下所示:

>>> print('We are the {} who say "{}!"'.format('knights', 'Ni'))
We are the knights who say "Ni!"

花括號和其中的字符(稱為格式字段)將替換為傳遞給 str.format() 方法的對象筋栋。花括號中的數(shù)字可用來表示傳遞給 str.format() 方法的對象的位置正驻。

>>> print('{0} and {1}'.format('spam', 'eggs'))
spam and eggs
>>> print('{1} and {0}'.format('spam', 'eggs'))
eggs and spam

如果在 str.format() 方法中使用關(guān)鍵字參數(shù)弊攘,則使用參數(shù)的名稱引用它們的值。:

>>> print('This {food} is {adjective}.'.format(
...       food='spam', adjective='absolutely horrible'))
This spam is absolutely horrible.

位置和關(guān)鍵字參數(shù)可以任意組合:

>>> print('The story of {0}, {1}, and {other}.'.format('Bill', 'Manfred',other='Georg'))
The story of Bill, Manfred, and Georg.

如果你有一個非常長的格式字符串姑曙,你不想把它拆開襟交,那么你最好是按名稱而不是按位置引用變量來進行格式化。 這可以通過簡單地傳遞字典并使用方括號 '[]' 訪問鍵來完成伤靠。

>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}
>>> print('Jack: {0[Jack]:d}; Sjoerd: {0[Sjoerd]:d}; '
...       'Dcab: {0[Dcab]:d}'.format(table))
Jack: 4098; Sjoerd: 4127; Dcab: 8637678

這也可以通過使用 '**' 符號將 table 作為關(guān)鍵字參數(shù)傳遞捣域。

>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}
>>> print('Jack: {Jack:d}; Sjoerd: {Sjoerd:d}; Dcab: {Dcab:d}'.format(**table))
Jack: 4098; Sjoerd: 4127; Dcab: 8637678

這在與內(nèi)置函數(shù) vars() 結(jié)合使用時非常有用,它會返回包含所有局部變量的字典宴合。

例如焕梅,下面幾行代碼生成一組整齊的列,其中包含給定的整數(shù)和它的平方以及立方:

>>> for x in range(1, 11):
...     print('{0:2d} {1:3d} {2:4d}'.format(x, x*x, x*x*x))
...
 1   1    1
 2   4    8
 3   9   27
 4  16   64
 5  25  125
 6  36  216
 7  49  343
 8  64  512
 9  81  729
10 100 1000

關(guān)于使用 str.format() 進行字符串格式化的完整概述卦洽,請參閱 格式字符串語法

手動格式化字符串

這是同一個平方和立方的表,手動格式化的:

>>> for x in range(1, 11):
...     print(repr(x).rjust(2), repr(x*x).rjust(3), end=' ')
...     # Note use of 'end' on previous line
...     print(repr(x*x*x).rjust(4))
...
 1   1    1
 2   4    8
 3   9   27
 4  16   64
 5  25  125
 6  36  216
 7  49  343
 8  64  512
 9  81  729
10 100 1000

(注意每列之間的一個空格是通過使用 print() 的方式添加的:它總是在其參數(shù)間添加空格火诸。)

字符串對象的 str.rjust() 方法通過在左側(cè)填充空格來對給定寬度的字段中的字符串進行右對齊。類似的方法還有 str.ljust()str.center() 弟蚀。

這些方法不會寫入任何東西,它們只是返回一個新的字符串酗失,如果輸入的字符串太長义钉,它們不會截斷字符串,而是原樣返回级零;這雖然會弄亂你的列布局断医,但這通常比另一種方法好,后者會在顯示值時可能不準確(如果你真的想截斷奏纪,你可以添加一個切片操作鉴嗤,例如 x.ljust(n)[:n] 。)

還有另外一個方法序调,str.zfill() 醉锅,它會在數(shù)字字符串的左邊填充零。它能識別正負號:

>>> '12'.zfill(5)
'00012'
>>> '-3.14'.zfill(7)
'-003.14'
>>> '3.14159265359'.zfill(5)
'3.14159265359'

舊的字符串格式化方法

% 運算符(求余)也可用于字符串格式化发绢。 給定 'string' % values硬耍,則 string 中的 % 實例會以零個或多個 values 元素替換。 此操作通常被稱為字符串插值边酒。 例如:

>>> import math
>>> print('The value of pi is approximately %5.3f.' % math.pi)
The value of pi is approximately 3.142.

可在 printf 風格的字符串格式化 部分找到更多信息经柴。

讀寫文件

open() 返回一個 file object,最常用的有兩個參數(shù): open(filename, mode)墩朦。

>>> f = open('workfile', 'w')

第一個參數(shù)是包含文件名的字符串坯认。第二個參數(shù)是另一個字符串,其中包含一些描述文件使用方式的字符氓涣。mode 可以是 'r' 牛哺,表示文件只能讀取,'w' 表示只能寫入(已存在的同名文件會被刪除)劳吠,還有 'a' 表示打開文件以追加內(nèi)容引润;任何寫入的數(shù)據(jù)會自動添加到文件的末尾。'r+' 表示打開文件進行讀寫痒玩。mode 參數(shù)是可選的淳附;省略時默認為 'r'

通常文件是以 text mode 打開的蠢古,這意味著從文件中讀取或?qū)懭胱址畷r燃观,都會以指定的編碼方式進行編碼。如果未指定編碼格式便瑟,默認值與平臺相關(guān) (參見 open())缆毁。在mode 中追加的 'b' 則以 binary mode 打開文件:現(xiàn)在數(shù)據(jù)是以字節(jié)對象的形式進行讀寫的。這個模式應(yīng)該用于所有不包含文本的文件到涂。

在文本模式下讀取時脊框,默認會把平臺特定的行結(jié)束符 (Unix 上的 \n, Windows 上的 \r\n) 轉(zhuǎn)換為 \n颁督。在文本模式下寫入時,默認會把出現(xiàn)的 \n 轉(zhuǎn)換回平臺特定的結(jié)束符浇雹。這樣在幕后修改文件數(shù)據(jù)對文本文件來說沒有問題沉御,但是會破壞二進制數(shù)據(jù)例如 JPEGEXE 文件中的數(shù)據(jù)。請一定要注意在讀寫此類文件時應(yīng)使用二進制模式昭灵。

在處理文件對象時吠裆,最好使用 with 關(guān)鍵字。 優(yōu)點是當子句體結(jié)束后文件會正確關(guān)閉烂完,即使在某個時刻引發(fā)了異常试疙。 而且使用 with 相比等效的 try-finally 代碼塊要簡短得多:

>>> with open('workfile') as f:
...     read_data = f.read()

>>> # We can check that the file has been automatically closed.
>>> f.closed
True

如果你沒有使用 with 關(guān)鍵字,那么你應(yīng)該調(diào)用 f.close() 來關(guān)閉文件并立即釋放它使用的所有系統(tǒng)資源抠蚣。如果你沒有顯式地關(guān)閉文件祝旷,Python的垃圾回收器最終將銷毀該對象并為你關(guān)閉打開的文件,但這個文件可能會保持打開狀態(tài)一段時間嘶窄。另外一個風險是不同的Python實現(xiàn)會在不同的時間進行清理怀跛。

通過 with 語句或者調(diào)用 f.close() 關(guān)閉文件對象后,嘗試使用該文件對象將自動失敗柄冲。:

>>> f.close()
>>> f.read()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: I/O operation on closed file.

文件對象的方法

本節(jié)中剩下的例子將假定你已創(chuàng)建名為 f 的文件對象吻谋。

要讀取文件內(nèi)容,請調(diào)用 f.read(size)现横,它會讀取一些數(shù)據(jù)并將其作為字符串(在文本模式下)或字節(jié)串對象(在二進制模式下)返回漓拾。 size 是一個可選的數(shù)值參數(shù)。 當 size 被省略或者為負數(shù)時长赞,將讀取并返回整個文件的內(nèi)容晦攒;如果文件的大小是你的機器內(nèi)存的兩倍就會出現(xiàn)問題闽撤。 當取其他值時得哆,將讀取并返回至多 size 個字符(在文本模式下)或 size 個字節(jié)(在二進制模式下)。 如果已到達文件末尾哟旗,f.read() 將返回一個空字符串 ('')贩据。

>>> f.read()
'This is the entire file.\n'
>>> f.read()
''

f.readline() 從文件中讀取一行;換行符(\n)留在字符串的末尾闸餐,如果文件不以換行符結(jié)尾饱亮,則在文件的最后一行省略。這使得返回值明確無誤舍沙;如果 f.readline() 返回一個空的字符串近上,則表示已經(jīng)到達了文件末尾,而空行使用 '\n' 表示拂铡,該字符串只包含一個換行符壹无。:

>>> f.readline()
'This is the first line of the file.\n'
>>> f.readline()
'Second line of the file\n'
>>> f.readline()
''

要從文件中讀取行葱绒,你可以循環(huán)遍歷文件對象。這是內(nèi)存高效斗锭,快速的地淀,并簡化代碼:

>>> for line in f:
...     print(line, end='')
...
This is the first line of the file.
Second line of the file

如果你想以列表的形式讀取文件中的所有行,你也可以使用 list(f)f.readlines()岖是。

f.write(string) 會把 string 的內(nèi)容寫入到文件中帮毁,并返回寫入的字符數(shù)。:

>>> f.write('This is a test\n')
15

在寫入其他類型的對象之前豺撑,需要先把它們轉(zhuǎn)化為字符串(在文本模式下)或者字節(jié)對象(在二進制模式下):

>>> value = ('the answer', 42)
>>> s = str(value)  # convert the tuple to string
>>> f.write(s)
18

f.tell() 返回一個整數(shù)烈疚,給出文件對象在文件中的當前位置,表示為二進制模式下時從文件開始的字節(jié)數(shù)前硫,以及文本模式下的意義不明的數(shù)字胞得。

要改變文件對象的位置,請使用 f.seek(offset, whence)屹电。 通過向一個參考點添加 offset 來計算位置阶剑;參考點由 whence 參數(shù)指定。 whence 的 0 值表示從文件開頭起算危号,1 表示使用當前文件位置牧愁,2 表示使用文件末尾作為參考點。 whence 如果省略則默認值為 0外莲,即使用文件開頭作為參考點猪半。

>>> f = open('workfile', 'rb+')
>>> f.write(b'0123456789abcdef')
16
>>> f.seek(5)      # Go to the 6th byte in the file
5
>>> f.read(1)
b'5'
>>> f.seek(-3, 2)  # Go to the 3rd byte before the end
13
>>> f.read(1)
b'd'

在文本文件(那些在模式字符串中沒有 b 的打開的文件)中,只允許相對于文件開頭搜索(使用 seek(0, 2) 搜索到文件末尾是個例外)并且唯一有效的 offset 值是那些能從 f.tell() 中返回的或者是零偷线。其他 offset 值都會產(chǎn)生未定義的行為磨确。

使用 json 保存結(jié)構(gòu)化數(shù)據(jù)

字符串可以很輕松地寫入文件并從文件中讀取出來。數(shù)字可能會費點勁声邦,因為 read() 方法只能返回字符串乏奥,這些字符串必須傳遞給類似 int() 的函數(shù),它會接受類似 '123' 這樣的字符串并返回其數(shù)字值 123亥曹。當你想保存諸如嵌套列表和字典這樣更復(fù)雜的數(shù)據(jù)類型時邓了,手動解析和序列化會變得復(fù)雜。

Python 允許你使用稱為 JSON (JavaScript Object Notation) 的流行數(shù)據(jù)交換格式媳瞪,而不是讓用戶不斷的編寫和調(diào)試代碼以將復(fù)雜的數(shù)據(jù)類型保存到文件中骗炉。名為 json 的標準模塊可以采用 Python 數(shù)據(jù)層次結(jié)構(gòu),并將它們轉(zhuǎn)化為字符串表示形式蛇受;這個過程稱為 serializing 。從字符串表示中重建數(shù)據(jù)稱為 deserializing 。在序列化和反序列化之間乍丈,表示對象的字符串可能已存儲在文件或數(shù)據(jù)中熊响,或通過網(wǎng)絡(luò)連接發(fā)送到某個遠程機器。

如果你有一個對象 x 诗赌,你可以用一行簡單的代碼來查看它的 JSON 字符串表示:

>>> import json
>>> json.dumps([1, 'simple', 'list'])
'[1, "simple", "list"]'

dumps() 函數(shù)的另一個變體叫做 dump() 汗茄,它只是將對象序列化為 text file 。因此铭若,如果 f 是一個 text file 對象洪碳,我們可以這樣做:

json.dump(x, f)

要再次解碼對象,如果 f 是一個打開的以供閱讀的 text file 對象:

x = json.load(f)

這種簡單的序列化技術(shù)可以處理列表和字典叼屠,但是在JSON中序列化任意類的實例需要額外的努力瞳腌。 json 模塊的參考包含對此的解釋。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末镜雨,一起剝皮案震驚了整個濱河市嫂侍,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌荚坞,老刑警劉巖挑宠,帶你破解...
    沈念sama閱讀 218,640評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異颓影,居然都是意外死亡各淀,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評論 3 395
  • 文/潘曉璐 我一進店門诡挂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來碎浇,“玉大人,你說我怎么就攤上這事璃俗∨В” “怎么了?”我有些...
    開封第一講書人閱讀 165,011評論 0 355
  • 文/不壞的土叔 我叫張陵城豁,是天一觀的道長苟穆。 經(jīng)常有香客問我,道長钮蛛,這世上最難降的妖魔是什么鞭缭? 我笑而不...
    開封第一講書人閱讀 58,755評論 1 294
  • 正文 為了忘掉前任剖膳,我火速辦了婚禮魏颓,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘吱晒。我一直安慰自己甸饱,他們只是感情好,可當我...
    茶點故事閱讀 67,774評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著叹话,像睡著了一般偷遗。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上驼壶,一...
    開封第一講書人閱讀 51,610評論 1 305
  • 那天氏豌,我揣著相機與錄音,去河邊找鬼热凹。 笑死泵喘,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的般妙。 我是一名探鬼主播纪铺,決...
    沈念sama閱讀 40,352評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼碟渺!你這毒婦竟也來了鲜锚?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,257評論 0 276
  • 序言:老撾萬榮一對情侶失蹤苫拍,失蹤者是張志新(化名)和其女友劉穎芜繁,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體绒极,經(jīng)...
    沈念sama閱讀 45,717評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡浆洗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,894評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了集峦。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片伏社。...
    茶點故事閱讀 40,021評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖塔淤,靈堂內(nèi)的尸體忽然破棺而出摘昌,到底是詐尸還是另有隱情,我是刑警寧澤高蜂,帶...
    沈念sama閱讀 35,735評論 5 346
  • 正文 年R本政府宣布聪黎,位于F島的核電站,受9級特大地震影響备恤,放射性物質(zhì)發(fā)生泄漏稿饰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,354評論 3 330
  • 文/蒙蒙 一露泊、第九天 我趴在偏房一處隱蔽的房頂上張望喉镰。 院中可真熱鬧,春花似錦惭笑、人聲如沸侣姆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽捺宗。三九已至柱蟀,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蚜厉,已是汗流浹背长已。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留昼牛,地道東北人痰哨。 一個月前我還...
    沈念sama閱讀 48,224評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像匾嘱,于是被迫代替她去往敵國和親斤斧。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,974評論 2 355