原文:https://automatetheboringstuff.com/2e/chapter6/
文本是程序?qū)⑻幚淼淖畛R姷臄?shù)據(jù)形式之一。您已經(jīng)知道如何用+
操作符將兩個(gè)字符串值連接在一起,但是您可以做得更多。您可以從字符串值中提取部分字符串,添加或刪除空格,將字母轉(zhuǎn)換為小寫或大寫,并檢查字符串的格式是否正確。您甚至可以編寫 Python 代碼來訪問剪貼板,以復(fù)制和粘貼文本。
在本章中,你將了解所有這些以及更多。然后,您將完成兩個(gè)不同的編程項(xiàng)目:一個(gè)存儲(chǔ)多個(gè)文本字符串的簡(jiǎn)單剪貼板和一個(gè)自動(dòng)完成格式化文本片段的枯燥工作的程序。
使用字符串
讓我們看看 Python 允許你在代碼中編寫、打印和訪問字符串的一些方法。
字符串字面值
用 Python 代碼鍵入字符串值相當(dāng)簡(jiǎn)單:它們以單引號(hào)開始和結(jié)束。但是你怎么能在字符串中使用引號(hào)呢?鍵入‘那是愛麗絲的貓?!粫?huì)起作用,因?yàn)?Python 認(rèn)為字符串在Alice
之后結(jié)束,剩下的(s cat.'
)是無效的 Python 代碼。幸運(yùn)的是,有多種方法可以輸入字符串。
雙引號(hào)
字符串可以用雙引號(hào)開始和結(jié)束,就像用單引號(hào)一樣。使用雙引號(hào)的一個(gè)好處是字符串中可以有一個(gè)單引號(hào)字符。在交互式 Shell 中輸入以下內(nèi)容:
>>> spam = "That is Alice's cat."
由于字符串以雙引號(hào)開始,Python 知道單引號(hào)是字符串的一部分,而不是標(biāo)記字符串的結(jié)尾。但是,如果需要在字符串中使用單引號(hào)和雙引號(hào),就需要使用轉(zhuǎn)義字符。
轉(zhuǎn)義字符
轉(zhuǎn)義字符讓你可以使用原本不可能放入字符串的字符。轉(zhuǎn)義字符由反斜杠(\
)后跟要添加到字符串中的字符組成。(盡管由兩個(gè)字符組成,但它通常被稱為單個(gè)轉(zhuǎn)義字符。)例如,單引號(hào)的轉(zhuǎn)義字符是\'
。您可以在以單引號(hào)開始和結(jié)束的字符串中使用它。要查看轉(zhuǎn)義字符如何工作,請(qǐng)?jiān)诮换ナ?Shell 中輸入以下內(nèi)容:
>>> spam = 'Say hi to Bob\'s mother.'
Python 知道,因?yàn)?code>Bob\'s中的單引號(hào)有一個(gè)反斜杠,所以它不是用來結(jié)束字符串值的單引號(hào)。轉(zhuǎn)義字符\'
和\"
讓你分別在字符串中使用單引號(hào)和雙引號(hào)。
表 6-1 列出了您可以使用的轉(zhuǎn)義字符。
表 6-1: 轉(zhuǎn)義字符
轉(zhuǎn)義字符 | 打印為 |
---|---|
\' |
單引號(hào) |
\" |
雙引號(hào) |
\t |
制表符 |
\n |
換行(換行符) |
\\ |
反斜線符號(hào) |
在交互式 Shell 中輸入以下內(nèi)容:
>>> print("Hello there!\nHow are you?\nI\'m doing fine.")
Hello there!
How are you?
I'm doing fine.
原始字符串
您可以在字符串的開始引號(hào)前放置一個(gè)r
,使其成為原始字符串。原始字符串完全忽略所有轉(zhuǎn)義字符并打印字符串中出現(xiàn)的任何反斜杠。例如,在交互式 Shell 中輸入以下內(nèi)容:
>>> print(r'That is Carol\'s cat.')
That is Carol\'s cat.
因?yàn)檫@是一個(gè)原始字符串,Python 將反斜杠視為字符串的一部分,而不是轉(zhuǎn)義字符的開始。如果您鍵入包含許多反斜杠的字符串值,例如用于 Windows 文件路徑的字符串,如r'C:\Users\Al\Desktop'
或下一章中描述的正則表達(dá)式,原始字符串會(huì)很有幫助。
帶三重引號(hào)的多行字符串
雖然您可以使用\n
轉(zhuǎn)義字符將換行符放入字符串中,但使用多行字符串通常更容易。Python 中的多行字符串以三個(gè)單引號(hào)或三個(gè)雙引號(hào)開始和結(jié)束?!叭匾?hào)”之間的任何引號(hào)、制表符或換行符都被視為字符串的一部分。Python 的塊縮進(jìn)規(guī)則不適用于多行字符串中的行。
打開文件編輯器,編寫以下內(nèi)容:
print('''Dear Alice,
Eve's cat has been arrested for catnapping, cat burglary, and extortion.
Sincerely,
Bob''')
將該程序保存為catnapping.py
并運(yùn)行。輸出將如下所示:
Dear Alice,
Eve's cat has been arrested for catnapping, cat burglary, and extortion.
Sincerely,
Bob
注意Eve's
中的單引號(hào)字符不需要轉(zhuǎn)義。在多行字符串中,轉(zhuǎn)義單引號(hào)和雙引號(hào)是可選的。下面的print()
調(diào)用將打印相同的文本,但不使用多行字符串:
print('Dear Alice,\n\nEve\'s cat has been arrested for catnapping, cat
burglary, and extortion.\n\nSincerely,\nBob')
多行注釋
雖然散列字符(#
)標(biāo)記了該行剩余部分的注釋的開始,但是多行字符串通常用于跨多行的注釋。以下是完全有效的 Python 代碼:
"""This is a test Python program.
Written by Al Sweigart al@inventwithpython.com
This program was designed for Python 3, not Python 2.
"""
def spam():
"""This is a multiline comment to help
explain what the spam() function does."""
print('Hello!')
索引和切片字符串
字符串和列表一樣使用索引和切片。您可以將字符串'Hello, world!'
視為一個(gè)列表,并將字符串中的每個(gè)字符視為一個(gè)具有相應(yīng)索引的項(xiàng)。
“你好,我好,我好!”
T1 0 1 2 3 4 5 6 7 8 9 10 11 12
空格和感嘆號(hào)包含在字符數(shù)中,所以'Hello, world!'
是 13 個(gè)字符長(zhǎng),從索引 0 處的H
到索引 12 處的!
。
在交互式 Shell 中輸入以下內(nèi)容:
>>> spam = 'Hello, world!'
>>> spam[0]
'H'
>>> spam[4]
'o'
>>> spam[-1]
'!'
>>> spam[0:5]
'Hello'
>>> spam[:5]
'Hello'
>>> spam[7:]
'world!'
如果你指定了一個(gè)索引,你將得到字符串中該位置的字符。如果指定從一個(gè)索引到另一個(gè)索引的范圍,則包括起始索引,不包括結(jié)束索引。這就是為什么,如果spam
是'Hello, world!'
,spam[0:5]
是'Hello'
。從spam[0:5]
得到的子串將包括從spam[0]
到spam[4]
的所有內(nèi)容,去掉索引 5 處的逗號(hào)和索引 6 處的空格。這類似于range(5)
如何導(dǎo)致for
循環(huán)迭代到5
,但不包括5
。
請(qǐng)注意,對(duì)字符串進(jìn)行切片不會(huì)修改原始字符串。您可以在單獨(dú)的變量中捕獲一個(gè)變量的切片。嘗試在交互式 Shell 中輸入以下內(nèi)容:
>>> spam = 'Hello, world!'
>>> fizz = spam[0:5]
>>> fizz
'Hello'
通過將得到的子串切片并存儲(chǔ)在另一個(gè)變量中,您可以方便地快速、輕松地訪問整個(gè)字符串和子串。
字符串與in
和not
運(yùn)算符
與列表值一樣,in
和not in
操作符也可以用于字符串。使用in
或not in
連接兩個(gè)字符串的表達(dá)式將求值為布爾型True
或False
。在交互式 Shell 中輸入以下內(nèi)容:
>>> 'Hello' in 'Hello, World'
True
>>> 'Hello' in 'Hello'
True
>>> 'HELLO' in 'Hello, World'
False
>>> '' in 'spam'
True
>>> 'cats' not in 'cats and dogs'
False
這些表達(dá)式測(cè)試是否可以在第二個(gè)字符串中找到第一個(gè)字符串(精確字符串,區(qū)分大小寫)。
將字符串放入其他字符串中
將字符串放入其他字符串中是編程中的常見操作。到目前為止,我們已經(jīng)使用了+
操作符和字符串連接來完成這項(xiàng)工作:
>>> name = 'Al'
>>> age = 4000
>>> 'Hello, my name is ' + name + '. I am ' + str(age) + ' years old.'
'Hello, my name is Al. I am 4000 years old.'
然而,這需要大量繁瑣的打字工作。一種更簡(jiǎn)單的方法是使用字符串插值,其中字符串中的%s
操作符作為一個(gè)標(biāo)記,將被字符串后面的值替換。字符串插值的一個(gè)好處是不需要調(diào)用str()
來將值轉(zhuǎn)換成字符串。在交互式 Shell 中輸入以下內(nèi)容:
>>> name = 'Al'
>>> age = 4000
>>> 'My name is %s. I am %s years old.' % (name, age)
'My name is Al. I am 4000 years old.'
Python 3.6 引入了f-strings
,除了用大括號(hào)代替了%s
,表達(dá)式直接放在大括號(hào)里面,與字符串插值類似。像原始字符串一樣,F(xiàn) 字符串在起始引號(hào)前有一個(gè)f
前綴。在交互式 Shell 中輸入以下內(nèi)容:
>>> name = 'Al'
>>> age = 4000
>>> f'My name is {name}. Next year I will be {age + 1}.'
'My name is Al. Next year I will be 4001.'
記得包括f
前綴;否則,大括號(hào)及其內(nèi)容將成為字符串值的一部分:
>>> 'My name is {name}. Next year I will be {age + 1}.'
'My name is {name}. Next year I will be {age + 1}.'
有用的字符串方法
一些字符串方法分析字符串或創(chuàng)建轉(zhuǎn)換后的字符串值。本節(jié)描述了您最常使用的方法。
upper()
、lower()
、isupper()
和islower()
方法
upper()
和lower()
字符串方法返回一個(gè)新的字符串,其中原始字符串中的所有字母已經(jīng)分別轉(zhuǎn)換為大寫或小寫。字符串中的非字母字符保持不變。在交互式 Shell 中輸入以下內(nèi)容:
>>> spam = 'Hello, world!'
>>> spam = spam.upper()
>>> spam
'HELLO, WORLD!'
>>> spam = spam.lower()
>>> spam
'hello, world!'
請(qǐng)注意,這些方法不會(huì)更改字符串本身,而是返回新的字符串值。如果你想改變?cè)瓉淼淖址?,你必須調(diào)用字符串上的upper()
或lower()
,然后把新的字符串賦給原來存儲(chǔ)的變量。這就是為什么你必須使用spam = spam.upper()
來改變spam
中的字符串,而不是簡(jiǎn)單地使用spam.upper()
。(這就像變量eggs
包含值10
。寫eggs + 3
不會(huì)改變eggs
的值,但eggs = eggs + 3
會(huì)。)
如果您需要進(jìn)行不區(qū)分大小寫的比較,那么upper()
和lower()
方法會(huì)很有幫助。例如,字符串'great'
和'GREat'
彼此不相等。但是在下面的小程序中,用戶鍵入Great
、GREAT
還是grEAT
都無關(guān)緊要,因?yàn)樽址紫缺晦D(zhuǎn)換成小寫。
print('How are you?')
feeling = input()
if feeling.lower() == 'great':
print('I feel great too.')
else:
print('I hope the rest of your day is good.')
當(dāng)你運(yùn)行這個(gè)程序時(shí),問題被顯示出來,在great
上輸入一個(gè)變量,比如GREat
,仍然會(huì)給出輸出I feel great too
。向程序中添加代碼來處理用戶輸入中的變化或錯(cuò)誤,例如大小寫不一致,將使程序更容易使用,并且不太可能失敗。
How are you?
GREat
I feel great too.
您可以在autbor.com/convertlowercase
查看該程序的執(zhí)行情況。如果字符串至少有一個(gè)字母并且所有字母都是大寫或小寫,那么isupper()
和islower()
方法將返回一個(gè)布爾值True
。否則,該方法返回False
。在交互式 Shell 中輸入以下內(nèi)容,并注意每個(gè)方法調(diào)用返回的內(nèi)容:
>>> spam = 'Hello, world!'
>>> spam.islower()
False
>>> spam.isupper()
False
>>> 'HELLO'.isupper()
True
>>> 'abc12345'.islower()
True
>>> '12345'.islower()
False
>>> '12345'.isupper()
False
因?yàn)?code>upper()和lower()
字符串方法本身返回字符串,所以您也可以在返回字符串值的字符串方法上調(diào)用它們。這樣做的表達(dá)式看起來像一個(gè)方法調(diào)用鏈。在交互式 Shell 中輸入以下內(nèi)容:
>>> 'Hello'.upper()
'HELLO'
>>> 'Hello'.upper().lower()
'hello'
>>> 'Hello'.upper().lower().upper()
'HELLO'
>>> 'HELLO'.lower()
'hello'
>>> 'HELLO'.lower().islower()
True
isX
方法
除了islower()
和isupper()
之外,還有其他幾個(gè)以單詞is
開頭的字符串方法。這些方法返回一個(gè)描述字符串性質(zhì)的布爾值。下面是一些常見的is
X 串音方法:
如果字符串僅由字母組成且不為空,則isalpha()
返回True
如果字符串僅由字母和數(shù)字組成并且不為空,則isalnum()
返回True
如果字符串僅由數(shù)字字符組成且不為空,則isdecimal()
返回True
如果字符串僅由空格、制表符和換行符組成并且不為空,則isspace()
返回True
如果字符串只包含以大寫字母開頭、后跟小寫字母的單詞,則istitle()
返回True
在交互式 Shell 中輸入以下內(nèi)容:
>>> 'hello'.isalpha()
True
>>> 'hello123'.isalpha()
False
>>> 'hello123'.isalnum()
True
>>> 'hello'.isalnum()
True
>>> '123'.isdecimal()
True
>>> ' '.isspace()
True
>>> 'This Is Title Case'.istitle()
True
>>> 'This Is Title Case 123'.istitle()
True
>>> 'This Is not Title Case'.istitle()
False
>>> 'This Is NOT Title Case Either'.istitle()
False
當(dāng)您需要驗(yàn)證用戶輸入時(shí),is
X() 字符串方法非常有用。例如,下面的程序反復(fù)詢問用戶的年齡和密碼,直到他們提供有效的輸入。打開一個(gè)新的文件編輯器窗口,進(jìn)入這個(gè)程序,保存為validateInput.py
:
while True:
print('Enter your age:')
age = input()
if age.isdecimal():
break
print('Please enter a number for your age.')
while True:
print('Select a new password (letters and numbers only):')
password = input()
if password.isalnum():
break
print('Passwords can only have letters and numbers.')
在第一個(gè)while
循環(huán)中,我們?cè)儐栍脩舻哪挲g,并將他們的輸入存儲(chǔ)在age
中。如果age
是一個(gè)有效的(十進(jìn)制)值,我們就跳出第一個(gè)while
循環(huán),進(jìn)入第二個(gè)循環(huán),要求輸入密碼。否則,我們會(huì)通知用戶需要輸入一個(gè)數(shù)字,并再次要求他們輸入年齡。在第二個(gè)while
循環(huán)中,我們要求輸入密碼,將用戶的輸入存儲(chǔ)在password
中,如果輸入是字母數(shù)字,就退出循環(huán)。如果不是,我們不滿意,所以我們告訴用戶密碼需要是字母數(shù)字,并再次要求他們輸入密碼。
運(yùn)行時(shí),程序的輸出看起來像這樣:
Enter your age:
forty two
Please enter a number for your age.
Enter your age:
42
Select a new password (letters and numbers only):
secr3t!
Passwords can only have letters and numbers.
Select a new password (letters and numbers only):
secr3t
您可以在autbor.com/validateinput
查看該程序的執(zhí)行情況。在變量上調(diào)用isdecimal()
和isalnum()
,我們能夠測(cè)試存儲(chǔ)在這些變量中的值是否是十進(jìn)制的,字母數(shù)字的。這里,這些測(cè)試幫助我們拒絕輸入forty two
但接受42
,拒絕secr3t!
但接受secr3t
。
startswith()
和endswith()
方法
如果被調(diào)用的字符串值以傳遞給方法的字符串開始或結(jié)束,則startswith()
和endswith()
方法返回True
;否則,它們返回False
。在交互式 Shell 中輸入以下內(nèi)容:
>>> 'Hello, world!'.startswith('Hello')
True
>>> 'Hello, world!'.endswith('world!')
True
>>> 'abc123'.startswith('abcdef')
False
>>> 'abc123'.endswith('12')
False
>>> 'Hello, world!'.startswith('Hello, world!')
True
>>> 'Hello, world!'.endswith('Hello, world!')
True
如果您只需要檢查字符串的第一部分或最后一部分是否等于另一個(gè)字符串,而不是整個(gè)字符串,這些方法是==
equals 運(yùn)算符的有用替代方法。
使用join()
和split()
方法
當(dāng)您有一個(gè)需要連接成一個(gè)字符串值的字符串列表時(shí),join()
方法很有用。在一個(gè)字符串上調(diào)用join()
方法,傳遞一個(gè)字符串列表,然后返回一個(gè)字符串。返回的字符串是傳入列表中每個(gè)字符串的連接。例如,在交互式 Shell 中輸入以下內(nèi)容:
>>> ', '.join(['cats', 'rats', 'bats'])
'cats, rats, bats'
>>> ' '.join(['My', 'name', 'is', 'Simon'])
'My name is Simon'
>>> 'ABC'.join(['My', 'name', 'is', 'Simon'])
'MyABCnameABCisABCSimon'
請(qǐng)注意,調(diào)用的字符串join()
被插入到列表參數(shù)的每個(gè)字符串之間。例如,當(dāng)在', '
字符串上調(diào)用join(['cats', 'rats', 'bats'])
時(shí),返回的字符串是'cats, rats, bats'
。
記住join()
是在一個(gè)字符串值上被調(diào)用的,并被傳遞一個(gè)列表值。(很容易不小心叫反了。)方法split()
的作用正好相反:它對(duì)一個(gè)字符串值進(jìn)行調(diào)用,并返回一個(gè)字符串列表。在交互式 Shell 中輸入以下內(nèi)容:
>>> 'My name is Simon'.split()
['My', 'name', 'is', 'Simon']
默認(rèn)情況下,字符串'My name is Simon'
會(huì)在發(fā)現(xiàn)空格、制表符或換行符等空白字符的地方被拆分。這些空白字符不包括在返回列表的字符串中。您可以向split()
方法傳遞一個(gè)分隔符字符串來指定一個(gè)不同的分割字符串。例如,在交互式 Shell 中輸入以下內(nèi)容:
>>> 'MyABCnameABCisABCSimon'.split('ABC')
['My', 'name', 'is', 'Simon']
>>> 'My name is Simon'.split('m')
['My na', 'e is Si', 'on']
split()
的一個(gè)常見用法是沿著換行符拆分多行字符串。在交互式 Shell 中輸入以下內(nèi)容:
>>> spam = '''Dear Alice,
How have you been? I am fine.
There is a container in the fridge
that is labeled "Milk Experiment."
Please do not drink it.
Sincerely,
Bob'''
>>> spam.split('\n')
['Dear Alice,', 'How have you been? I am fine.', 'There is a container in the
fridge', 'that is labeled "Milk Experiment."', '', 'Please do not drink it.',
'Sincerely,', 'Bob']
傳遞參數(shù)'\n'
給split()
讓我們沿著新行分割存儲(chǔ)在spam
中的多行字符串,并返回一個(gè)列表,其中每一項(xiàng)對(duì)應(yīng)于字符串的一行。
用partition()
方法拆分字符串
partition()
字符串方法可以將一個(gè)字符串拆分成分隔符字符串前后的文本。此方法在調(diào)用它的字符串中搜索它所傳遞的分隔符字符串,并為before
、separator
和after
子字符串返回一個(gè)由三個(gè)子字符串組成的元組。在交互式 Shell 中輸入以下內(nèi)容:
>>> 'Hello, world!'.partition('w')
('Hello, ', 'w', 'orld!')
>>> 'Hello, world!'.partition('world')
('Hello, ', 'world', '!')
如果您傳遞給partition()
的分隔符字符串在partition()
調(diào)用的字符串中出現(xiàn)多次,該方法只在第一次出現(xiàn)時(shí)拆分字符串:
>>> 'Hello, world!'.partition('o')
('Hell', 'o', ', world!')
如果找不到分隔符字符串,則元組中返回的第一個(gè)字符串將是整個(gè)字符串,其他兩個(gè)字符串將為空:
>>> 'Hello, world!'.partition('XYZ')
('Hello, world!', '', '')
您可以使用多重賦值技巧將三個(gè)返回的字符串賦給三個(gè)變量:
>>> before, sep, after = 'Hello, world!'.partition(' ')
>>> before
'Hello,'
>>> after
'world!'
無論何時(shí),當(dāng)您需要某個(gè)特定分隔符字符串之前、之后的部分時(shí),partition()
方法對(duì)于拆分字符串非常有用。
用rjust()
、just()
和center()
方法調(diào)整文本
rjust()
和ljust()
字符串方法返回它們被調(diào)用的字符串的填充版本,其中插入空格以對(duì)齊文本。這兩種方法的第一個(gè)參數(shù)是兩端對(duì)齊的字符串的整數(shù)長(zhǎng)度。在交互式 Shell 中輸入以下內(nèi)容:
>>> 'Hello'.rjust(10)
' Hello'
>>> 'Hello'.rjust(20)
' Hello'
>>> 'Hello, World'.rjust(20)
' Hello, World'
>>> 'Hello'.ljust(10)
'Hello '
'Hello'.rjust(10)
表示我們想要右對(duì)齊總長(zhǎng)度為10
的字符串中的'Hello'
。'Hello'
是五個(gè)字符,所以它的左邊會(huì)增加五個(gè)空格,這樣我們得到一個(gè)由 10 個(gè)字符組成的字符串,其中'Hello'
右對(duì)齊。
可選的第二個(gè)參數(shù)rjust()
和ljust()
將指定一個(gè)填充字符而不是一個(gè)空格字符。在交互式 Shell 中輸入以下內(nèi)容:
>>> 'Hello'.rjust(20, '*')
'***************Hello'
>>> 'Hello'.ljust(20, '-')
'Hello---------------'
center()
字符串方法的工作方式類似于ljust()
和rjust()
,但是它將文本居中,而不是將其向左或向右對(duì)齊。在交互式 Shell 中輸入以下內(nèi)容:
>>> 'Hello'.center(20)
' Hello '
>>> 'Hello'.center(20, '=')
'=======Hello========'
當(dāng)您需要打印具有正確間距的表格數(shù)據(jù)時(shí),這些方法特別有用。打開一個(gè)新的文件編輯器窗口,輸入以下代碼,保存為pickTable.py
:
def printPicnic(itemsDict, leftWidth, rightWidth):
print('PICNIC ITEMS'.center(leftWidth + rightWidth, '-'))
for k, v in itemsDict.items():
print(k.ljust(leftWidth, '.') + str(v).rjust(rightWidth))
picnicItems = {'sandwiches': 4, 'apples': 12, 'cups': 4, 'cookies': 8000}
printPicnic(picnicItems, 12, 5)
printPicnic(picnicItems, 20, 6)
您可以在autbor.com/picnictable
查看該程序的執(zhí)行情況。在這個(gè)程序中,我們定義了一個(gè)printPicnic()
方法,它將接收一個(gè)信息字典,并使用center()
、ljust()
和rjust()
以整齊排列的表格格式顯示信息。
我們將傳遞給printPicnic()
的字典是picnicItems
。在picnicItems
,我們有 4 個(gè)三明治、12 個(gè)蘋果、4 個(gè)杯子和 8000 塊餅干。我們希望將這些信息組織成兩列,左邊是商品名稱,右邊是數(shù)量。
要做到這一點(diǎn),我們需要決定左右欄的寬度。連同我們的字典,我們將把這些值傳遞給printPicnic()
。
printPicnic()
函數(shù)接收一個(gè)字典,一個(gè)leftWidth
用于表格的左列,一個(gè)rightWidth
用于右列。它在表的中央打印一個(gè)標(biāo)題PICNIC ITEMS
。然后,它遍歷字典,在一行上打印每個(gè)鍵-值對(duì),鍵靠左對(duì)齊并用句點(diǎn)填充,值靠右對(duì)齊并用空格填充。
在定義了printPicnic()
之后,我們定義了字典picnicItems
并調(diào)用了printPicnic()
兩次,為左右表列傳遞了不同的寬度。
當(dāng)您運(yùn)行這個(gè)程序時(shí),野餐項(xiàng)目會(huì)顯示兩次。第一次左欄寬 12 個(gè)字符,右欄寬 5 個(gè)字符。第二次分別是 20 和 6 個(gè)字符寬。
---PICNIC ITEMS--
sandwiches.. 4
apples...... 12
cups........ 4
cookies..... 8000
-------PICNIC ITEMS-------
sandwiches.......... 4
apples.............. 12
cups................ 4
cookies............. 8000
使用rjust()
、ljust()
和center()
可以確保字符串整齊對(duì)齊,即使您不確定字符串有多少個(gè)字符長(zhǎng)。
用strip()
、rstrip()
和lstrip()
方法去除空白
有時(shí),您可能希望去除字符串左側(cè)、右側(cè)或兩側(cè)的空白字符(空格、制表符和換行符)。字符串方法將返回一個(gè)開頭或結(jié)尾沒有任何空白字符的新字符串。lstrip()
和rstrip()
方法將分別刪除左端和右端的空白字符。在交互式 Shell 中輸入以下內(nèi)容:
>>> spam = ' Hello, World '
>>> spam.strip()
'Hello, World'
>>> spam.lstrip()
'Hello, World '
>>> spam.rstrip()
' Hello, World'
或者,字符串參數(shù)將指定應(yīng)該去除末尾的哪些字符。在交互式 Shell 中輸入以下內(nèi)容:
>>> spam = 'SpamSpamBaconSpamEggsSpamSpam'
>>> spam.strip('ampS')
'BaconSpamEggs'
傳遞strip()
參數(shù)'ampS'
將告訴它從存儲(chǔ)在spam
中的字符串末尾刪除出現(xiàn)的a
、m
、p
和大寫S
。傳遞給strip()
的字符串中字符的順序無關(guān)緊要:strip('ampS')
將做與strip('mapS')
或strip('Spam')
相同的事情。
ord()和chr()函數(shù)與字符的數(shù)值
計(jì)算機(jī)將信息存儲(chǔ)為字節(jié)——二進(jìn)制數(shù)的字符串,這意味著我們需要能夠?qū)⑽谋巨D(zhuǎn)換為數(shù)字。因此,每個(gè)文本字符都有一個(gè)對(duì)應(yīng)的數(shù)值,稱為 Unicode 碼位。例如,'A'
的數(shù)字碼位為65
,'4'
的數(shù)字碼位為52
,'!'
的數(shù)字碼位為33
。您可以使用ord()
函數(shù)獲取單字符字符串的碼位,使用chr()
函數(shù)獲取整數(shù)碼位的單字符字符串。在交互式 Shell 中輸入以下內(nèi)容:
>>> ord('A')
65
>>> ord('4')
52
>>> ord('!')
33
>>> chr(65)
'A'
當(dāng)您需要排序字符或數(shù)學(xué)運(yùn)算時(shí),這些函數(shù)非常有用:
>>> ord('B')
66
>>> ord('A') < ord('B')
True
>>> chr(ord('A'))
'A'
>>> chr(ord('A') + 1)
'B'
關(guān)于 Unicode 和代碼點(diǎn)還有更多內(nèi)容,但是這些細(xì)節(jié)已經(jīng)超出了本書的范圍。如果你想了解更多,我推薦你觀看內(nèi)德·巴徹爾德 2012 年的 PyCon 演講,“實(shí)用 Unicode,或者,我如何停止痛苦?”在youtu.be/sgHbC6udIqc
上。
使用pyperclip模塊復(fù)制和粘貼字符串
pyperclip
模塊有copy()
和paste()
函數(shù),可以向你的電腦剪貼板發(fā)送文本,也可以從剪貼板接收文本。將程序的輸出發(fā)送到剪貼板會(huì)使它更容易粘貼到電子郵件、文字處理器或其他軟件中。
在Mu之外運(yùn)行 PYTHON 腳本
到目前為止,您已經(jīng)使用 Mu 中的交互式 Shell 和文件編輯器運(yùn)行了 Python 腳本。然而,你不會(huì)想每次運(yùn)行腳本時(shí)都要經(jīng)歷打開 Mu 和 Python 腳本的不便。幸運(yùn)的是,您可以設(shè)置一些快捷方式來簡(jiǎn)化 Python 腳本的運(yùn)行。對(duì)于 Windows、MacOS 和 Linux,這些步驟略有不同,但是每個(gè)步驟都在附錄 B 中進(jìn)行了描述。轉(zhuǎn)到附錄 B 來學(xué)習(xí)如何方便地運(yùn)行你的 Python 腳本,并能夠向它們傳遞命令行參數(shù)。(您將無法使用 Mu 向您的程序傳遞命令行參數(shù)。)
Python 中沒有pyperclip
模塊。要安裝它,請(qǐng)遵循附錄 A 中安裝第三方模塊的說明。安裝pyperclip
后,在交互 Shell 中輸入以下內(nèi)容:
>>> import pyperclip
>>> pyperclip.copy('Hello, world!')
>>> pyperclip.paste()
'Hello, world!'
當(dāng)然,如果程序之外的東西改變了剪貼板的內(nèi)容,paste()
函數(shù)將返回它。例如,如果我將這句話復(fù)制到剪貼板,然后調(diào)用paste()
,它會(huì)是這樣的:
>>> pyperclip.paste()
'For example, if I copied this sentence to the clipboard and then called
paste(), it would look like this:'
項(xiàng)目:多剪貼板自動(dòng)消息
如果你回復(fù)了大量措辭相似的郵件,你可能不得不做大量的重復(fù)輸入。也許你用這些短語保存了一個(gè)文本文檔,這樣你就可以用剪貼板方便地復(fù)制和粘貼它們。但是你的剪貼板一次只能存儲(chǔ)一條消息,這不是很方便。讓我們用一個(gè)存儲(chǔ)多個(gè)短語的程序來簡(jiǎn)化這個(gè)過程。
第一步:程序設(shè)計(jì)和數(shù)據(jù)結(jié)構(gòu)
您希望能夠用一個(gè)簡(jiǎn)短的關(guān)鍵短語作為命令行參數(shù)來運(yùn)行這個(gè)程序,例如,同意或忙碌。與該關(guān)鍵短語相關(guān)聯(lián)的消息將被復(fù)制到剪貼板,以便用戶可以將其粘貼到電子郵件中。這樣,用戶不必重新輸入就可以獲得長(zhǎng)而詳細(xì)的消息。
章節(jié)項(xiàng)目
這是本書的第一個(gè)“章節(jié)項(xiàng)目”。從現(xiàn)在開始,每章都將有展示本章所涵蓋概念的項(xiàng)目。這些項(xiàng)目以一種風(fēng)格編寫,將您從一個(gè)空白的文件編輯器窗口帶到一個(gè)完整的工作程序。就像交互式 Shell 示例一樣,不要只閱讀項(xiàng)目部分——在您的計(jì)算機(jī)上跟著做!
打開一個(gè)新的文件編輯器窗口,將程序保存為mclip.py
。你需要用一行#!
( shebang
)來開始這個(gè)程序(見附錄 B ),還應(yīng)該寫一個(gè)簡(jiǎn)短描述這個(gè)程序的注釋。因?yàn)槟M麑⒚慷挝谋九c其關(guān)鍵短語相關(guān)聯(lián),所以可以將它們作為字符串存儲(chǔ)在字典中。字典將是組織你的關(guān)鍵短語和文本的數(shù)據(jù)結(jié)構(gòu)。讓您的程序看起來像下面這樣:
#! python3
# mclip.py - A multi-clipboard program.
TEXT = {'agree': """Yes, I agree. That sounds fine to me.""",
'busy': """Sorry, can we do this later this week or next week?""",
'upsell': """Would you consider making this a monthly donation?"""}
第二步:處理命令行參數(shù)
命令行參數(shù)將存儲(chǔ)在變量sys.argv
中。(關(guān)于如何在你的程序中使用命令行參數(shù)的更多信息,請(qǐng)參見附錄 B 。)列表中的第一項(xiàng)應(yīng)該總是包含程序文件名('mclip.py'
)的字符串,第二項(xiàng)應(yīng)該是第一個(gè)命令行參數(shù)。對(duì)于這個(gè)程序,這個(gè)參數(shù)是你想要的信息的關(guān)鍵短語。因?yàn)槊钚袇?shù)是強(qiáng)制的,所以如果用戶忘記添加它(也就是說,如果sys.argv
列表中的值少于兩個(gè)),您會(huì)向用戶顯示一條用法消息。讓您的程序看起來像下面這樣:
#! python3
# mclip.py - A multi-clipboard program.
TEXT = {'agree': """Yes, I agree. That sounds fine to me.""",
'busy': """Sorry, can we do this later this week or next week?""",
'upsell': """Would you consider making this a monthly donation?"""}
import sys
if len(sys.argv) < 2:
print('Usage: python mclip.py [keyphrase] - copy phrase text')
sys.exit()
keyphrase = sys.argv[1] # first command line arg is the keyphrase
第三步:復(fù)制正確詞組
現(xiàn)在關(guān)鍵短語作為字符串存儲(chǔ)在變量keyphrase
中,您需要查看它是否作為一個(gè)鍵存在于TEXT
字典中。如果是這樣,您需要使用pyperclip.copy()
將鍵值復(fù)制到剪貼板。(因?yàn)槟谑褂?code>pyperclip模塊,所以您需要導(dǎo)入它。)注意,實(shí)際上并不需要這個(gè)keyphrase
變量;你可以在這個(gè)程序中使用keyphrase
的任何地方使用sys.argv[1]
。但是一個(gè)名為keyphrase
的變量比類似于sys.argv[1]
的神秘東西更具可讀性。
讓您的程序看起來像下面這樣:
#! python3
# mclip.py - A multi-clipboard program.
TEXT = {'agree': """Yes, I agree. That sounds fine to me.""",
'busy': """Sorry, can we do this later this week or next week?""",
'upsell': """Would you consider making this a monthly donation?"""}
import sys, pyperclip
if len(sys.argv) < 2:
print('Usage: py mclip.py [keyphrase] - copy phrase text')
sys.exit()
keyphrase = sys.argv[1] # first command line arg is the keyphrase
if keyphrase in TEXT:
pyperclip.copy(TEXT[keyphrase])
print('Text for ' + keyphrase + ' copied to clipboard.')
else:
print('There is no text for ' + keyphrase)
這個(gè)新代碼在TEXT
字典中查找關(guān)鍵短語。如果關(guān)鍵短語是字典中的一個(gè)鍵,我們獲得對(duì)應(yīng)于該鍵的值,將其復(fù)制到剪貼板,并打印一條消息,說明我們復(fù)制了該值。否則,我們會(huì)打印一條消息,說明沒有該名稱的關(guān)鍵短語。
這是完整的劇本。使用附錄 B 中的指令輕松啟動(dòng)命令行程序,你現(xiàn)在有了一個(gè)快速復(fù)制信息到剪貼板的方法。每當(dāng)您想用新消息更新程序時(shí),您必須修改源代碼中的TEXT
字典值。
在 Windows 上,您可以創(chuàng)建一個(gè)批處理文件,用WIN-R
運(yùn)行窗口運(yùn)行這個(gè)程序。(關(guān)于批處理文件的更多信息,參見附錄 B 。)在文件編輯器中輸入以下內(nèi)容,并將該文件作為mclip.bat
保存在C:\Windows
文件夾中:
@py.exe C:\path_to_file\mclip.py %*
@pause
創(chuàng)建了這個(gè)批處理文件后,在 Windows 上運(yùn)行多剪貼板程序只需按下WIN-R
并鍵入mclip
關(guān)鍵詞即可。
項(xiàng)目:向維基標(biāo)記添加項(xiàng)目符號(hào)
編輯維基百科文章時(shí),你可以創(chuàng)建一個(gè)項(xiàng)目符號(hào)列表,將每個(gè)列表項(xiàng)放在自己的行上,并在前面加一個(gè)星號(hào)。但是假設(shè)你有一個(gè)很大的列表,你想添加要點(diǎn)。你可以在每一行的開頭一個(gè)接一個(gè)地輸入這些星號(hào)。或者您可以使用一個(gè)簡(jiǎn)短的 Python 腳本來自動(dòng)完成這項(xiàng)任務(wù)。
bulletPointAdder.py
腳本將從剪貼板獲取文本,在每一行的開頭添加一個(gè)星號(hào)和空格,然后將這個(gè)新文本粘貼到剪貼板。例如,如果我將以下文本(針對(duì)維基百科文章“列表列表列表”)復(fù)制到剪貼板:
Lists of animals
Lists of aquarium life
Lists of biologists by author abbreviation
Lists of cultivars
然后運(yùn)行bulletPointAdder.py
程序,剪貼板將包含以下內(nèi)容:
* Lists of animals
* Lists of aquarium life
* Lists of biologists by author abbreviation
* Lists of cultivars
這個(gè)帶星號(hào)前綴的文本可以作為項(xiàng)目符號(hào)列表粘貼到維基百科的文章中。
第一步:從剪貼板復(fù)制粘貼
您希望bulletPointAdder.py
程序執(zhí)行以下操作:
- 從剪貼板粘貼文本。
- 做點(diǎn)什么。
- 將新文本復(fù)制到剪貼板。
第二步有點(diǎn)復(fù)雜,但是第一步和第三步非常簡(jiǎn)單:它們只涉及到pyperclip.copy()
和pyperclip.paste()
函數(shù)?,F(xiàn)在,讓我們只編寫程序的第 1 步和第 3 步。輸入以下內(nèi)容,將程序保存為bulletPointAdder.py
:
#! python3
# bulletPointAdder.py - Adds Wikipedia bullet points to the start
# of each line of text on the clipboard.
import pyperclip
text = pyperclip.paste()
# TODO: Separate lines and add stars.
pyperclip.copy(text)
TODO
注釋提醒您最終應(yīng)該完成程序的這一部分。下一步是實(shí)際實(shí)現(xiàn)程序的這一部分。
第二步:把文字的行分開,加上星號(hào)
對(duì)pyperclip.paste()
的調(diào)用將剪貼板上的所有文本作為一個(gè)大字符串返回。如果我們使用“列表的列表的列表”示例,存儲(chǔ)在text
中的字符串將如下所示:
'Lists of animals\nLists of aquarium life\nLists of biologists by author
abbreviation\nLists of cultivars'
該字符串中的\n
換行符導(dǎo)致它在從剪貼板打印或粘貼時(shí)顯示為多行。在這個(gè)字符串值中有許多“行”。您需要在每一行的開頭添加一個(gè)星號(hào)。
您可以編寫代碼來搜索字符串中的每個(gè)\n
換行符,然后在其后添加星號(hào)。但是使用split()
方法返回一個(gè)字符串列表會(huì)更容易,原始字符串中的每一行都有一個(gè)字符串,然后在列表中的每個(gè)字符串前面加上星號(hào)。
讓您的程序看起來像下面這樣:
#! python3
# bulletPointAdder.py - Adds Wikipedia bullet points to the start
# of each line of text on the clipboard.
import pyperclip
text = pyperclip.paste()
# Separate lines and add stars.
lines = text.split('\n')
for i in range(len(lines)): # loop through all indexes in the "lines" list
lines[i] = '* ' + lines[i] # add star to each string in "lines" list
pyperclip.copy(text)
我們沿著文本的新行分割文本,得到一個(gè)列表,列表中的每一項(xiàng)都是文本的一行。我們將列表存儲(chǔ)在lines
中,然后遍歷lines
中的項(xiàng)目。對(duì)于每一行,我們?cè)谛惺滋砑右粋€(gè)星號(hào)和一個(gè)空格?,F(xiàn)在lines
中的每個(gè)字符串都以一個(gè)星號(hào)開始。
第三步:添加修改后的行
lines
列表現(xiàn)在包含以星號(hào)開始的修改行。但是pyperclip.copy()
期望的是單個(gè)字符串值,而不是字符串值的列表。要生成這個(gè)單個(gè)字符串值,將lines
傳遞到join()
方法中,從列表的字符串中獲取一個(gè)單個(gè)連接的字符串。讓您的程序看起來像下面這樣:
#! python3
# bulletPointAdder.py - Adds Wikipedia bullet points to the start
# of each line of text on the clipboard.
import pyperclip
text = pyperclip.paste()
# Separate lines and add stars.
lines = text.split('\n')
for i in range(len(lines)): # loop through all indexes for "lines" list
lines[i] = '* ' + lines[i] # add star to each string in "lines" list
text = '\n'.join(lines)
pyperclip.copy(text)
當(dāng)這個(gè)程序運(yùn)行時(shí),它將剪貼板上的文本替換為每行開頭都有星號(hào)的文本?,F(xiàn)在程序完成了,您可以嘗試用復(fù)制到剪貼板的文本運(yùn)行它。
即使您不需要自動(dòng)化這個(gè)特定的任務(wù),您也可能希望自動(dòng)化一些其他類型的文本操作,比如刪除行尾的尾隨空格或者將文本轉(zhuǎn)換為大寫或小寫。無論您需要什么,您都可以使用剪貼板進(jìn)行輸入和輸出。
一個(gè)簡(jiǎn)短的程序:PigLatin
PigLatin是一種改變英語單詞的愚蠢的虛構(gòu)語言。如果一個(gè)單詞以元音開頭,單詞yay
會(huì)加到它的末尾。如果一個(gè)單詞以一個(gè)輔音或輔音群開頭(比如ch
或gr
,那么這個(gè)輔音或輔音群會(huì)被移到單詞的末尾,后面跟著ay
。
讓我們編寫一個(gè) Pig Latin 程序,它將輸出如下內(nèi)容:
Enter the English message to translate into Pig Latin:
My name is AL SWEIGART and I am 4,000 years old.
Ymay amenay isyay ALYAY EIGARTSWAY andyay Iyay amyay 4,000 yearsyay oldyay.
這個(gè)程序的工作原理是使用本章介紹的方法改變字符串。在文件編輯器中鍵入以下源代碼,并將文件保存為pigLat.py
:
# English to Pig Latin
print('Enter the English message to translate into Pig Latin:')
message = input()
VOWELS = ('a', 'e', 'i', 'o', 'u', 'y')
pigLatin = [] # A list of the words in Pig Latin.
for word in message.split():
# Separate the non-letters at the start of this word:
prefixNonLetters = ''
while len(word) > 0 and not word[0].isalpha():
prefixNonLetters += word[0]
word = word[1:]
if len(word) == 0:
pigLatin.append(prefixNonLetters)
continue
# Separate the non-letters at the end of this word:
suffixNonLetters = ''
while not word[-1].isalpha():
suffixNonLetters += word[-1]
word = word[:-1]
# Remember if the word was in uppercase or title case.
wasUpper = word.isupper()
wasTitle = word.istitle()
word = word.lower() # Make the word lowercase for translation.
# Separate the consonants at the start of this word:
prefixConsonants = ''
while len(word) > 0 and not word[0] in VOWELS:
prefixConsonants += word[0]
word = word[1:]
# Add the Pig Latin ending to the word:
if prefixConsonants != '':
word += prefixConsonants + 'ay'
else:
word += 'yay'
# Set the word back to uppercase or title case:
if wasUpper:
word = word.upper()
if wasTitle:
word = word.title()
# Add the non-letters back to the start or end of the word.
pigLatin.append(prefixNonLetters + word + suffixNonLetters)
# Join all the words back together into a single string:
print(' '.join(pigLatin))
讓我們從頂部開始,逐行查看這段代碼:
# English to Pig Latin
print('Enter the English message to translate into Pig Latin:')
message = input()
VOWELS = ('a', 'e', 'i', 'o', 'u', 'y')
首先,我們要求用戶輸入要翻譯成PigLatin的英語文本。此外,我們創(chuàng)建一個(gè)常量,將每個(gè)小寫元音字母(和y
)保存為一個(gè)字符串元組。這將在我們的程序中用到。
接下來,我們將創(chuàng)建pigLatin
變量來存儲(chǔ)我們翻譯成 Pig Latin 的單詞:
pigLatin = [] # A list of the words in Pig Latin.
for word in message.split():
# Separate the non-letters at the start of this word:
prefixNonLetters = ''
while len(word) > 0 and not word[0].isalpha():
prefixNonLetters += word[0]
word = word[1:]
if len(word) == 0:
pigLatin.append(prefixNonLetters)
continue
我們需要每個(gè)單詞都是它自己的字符串,所以我們調(diào)用message.split()
來獲得作為單獨(dú)字符串的單詞列表。字符串'My name is AL SWEIGART and I am 4,000 years old.'
將導(dǎo)致split()
返回['My', 'name', 'is', 'AL', 'SWEIGART', 'and', 'I', 'am', '4,000', 'years', 'old.']
。
我們需要?jiǎng)h除每個(gè)單詞開頭和結(jié)尾的任何非字母,這樣像'old.'
這樣的字符串就可以翻譯成'oldyay.'
而不是'old.yay'
。我們將這些非字母保存到一個(gè)名為prefixNonLetters
的變量中。
# Separate the non-letters at the end of this word:
suffixNonLetters = ''
while not word[-1].isalpha():
suffixNonLetters += word[-1]
word = word[:-1]
對(duì)單詞中的第一個(gè)字符調(diào)用isalpha()
的循環(huán)將決定我們是否應(yīng)該從單詞中刪除一個(gè)字符,并將其連接到prefixNonLetters
的末尾。如果整個(gè)單詞是由非字母字符組成的,比如'4,000'
,我們可以簡(jiǎn)單地將它添加到pigLatin
列表中,然后繼續(xù)翻譯下一個(gè)單詞。我們還需要保存word
字符串末尾的非字母。這段代碼類似于前面的循環(huán)。
接下來,我們將確保程序記住單詞是大寫還是大寫,這樣我們就可以在將單詞翻譯成 Pig Latin 后恢復(fù)它:
# Remember if the word was in uppercase or title case.
wasUpper = word.isupper()
wasTitle = word.istitle()
word = word.lower() # Make the word lowercase for translation.
對(duì)于for
循環(huán)中的其余代碼,我們將使用小寫版本的word
。
要將像sweigart
這樣的單詞轉(zhuǎn)換成eigart-sway
,我們需要?jiǎng)h除word
開頭的所有輔音:
# Separate the consonants at the start of this word:
prefixConsonants = ''
while len(word) > 0 and not word[0] in VOWELS:
prefixConsonants += word[0]
word = word[1:]
我們使用了一個(gè)類似于從word
的開頭刪除非字母的循環(huán),除了現(xiàn)在我們正在刪除輔音并將它們存儲(chǔ)到一個(gè)名為prefixConsonants
的變量中。
如果在單詞的開頭有任何輔音,它們現(xiàn)在在prefixConsonants
中,我們應(yīng)該將那個(gè)變量和字符串'ay'
連接到word
的結(jié)尾。否則,我們可以假設(shè)word
以元音開始,我們只需要連接'yay'
:
# Add the Pig Latin ending to the word:
if prefixConsonants != '':
word += prefixConsonants + 'ay'
else:
word += 'yay'
回想一下,我們用word = word.lower()
將word
設(shè)置為小寫版本。如果word
最初是大寫或標(biāo)題大寫,這段代碼將把word
轉(zhuǎn)換回它原來的大小寫:
# Set the word back to uppercase or title case:
if wasUpper:
word = word.upper()
if wasTitle:
word = word.title()
在for
循環(huán)結(jié)束時(shí),我們將這個(gè)單詞,連同它原來的任何非字母前綴或后綴,添加到pigLatin
列表中:
# Add the non-letters back to the start or end of the word.
pigLatin.append(prefixNonLetters + word + suffixNonLetters)
# Join all the words back together into a single string:
print(' '.join(pigLatin))
這個(gè)循環(huán)結(jié)束后,我們通過調(diào)用join()
方法將字符串列表合并成一個(gè)字符串。這個(gè)字符串被傳遞給print()
以在屏幕上顯示我們的豬拉丁。
你可以在找到其他簡(jiǎn)短的基于文本的 Python 程序,比如這個(gè)。
總結(jié)
文本是一種常見的數(shù)據(jù)形式,Python 附帶了許多有用的字符串方法來處理存儲(chǔ)在字符串值中的文本。您將在您編寫的幾乎每個(gè) Python 程序中使用索引、切片和字符串方法。
你現(xiàn)在寫的程序看起來不太復(fù)雜——它們沒有帶有圖像和彩色文本的圖形用戶界面。到目前為止,您使用print()
顯示文本,并讓用戶使用input()
輸入文本。然而,用戶可以通過剪貼板快速輸入大量文本。這種能力為編寫處理大量文本的程序提供了一個(gè)有用的途徑。這些基于文本的程序可能沒有華麗的窗口或圖形,但它們可以快速完成大量有用的工作。
另一種處理大量文本的方法是直接從硬盤上讀寫文件。你將在第 9 章中學(xué)習(xí)如何用 Python 做這件事。
這幾乎涵蓋了 Python 編程的所有基本概念!在本書的其余部分,您將繼續(xù)學(xué)習(xí)新概念,但是您現(xiàn)在已經(jīng)知道了足夠多的知識(shí),可以開始編寫一些有用的程序來自動(dòng)化任務(wù)。如果你想看一組簡(jiǎn)短的 Python 程序,這些程序是根據(jù)你到目前為止所學(xué)的基本概念構(gòu)建的,請(qǐng)查看asweigart/pythonstdiogames
。嘗試手動(dòng)復(fù)制每個(gè)程序的源代碼,然后進(jìn)行修改,看看它們?nèi)绾斡绊懗绦虻男袨?。一旦你理解了程序是如何工作的,試著從頭開始重新創(chuàng)建程序。你不需要完全重新創(chuàng)建源代碼;只需關(guān)注程序做什么,而不是如何做。
您可能認(rèn)為自己沒有足夠的 Python 知識(shí)來做諸如下載網(wǎng)頁、更新電子表格或發(fā)送文本消息之類的事情,但這正是 Python 模塊的用武之地!這些由其他程序員編寫的模塊提供了一些函數(shù),使您可以輕松完成所有這些事情。因此,讓我們學(xué)習(xí)如何編寫真正的程序來完成有用的自動(dòng)化任務(wù)。
練習(xí)題
-
什么是轉(zhuǎn)義字符?
-
\n
和\t
轉(zhuǎn)義字符分別代表什么? -
如何在字符串中放一個(gè)
\
反斜杠字符? -
字符串值
"Howl's Moving Castle"
是有效的字符串。為什么單詞Howl's
中的單引號(hào)字符不轉(zhuǎn)義就不是問題了? -
如果你不想把
\n
放在你的字符串里,你怎么能寫一個(gè)有換行符的字符串呢? -
下面的表達(dá)式表示什么?
'Hello, world!'[1]
'Hello, world!'[0:5]
'Hello, world!'[:5]
'Hello, world!'[3:]
-
下面的表達(dá)式表示什么?
'Hello'.upper()
'Hello'.upper().isupper()
'Hello'.upper().lower()
-
下面的表達(dá)式表示什么?
'Remember, remember, the fifth of November.'.split()
'-'.join('There can be only one.'.split())
-
有哪些字符串方法可以用來右對(duì)齊、左對(duì)齊和居中字符串?
-
如何從字符串的開頭或結(jié)尾修剪空白字符?
實(shí)踐項(xiàng)目
為了練習(xí),編寫執(zhí)行以下操作的程序。
表格打印
編寫一個(gè)名為printTable()
的函數(shù),它獲取一個(gè)字符串列表列表,并將其顯示在一個(gè)組織良好的表格中,每列右對(duì)齊。假設(shè)所有內(nèi)部列表將包含相同數(shù)量的字符串。例如,該值可能如下所示:
tableData = [['apples', 'oranges', 'cherries', 'banana'],
['Alice', 'Bob', 'Carol', 'David'],
['dogs', 'cats', 'moose', 'goose']]
您的printTable()
函數(shù)將打印以下內(nèi)容:
apples Alice dogs
oranges Bob cats
cherries Carol moose
banana David goose
提示:您的代碼首先必須在每個(gè)內(nèi)部列表中找到最長(zhǎng)的字符串,以便整列足夠?qū)捯匀菁{所有的字符串。您可以將每列的最大寬度存儲(chǔ)為一個(gè)整數(shù)列表。printTable()
函數(shù)可以從colWidths = [0] * len(tableData)
開始,這將創(chuàng)建一個(gè)包含與tableData
中內(nèi)部列表數(shù)量相同的0
值的列表。這樣,colWidths[0]
可以在tableData[0]
中存儲(chǔ)最長(zhǎng)字符串的寬度,colWidths[1]
可以在tableData[1]
中存儲(chǔ)最長(zhǎng)字符串的寬度,以此類推。然后,您可以在colWidths
列表中找到最大值,以找出要傳遞給rjust()
字符串方法的整數(shù)寬度。
僵尸骰子
編程游戲是一種游戲類型,玩家不是直接玩游戲,而是編寫機(jī)器人程序來自主玩游戲。我已經(jīng)創(chuàng)建了一個(gè)僵尸骰子模擬器,它允許程序員在制作玩游戲的人工智能時(shí)練習(xí)他們的技能。僵尸骰子機(jī)器人可以很簡(jiǎn)單,也可以非常復(fù)雜,非常適合課堂練習(xí)或個(gè)人編程挑戰(zhàn)。
僵尸骰子是一個(gè)快速,有趣的史蒂夫杰克遜游戲骰子游戲。玩家是僵尸,試圖在不被擊中三次的情況下吃掉盡可能多的人腦。有一個(gè) 13 個(gè)骰子的杯子,上面有大腦、腳步聲和獵槍圖標(biāo)。骰子圖標(biāo)是有顏色的,每種顏色表示每個(gè)事件發(fā)生的可能性不同。每個(gè)骰子都有兩面有足跡,但綠色圖標(biāo)的骰子有更多面有大腦,紅色圖標(biāo)的骰子有更多散彈槍,黃色圖標(biāo)的骰子有平均分配的大腦和散彈槍。在每個(gè)玩家的回合中執(zhí)行以下操作:
- 將所有 13 個(gè)骰子放入杯中。玩家從杯中隨機(jī)抽取三個(gè)骰子,然后擲出。玩家總是擲出正好三個(gè)骰子。
- 他們把任何大腦(大腦被吃掉的人類)和獵槍(反擊的人類)放在一邊,清點(diǎn)起來。累積三支獵槍自動(dòng)以零分結(jié)束玩家的回合(不管他們有多少大腦)。如果他們有零到兩支獵槍,他們可以繼續(xù)滾動(dòng),如果他們想要的。他們也可以選擇結(jié)束他們的回合,每個(gè)大腦收集一點(diǎn)。
- 如果玩家決定繼續(xù)擲骰子,他們必須用腳步重?cái)S所有骰子。記住玩家必須總是擲出三個(gè)骰子;如果他們滾動(dòng)的腳步少于三步,他們必須從杯子里抽出更多的骰子。一個(gè)玩家可能會(huì)繼續(xù)擲骰子,直到他們得到三把散彈槍——失去一切——或者所有 13 個(gè)骰子都被擲出。玩家不能只重?cái)S一個(gè)或兩個(gè)骰子,也不能中途停止重?cái)S。
- 當(dāng)有人達(dá)到 13 個(gè)大腦時(shí),其余的玩家完成這一輪。最聰明的人贏了。如果出現(xiàn)平局,平局的玩家進(jìn)行最后一輪決勝局。
僵尸骰子有一個(gè)碰運(yùn)氣的游戲機(jī)制:你重新擲骰子越多,你能得到的大腦就越多,但你最終積累三把獵槍并輸?shù)粢磺械目赡苄跃驮酱?。一旦一名玩家達(dá)到 13 分,其余的玩家再獲得一次機(jī)會(huì)(可能會(huì)趕上),游戲結(jié)束。得分最高的玩家獲勝。你可以在asweigart/zombiedice
找到完整的規(guī)則。
按照附錄 A 中的說明安裝帶 PIP 的zombiedice
模塊。通過在交互式 Shell 中運(yùn)行以下內(nèi)容,您可以使用一些預(yù)制的機(jī)器人來運(yùn)行模擬器的演示:
>>> import zombiedice
>>> zombiedice.demo()
Zombie Dice Visualization is running. Open your browser to http://
localhost:51810 to view it.
Press Ctrl-C to quit.
該程序啟動(dòng)你的網(wǎng)絡(luò)瀏覽器,看起來像圖 6-1 。
圖 6-1:僵尸骰子模擬器的網(wǎng)絡(luò)圖形用戶界面
您將通過編寫一個(gè)帶有turn()
方法的類來創(chuàng)建機(jī)器人,當(dāng)輪到您的機(jī)器人擲骰子時(shí),模擬器將調(diào)用該方法。類已經(jīng)超出了本書的范圍,所以類代碼已經(jīng)在myzombie.py
程序中為你設(shè)置好了,它在本書的可下載 ZIP 文件中nostarch.com/automatestuff2
。寫方法本質(zhì)上和寫函數(shù)是一樣的,可以使用myZombie.py
程序中的turn()
代碼作為模板。在這個(gè)turn()
方法中,只要你想讓你的機(jī)器人擲骰子,你就可以調(diào)用zombiedice.roll()
函數(shù)。
import zombiedice
class MyZombie:
def __init__(self, name):
# All zombies must have a name:
self.name = name
def turn(self, gameState):
# gameState is a dict with info about the current state of the game.
# You can choose to ignore it in your code.
diceRollResults = zombiedice.roll() # first roll
# roll() returns a dictionary with keys 'brains', 'shotgun', and
# 'footsteps' with how many rolls of each type there were.
# The 'rolls' key is a list of (color, icon) tuples with the
# exact roll result information.
# Example of a roll() return value:
# {'brains': 1, 'footsteps': 1, 'shotgun': 1,
# 'rolls': [('yellow', 'brains'), ('red', 'footsteps'),
# ('green', 'shotgun')]}
# REPLACE THIS ZOMBIE CODE WITH YOUR OWN:
brains = 0
while diceRollResults is not None:
brains += diceRollResults['brains']
if brains < 2:
diceRollResults = zombiedice.roll() # roll again
else:
break
zombies = (
zombiedice.examples.RandomCoinFlipZombie(name='Random'),
zombiedice.examples.RollsUntilInTheLeadZombie(name='Until Leading'),
zombiedice.examples.MinNumShotgunsThenStopsZombie(name='Stop at 2
Shotguns', minShotguns=2),
zombiedice.examples.MinNumShotgunsThenStopsZombie(name='Stop at 1
Shotgun', minShotguns=1),
MyZombie(name='My Zombie Bot'),
# Add any other zombie players here.
)
# Uncomment one of the following lines to run in CLI or Web GUI mode:
#zombiedice.runTournament(zombies=zombies, numGames=1000)
zombiedice.runWebGui(zombies=zombies, numGames=1000)
turn()
方法有兩個(gè)參數(shù):self
和gameState
。在最初的幾個(gè)僵尸機(jī)器人中,你可以忽略這些,如果你想了解更多,可以稍后查閱在線文檔了解詳細(xì)信息。對(duì)于初始擲骰子,turn()
方法應(yīng)該至少調(diào)用zombiedice.roll()
一次。然后,根據(jù)機(jī)器人使用的策略,它可以再次調(diào)用zombiedice.roll()
任意多次。在myZombie.py
中,turn()
方法調(diào)用zombiedice.roll()
兩次,這意味著僵尸機(jī)器人將總是每回合擲骰子兩次,而不管擲骰子的結(jié)果如何。
zombiedice.roll()
的返回值告訴你的代碼擲骰子的結(jié)果。這是一本有四個(gè)鍵的字典。其中三個(gè)鍵,'shotgun'
、'brains'
和'footsteps'
,有整數(shù)值,表示有多少骰子出現(xiàn)了這些圖標(biāo)。第四個(gè)'rolls'
鍵的值是每個(gè)骰子點(diǎn)數(shù)的元組列表。元組包含兩個(gè)字符串:索引0
處骰子的顏色和索引1
處滾動(dòng)的圖標(biāo)。請(qǐng)看turn()
方法定義中的代碼注釋作為例子。如果機(jī)器人已經(jīng)滾了三把獵槍,那么zombiedice.roll()
就會(huì)返回None
。
試著寫一些你自己的機(jī)器人來玩僵尸骰子,看看它們與其他機(jī)器人相比如何。具體來說,嘗試創(chuàng)建以下機(jī)器人:文章來源:http://www.zghlxwxcb.cn/news/detail-403051.html
- 一個(gè)機(jī)器人,在第一次投擲后,隨機(jī)決定是繼續(xù)還是停止
- 一個(gè)機(jī)器人在滾過兩個(gè)大腦后停止?jié)L動(dòng)
- 一個(gè)機(jī)器人滾了兩把獵槍后就停止了滾動(dòng)
- 最初決定擲骰子一到四次的機(jī)器人,但如果擲出兩把獵槍,就會(huì)提前停止
- 一個(gè)機(jī)器人在滾過比大腦還多的獵槍后停止?jié)L動(dòng)
通過模擬器運(yùn)行這些機(jī)器人,看看它們之間的比較如何。你也可以在檢查一些預(yù)制的機(jī)器人的代碼。如果你發(fā)現(xiàn)自己在現(xiàn)實(shí)世界中玩這個(gè)游戲,你會(huì)受益于數(shù)以千計(jì)的模擬游戲,告訴你最好的策略之一是一旦你滾了兩把獵槍就停下來。但是你可以試試碰運(yùn)氣…文章來源地址http://www.zghlxwxcb.cn/news/detail-403051.html
到了這里,關(guān)于Python 自動(dòng)化指南(繁瑣工作自動(dòng)化)第二版:六、字符串操作的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!