如何正確保留大括號(hào)?
自從Python 3.6開始,引入了f表達(dá)式(f-string),這使得Python在填充字符串時(shí)可以進(jìn)行一些簡(jiǎn)單的計(jì)算。并且f表達(dá)式的運(yùn)算速度是字符串.format方法的很多倍。
無論是f表達(dá)式還是字符串的.format方法,我們都可以使用大括號(hào)作為占位符,來填充數(shù)據(jù)。例如:
- >>> name = 'kingname'
- >>> print(f'我的名字是:{name}')
- 我的名字是:kingname
- >>> print(f'1+1的結(jié)果為:{1 + 1}')
- 1+1的結(jié)果為:2
- >>> salary = 999999
- >>> print('我的月薪是:{salary}'.format(salary=salary))
- 我的月薪是:999999
但現(xiàn)在問題來了,如果我希望在使用f表達(dá)式或者.format方法填充內(nèi)容的同時(shí),又能保留大括號(hào)應(yīng)該怎么辦呢?
舉個(gè)例子,在寫爬蟲的時(shí)候,我需要使用正則表達(dá)式從當(dāng)前URL中提取當(dāng)前的頁數(shù):page=\d{0,3}。但是,對(duì)于不同的網(wǎng)站,表示頁數(shù)的這個(gè)參數(shù)名可能是不一樣的,有些是page=xxx,有些是Pag=xxx,有些是pageNo=xxx,有些是p=xxx。所以我想動(dòng)態(tài)生成這個(gè)正則表達(dá)式。
如果我們直接使用f表達(dá)式或者.format方法,就會(huì)報(bào)錯(cuò):
- >>> 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'
為了能夠正常生成正則表達(dá)式,可能有人會(huì)想到使用古老的%s占位符:
- >>> page_name = 'page'
- >>> page_regex_template = '%s=\d{0,3}'
- >>> print(page_regex_template % page_name)
- page=\d{0,3}
雖然確實(shí)可行,但是混用兩種填充字符串的方法,代碼會(huì)變得不好維護(hù),而且%s這種占位符填充速度也非常慢。
實(shí)際上,在Python的f表達(dá)式和.format方法中,如果你需要保留大括號(hào),那么只需要寫成大括號(hào)套大括號(hào)的形式就行了:
- >>> page_name = 'page'
- >>> page_regex_template = '{page_name}=\d{{0,3}}'
- >>> print(page_regex_template.format(page_name=page_name))
- page=\d{0,3}
大括號(hào)里面的第一層大括號(hào)會(huì)自動(dòng)失效,變成普通的字符。但如果是大括號(hào)套大括號(hào)套大括號(hào),那么最里面的一對(duì)大括號(hào)會(huì)繼續(xù)生效充當(dāng)占位符,例如:
- >>> 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}
總結(jié)起來就是,如果從外向內(nèi)數(shù),如果最外層大括號(hào)稱為第1層,那么,第奇數(shù)層的大括號(hào)用來填充數(shù)據(jù),第偶數(shù)層的大括號(hào)就是普通的字符。因此,如果不考慮代碼可讀性,如果我們需要最終生成的字符串本身就是嵌套大括號(hào)的形式,我們還可以進(jìn)一步寫成:
- >>> ugly_string = '2層嵌套大括號(hào):{{{{{variable}}}}}'
- >>> print(ugly_string.format(variable=variable))
- 2層嵌套大括號(hào):{{name}}
- >>> ugly_string = '3層嵌套大括號(hào):{{{{{{{variable}}}}}}}'
- >>> print(ugly_string.format(variable=variable))
- 3層嵌套大括號(hào):{{{name}}}
假設(shè)我們希望最終輸出的字符串里面,保留n層大括號(hào),那么在代碼里面,我們需要寫2n + 1層大括號(hào)。大家也看出來了,如果你要這樣寫,數(shù)大括號(hào)的個(gè)數(shù)都要把你的眼鏡數(shù)瞎。所以,在實(shí)際開發(fā)中,大括號(hào)的層數(shù)絕對(duì)不要超過2層。
以下內(nèi)容供學(xué)有余力的同學(xué)閱讀。
上面講到的方法,適用于大括號(hào)成對(duì)出現(xiàn)的情況,如果大括號(hào)只有半邊,例如有些網(wǎng)站的正文信息是以JSON格式寫在源代碼里面的,于是我們可以使用正則表達(dá)式提取出包含正文的JSON然后進(jìn)一步處理,一般來說,正則表達(dá)式可能是這樣的:content=(\{);。不過,有的網(wǎng)站用的單詞是content,有的網(wǎng)站用的是detail,所以這個(gè)地方需要填充,如果我們像往常那樣寫,那么還是會(huì)報(bào)錯(cuò),例如:
- >>> 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
套兩層還是錯(cuò)。
如果字符串不含反斜杠,我們可以使用f表達(dá)式配合引號(hào)包住半邊大括號(hào)來實(shí)現(xiàn),例如:
- >>> name = 'kingname'
- >>> print(f'我的名字是:{name},我的參數(shù)是:{"{"}')
- 我的名字是:kingname,我的參數(shù)是:{
但問題是,f表達(dá)式里面是不允許出現(xiàn)反斜杠的,否則會(huì)報(bào)錯(cuò):
- >>> content_name = 'detail'
- >>> content_regex = f'{content_name}=({\"{"})$'
- File "<stdin>", line 1
- SyntaxError: f-string expression part cannot include a backslash
而.format方法支持反斜杠,但它又不支持引號(hào)包住單邊大括號(hào)的寫法:
- >>> name = 'kingname'
- >>> print('我的名字是:{name},我的參數(shù)是:{"{"}'.format(name=name))
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- ValueError: unexpected '{' in field name
遇到這種情況,我們應(yīng)該怎么解決呢?只要把思路放開,靈活變通,能找出很多方法,這里僅舉兩例:
把大括號(hào)放到值里面
把思路調(diào)整過來,既然大括號(hào)不能放在句子模板里面,那我們就放在被填充的值里面:
- # 使用.format方法
- >>> name = 'kingname'
- >>> brace = '{'
- >>> print('我的名字是:{name},我的參數(shù)是:{brace}'.format(name=name, brace=brace))
- 我的名字是:kingname,我的參數(shù)是:{
- # 使用f表達(dá)式
- >>> content_name = 'detail'
- >>> brace = '\{'
- >>> print(f'{content_name}=({brace})$')
- detail=(\{)$
不要忘記字符串拼接
大家最容易犯的一個(gè)問題就是學(xué)了新的東西,就忘記了舊的,實(shí)際上用字符串拼接也能解決問題:
- >>> content_name = 'detail'
- >>> content_json = content_name + '=(\{)$'
- >>> print(content_json)
- detail=(\{)$
本文轉(zhuǎn)載自微信公眾號(hào)「未聞Code」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系未聞Code公眾號(hào)。