如何正确保留大括号?

简介: 如何正确保留大括号?

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

自从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=(\{)$
目录
相关文章
|
15小时前
|
编译器 C++
C++ 双冒号::开头的语法,::变量名,获取全局作用域变量
C++ 双冒号::开头的语法,::变量名,获取全局作用域变量
18 0
|
6月前
数组外面包了引号,怎么去掉外面的引号,变成原来的数组
数组外面包了引号,怎么去掉外面的引号,变成原来的数组
31 0
|
15小时前
|
存储 程序员 编译器
C++注释、变量、常量、关键字、标识符、输入输出
C++注释、变量、常量、关键字、标识符、输入输出
|
15小时前
for循环去掉最后一个逗号(三种方法)
for循环去掉最后一个逗号(三种方法)
26 1
for循环去掉最后一个逗号(三种方法)
|
10月前
|
C语言
学C的第二天(变量‘补充’;简单了解常量,字符串,转义字符,注释,if选择语句,while循环语句)(1)
4.4*变量的使用(上期继续补充): 字符类型: %c - 字符类型 %d - 整型 %s - 字符串 %f - float类型 %lf - double类型
|
Python
vscode编译 不允许使用与号(&)。& 运算符是为将来使用而保留的;请用双引号将与号引起来(\“&\“),以将其作为字符串的一部分传递
vscode编译 不允许使用与号(&)。& 运算符是为将来使用而保留的;请用双引号将与号引起来(\“&\“),以将其作为字符串的一部分传递
389 0
C++中的保留字、C++11的原始字面量
C++中的保留字、C++11的原始字面量
C++中的保留字、C++11的原始字面量
|
JavaScript 前端开发
JavaScript 里变量名前面加了大括号代表什么含义
JavaScript 里变量名前面加了大括号代表什么含义
353 0
JavaScript 里变量名前面加了大括号代表什么含义
复习C部分:1.什么是常量 2.初时字符串 3.初识转义字符 4.注释 5.初识选择语句 6.初识循环语句 7.初识函数和数组 8.初识操作符 9.初始操作符2
复习C部分:1.什么是常量 2.初时字符串 3.初识转义字符 4.注释 5.初识选择语句 6.初识循环语句 7.初识函数和数组 8.初识操作符 9.初始操作符2
81 0
复习C部分:1.什么是常量 2.初时字符串 3.初识转义字符 4.注释 5.初识选择语句 6.初识循环语句 7.初识函数和数组 8.初识操作符 9.初始操作符2