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