文章目录
- 1. 旧式的字符串格式化
- 2. 新式的字符串格式化
- 3. 字符串插值法(python 3.6+)—— f-string
- 4. Template Strings
- 我应该选择哪一种字符串格式化方法呢?
参考:
《Python Tricks: A Buffet of Awesome Python Features》
1. 旧式的字符串格式化
>>> errno = 50159747054
>>> name = 'Bob'
>>> 'Hello, %s' % name
'Hello, Bob'
>>> '%x' % errno # 将整数转为十六进制,并输出为字符串
'badc0ffee'
如果您想在一个字符串中进行多个替换,那么“旧版”字符串格式化语法会发生轻微的变化。因为% -运算符只接受一个参数,所以需要将右侧封装在一个元组中
>>> 'Hey %s, there is a 0x%x error!' % (name, errno)
'Hey Bob, there is a 0xbadc0ffee error!'
如果将映射传递给% -运算符,还可以在格式字符串中按名称引用变量替换
>>> 'Hey %(name)s, there is a 0x%(errno)x error!' % {... "name": name, "errno": errno }
'Hey Bob, there is a 0xbadc0ffee error!'
参考:
printf-style-string-formatting
2. 新式的字符串格式化
利用format来格式化字符串
>>> 'Hello, {}'.format(name)
'Hello, Bob'
format支持给指定位置的变量赋值
>>> 'Hey {name}, there is a 0x{errno:x} error!'.format(name=name, errno=errno)
'Hey Bob, there is a 0xbadc0ffee error!'
在format中,把errno和name调换一下位置,也依然能输出正确的结果
>>> 'Hey {name}, there is a 0x{errno:x} error!'.format(errno=errno, name=name)
'Hey Bob, there is a 0xbadc0ffee error!'
errno后面的 :x
表示的是输出一个十六进制的数
>>> "int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}".format(42)
'int: 42; hex: 2a; oct: 52; bin: 101010'
>>> # with 0x, 0o, or 0b as prefix:
>>> "int: {0:d}; hex: {0:#x}; oct: {0:#o}; bin: {0:#b}".format(42)
'int: 42; hex: 0x2a; oct: 0o52; bin: 0b101010'
参考:
str.formatstring-formattingformat string syntax
3. 字符串插值法(python 3.6+)—— f-string
python3.6 增加了一种新的方式来格式化字符串。这种方式能够让你在字符串常量中嵌入python表达式。
>>> f'Hello, {name}!'
'Hello, Bob!'
因为这种方法能够嵌入python表达式,你甚至可以用来来做内联算术。
>>> a = 5
>>> b = 10
>>> f'Five plus ten is {a + b} and not {2 * (a + b)}.'
'Five plus ten is 15 and not 30.'
>>> def greet(name, question):
... return f"Hello, {name}! How's it {question}?"
...
>>> greet('Bob', 'going')
"Hello, Bob! How's it going?"
那么这个函数是如何做到的呢?实际上,f
这个字符串所做的事类似于下面这个操作
>>> def greet(name, question):
... return ("Hello, " + name + "! How's it " +question + "?")
在f-字符串
中,如果要输出其他进制的数字,比如十六进制,就要在:
后面加上#x
。官网上写的是:#0x
,经过打印,发现效果是一样的。
>>> f"Hey {name}, there's a {errno:#x} error!"
"Hey Bob, there's a 0xbadc0ffee error!"
>>> f"Hey {name}, there's a {errno:#0x} error!"
"Hey Bob, there's a 0xbadc0ffee error!"
4. Template Strings
Template Strings,较为简单。
>>> from string import Template
>>> t = Template('Hey, $name!')
>>> t.substitute(name=name)
'Hey, Bob!'
我们需要做的,就是从python内置的string库中导入Template
,然后定义一个字符串,把这个字符串放入Template()中,再执行substitute()方法。
>>> templ_string = 'Hey $name, there is a $error error!'
>>> Template(templ_string).substitute(name=name, error=hex(errno))
'Hey Bob, there is a 0xbadc0ffee error!'
应用Template Strings的最好的例子,是当你正在处理用户生成的字符串时。
举个例子,用户是有可能通过格式化字符串来获得程序中的任意变量的。也就是,如果一个恶意用户能提供一个格式化字符串,他们也能泄露密钥或者其他敏感信息。
The more complex formatting mini-languages of other string format-
ting techniques might introduce security vulnerabilities to your pro-
grams. For example, it’s possible for format strings to access arbitrary
variables in your program.
That means, if a malicious user can supply a format string they can
also potentially leak secret keys and other sensible information!
Here’s a simple proof of concept of how this attack might be used:
>>> SECRET = 'this-is-a-secret'
>>> class Error:
... def __init__(self):
... pass
>>> err = Error()
>>> user_input = '{error.__init__.__globals__[SECRET]}'
# Uh-oh...
>>> user_input.format(error=err)
'this-is-a-secret'
See how the hypothetical attacker was able to
使用 Template 能够阻止上述事情的发生。
>>> user_input = '${error.__init__.__globals__[SECRET]}'
>>> Template(user_input).substitute(error=err)
ValueError:
"Invalid placeholder in string: line 1, col 1"
我应该选择哪一种字符串格式化方法呢?
- 如果格式化字符串是用户提供的,就使用Template Strings,来避免安全问题
- 如果使用的是python 3.6+,就用字符串插值的方法
- 如果使用的版本低于python 3.6,那么使用第二种,也就是新式字符串格式化方法