自從Python 3.6開始,引入了f表達式(f-string),這使得Python在填充字符串時可以進行一些簡單的計算。並且f表達式的運算速度是字符串.format方法的很多倍。
無論是f表達式還是字符串的.format方法,我們都可以使用大括號作為佔位符,來填充數據。例如:
>>> name = 'kingname'
>>> print(f'我的名字是:{name}')
我的名字是:kingname
>>> print(f'1+1的結果為:{1 + 1}')
1+1的結果為:2
>>> salary = 999999
>>> print('我的月薪是:{salary}'.format(salary=salary))
我的月薪是:999999但現在問題來了,如果我希望在使用f表達式或者.format方法填充內容的同時,又能保留大括號應該怎麼辦呢?
舉個例子,在寫爬蟲的時候,我需要使用正則表達式從當前URL中提取當前的頁數:page=\d{0,3}。但是,對於不同的網站,表示頁數的這個參數名可能是不一樣的,有些是page=xxx,有些是Pag=xxx,有些是pageNo=xxx,有些是p=xxx。所以我想動態生成這個正則表達式。
如果我們直接使用f表達式或者.format方法,就會報錯:
>>> page_name = 'page'
>>> page_regex_template = '{page_name}=\d{0,3}'
>>> print(page_regex_template.format(page_name=page_name))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: '0,3'為了能夠正常生成正則表達式,可能有人會想到使用古老的%s佔位符:
>>> page_name = 'page'
>>> page_regex_template = '%s=\d{0,3}'
>>> print(page_regex_template % page_name)
page=\d{0,3}雖然確實可行,但是混用兩種填充字符串的方法,代碼會變得不好維護,而且%s這種佔位符填充速度也非常慢。
實際上,在Python的f表達式和.format方法中,如果你需要保留大括號,那麼只需要寫成大括號套大括號的形式就行了:
>>> page_name = 'page'
>>> page_regex_template = '{page_name}=\d{{0,3}}'
>>> print(page_regex_template.format(page_name=page_name))
page=\d{0,3}大括號裡面的第一層大括號會自動失效,變成普通的字符。但如果是大括號套大括號套大括號,那麼最裡面的一對大括號會繼續生效充當佔位符,例如:
>>> page_name = 'page'
>>> page_range = '0,5'
>>> page_regex_template = '{page_name}=\d{{{page_range}}}'
>>> print(page_regex_template.format(page_name=page_name, page_range=page_range))
page=\d{0,5}總結起來就是,如果從外向內數,如果最外層大括號稱為第1層,那麼,第奇數層的大括號用來填充數據,第偶數層的大括號就是普通的字符。因此,如果不考慮代碼可讀性,如果我們需要最終生成的字符串本身就是嵌套大括號的形式,我們還可以進一步寫成:
>>> ugly_string = '2層嵌套大括號:{{{{{variable}}}}}'
>>> print(ugly_string.format(variable=variable))
2層嵌套大括號:{{name}}
>>> ugly_string = '3層嵌套大括號:{{{{{{{variable}}}}}}}'
>>> print(ugly_string.format(variable=variable))
3層嵌套大括號:{{{name}}}假設我們希望最終輸出的字符串裡面,保留n層大括號,那麼在代碼裡面,我們需要寫2n + 1層大括號。大家也看出來了,如果你要這樣寫,數大括號的個數都要把你的眼鏡數瞎。所以,在實際開發中,大括號的層數絕對不要超過2層。
以下內容供學有餘力的同學閱讀。上面講到的方法,適用於大括號成對出現的情況,如果大括號只有半邊,例如有些網站的正文信息是以JSON格式寫在原始碼裡面的,於是我們可以使用正則表達式提取出包含正文的JSON然後進一步處理,一般來說,正則表達式可能是這樣的:content=(\{);。不過,有的網站用的單詞是content,有的網站用的是detail,所以這個地方需要填充,如果我們像往常那樣寫,那麼還是會報錯,例如:
>>> content_name = 'detail'
>>> content_json_template = '{content_name}=({\{})$'
>>> print(content_json_template.format(content_name=content_name))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: unexpected '{' in field name套一層不行,那我們看看套兩層如何:
>>> content_name = 'detail'
>>> content_json_template = '{content_name}=({{\{}})$'
>>> print(content_json_template.format(content_name=content_name))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: Replacement index 0 out of range for positional args tuple套兩層還是錯。
如果字符串不含反斜槓,我們可以使用f表達式配合引號包住半邊大括號來實現,例如:
>>> name = 'kingname'
>>> print(f'我的名字是:{name},我的參數是:{"{"}')
我的名字是:kingname,我的參數是:{但問題是,f表達式裡面是不允許出現反斜槓的,否則會報錯:
>>> content_name = 'detail'
>>> content_regex = f'{content_name}=({\"{"})$'
File "<stdin>", line 1
SyntaxError: f-string expression part cannot include a backslash而.format方法支持反斜槓,但它又不支持引號包住單邊大括號的寫法:
>>> name = 'kingname'
>>> print('我的名字是:{name},我的參數是:{"{"}'.format(name=name))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: unexpected '{' in field name遇到這種情況,我們應該怎麼解決呢?只要把思路放開,靈活變通,能找出很多方法,這裡僅舉兩例:
把大括號放到值裡面把思路調整過來,既然大括號不能放在句子模板裡面,那我們就放在被填充的值裡面:
# 使用.format方法
>>> name = 'kingname'
>>> brace = '{'
>>> print('我的名字是:{name},我的參數是:{brace}'.format(name=name, brace=brace))
我的名字是:kingname,我的參數是:{
# 使用f表達式
>>> content_name = 'detail'
>>> brace = '\{'
>>> print(f'{content_name}=({brace})$')
detail=(\{)$
不要忘記字符串拼接大家最容易犯的一個問題就是學了新的東西,就忘記了舊的,實際上用字符串拼接也能解決問題:
>>> content_name = 'detail'
>>> content_json = content_name + '=(\{)$'
>>> print(content_json)
detail=(\{)$