12

如何正确保留大括号?

 3 years ago
source link: https://mp.weixin.qq.com/s?__biz=MzI2MzEwNTY3OQ%3D%3D&%3Bmid=2648980090&%3Bidx=1&%3Bsn=86492da3fd2ae2cbc8d0fa49b7a27d07
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

biMfimj.png!mobile

摄影:产品经理

酸梅的味道让我想到了产品经理做的小吊梨汤

自从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=(\{)$

j6VVnai.gif!mobile

YBnEnuv.png!mobile


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK