特殊情况
根据使用变量数据与字符串的原因,通用方法可能不适用。
如果您需要准备SQL查询
不要使用任何常规技术来组装字符串。相反,使用您的SQL库的参数化查询功能。
查询是代码,因此不应像普通文本那样考虑。使用库将确保任何插入的文本都被正确转义。如果查询的任何部分可能来自程序外部的任何方式,这就是恶意用户执行 SQL注入的机会。这被广泛认为是重要的计算机安全问题,每年给真正的公司造成巨大的损失,并给无数客户带来问题。即使您认为数据是“安全的”,使用其他方法也没有实际好处。
语法将取决于您使用的库,并且超出了本答案的范围。
如果您需要准备URL查询字符串
查看在Python中向给定的URL添加参数。不要自己做;没有实际理由使你的生活更加困难。
写入文件
虽然可以提前准备好字符串,但只需使用单独的.write
调用写入每个数据可能更简单、更节省内存。当然,非字符串仍需要在写入之前转换为字符串,这可能会使代码变得复杂。这里没有一种适合所有情况的答案,但选择不当通常不会有太大关系。
如果你只是调用print
内置的print
函数接受可变数量的参数,并且可以使用str
将任何对象转换为字符串。在尝试字符串格式化之前,请考虑是否只需传递多个参数即可完成所需操作。(您还可以使用sep
关键字参数控制参数之间的间距。)
# display a filename, as an example
print('hanning', num, '.pdf', sep='')
当然,程序拼接字符串可能有其他原因使其变得有用;在适当的情况下,请务必这样做。
重要的是要注意,print
是一个特例。只有那些明确写成这种方式的函数才能像这样工作。对于普通函数和方法,例如input
或Matplotlib图的savefig
方法,我们需要自己准备一个字符串。
字符串连接
Python支持使用+
将两个字符串连接在一起,但不能用于字符串和其他类型之间。为了解决这个问题,我们需要显式地将其他值转换为字符串:'hanning' + str(num) + '.pdf'
。
基于模板的方法
解决该问题的大多数方法都涉及到拥有某种包含“占位符”的“模板”字符串,这些“占位符”显示应添加信息的位置,然后使用某些函数或方法添加缺失的信息。
f-strings
这是可能时推荐的方法。它看起来像
f'hanning{num}.pdf'
。要插入的变量名称直接出现在字符串中。需要注意的是,实际上并不存在“f-string”这样的东西;它不是单独的类型。相反,Python会提前转换代码:
>>> def example(num):
... return f'hanning{num}.pdf'
...
>>> import dis
>>> dis.dis(example)
2 0 LOAD_CONST 1 ('hanning')
2 LOAD_FAST 0 (num)
4 FORMAT_VALUE 0
6 LOAD_CONST 2 ('.pdf')
8 BUILD_STRING 3
10 RETURN_VALUE
因为它是一种特殊的语法,可以访问其他方法中未使用的操作码。
str.format
这是在f-strings不可用时推荐的方法,主要是因为模板字符串需要事先准备好并稍后填充。它看起来像
'hanning{}.pdf'.format(num)
或者
'hanning{num}.pdf'.format(num=num)'
。在这里,
format
是字符串内置的方法,可以通过位置或关键字接受参数。
特别是对于
str.format
,有用的是要知道内置的
locals
、
globals
和
vars
函数返回将变量名映射到这些变量内容的字典。因此,我们可以使用类似
'{a}{b}{c}'.format(**locals())
的东西,
unpacking locals()
字典。
str.format_map
这是对
.format
的一种罕见变化。它看起来像
'hanning{num}.pdf'.format_map({'num': num})
。与其接受关键字参数,它接受一个映射作为单个参数。
这可能听起来并不是很有用 - 毕竟,与其使用
'hanning{num}.pdf'.format_map(my_dict)
,我们可以很容易地写成
'hanning{num}.pdf'.format(**my_dict)
。但是,对于决定值的映射而言,这是非常有用的,而不是普通的
dict
。在这些情况下,使用
**
解包可能行不通,因为键集可能不能提前确定;而基于模板进行键解包是笨拙的(想象一下:对于每个占位符都有一个单独的参数
'hanning{num}.pdf'.format(num=my_mapping[num])
)。
string.Formatter
“string”标准库模块包含一个很少使用的“Formatter”类。使用它看起来像这样:“string.Formatter().format('hanning{num}.pdf', num=num)”。模板字符串再次使用相同的语法。这显然比仅在字符串上调用“.format”更加笨重;动机是允许用户子类化“Formatter”以定义模板字符串的不同语法。
以上所有方法都使用通用的“格式化语言”(尽管“string.Formatter”允许更改它);还有许多其他可以放在“{}”中的内容。解释它如何工作超出了本答案的范围;请
参考文档。请记住,文字“{”和“}”需要通过加倍来转义。语法可能受C#的启发。
“%”运算符
这是一种传统的解决问题的方式,受到C和C++的启发。长期以来,已经被不鼓励使用,但仍得到支持。对于简单情况,它看起来像
'hanning%s.pdf' % num
。顾名思义,在模板中字面上的百分号
'%'
需要加倍进行转义。
它存在一些问题:
似乎转换说明符(在
%
后面的字母)应该与被插值的内容类型相匹配,但实际上并非如此。相反,该值将转换为指定的类型,然后从那里转换为字符串。这通常是不必要的;直接转换为字符串大多数情况下都有效,而先转换为其他类型并不能帮助大部分情况下。因此,几乎总是使用
's'
(除非您想要值的
repr
,使用
'r'
)。尽管如此,转换说明符是语法的强制部分。
元组有特殊处理:在右侧传递元组是提供多个参数的方式。这是一个丑陋的特例,必须使用它,因为我们没有使用函数调用语法。因此,如果您实际上想将元组格式化为单个占位符,则必须将其包装在1元组中。
其他序列类型
不被特殊处理,不同的行为可能会出现问题。
string.Template
“string”标准库模块包含一个很少使用的“Template”类。实例提供“substitute”和“safe_substitute”方法,与内置的“.format”类似(当参数不匹配时,“safe_substitute”将保留占位符而不是引发异常)。这也应该被视为解决问题的遗留方法。
它看起来像“string.Template('hanning$num.pdf').substitute(num=num)”,受传统Perl语法启发。显然,这种方法比“.format”方法更加笨重,因为必须在方法可用之前使用单独的类。大括号({})可以选择性地用于变量名称周围,以避免歧义。与其他方法类似,模板中的文字“$”需要加倍转义。
'foo %d, bar %d' % (foo, bar)
。 - fiedlplot.savefig('hanning{num}s.pdf'.format(**locals()))
。 - pix.format
方法。 - Karl Knechtel