变量

你想创建一个内嵌变量的字符串,变量被它的值所表示的字符串替换掉。

解决方案

Python并没有在字符串中简单替换变量值提供直接的支持,但是通过使用字符的format()方法来解决这个问题,比如:

s='{name} has {n} message'
print(s.format(name='XingXing',n=35))   # ->XingXing has 35 message

或者,如果要被替换的变量能在变量域中找到,那么你可以结合使用format_map()和vars()。就像下面这样:

name='XingXing'
n=35
print(s.format_map(vars()))  # ->XingXing has 35 message

vars()还有 一个有意思的特性就是它也适用于对象实例。比如:

class Info:
    def __init__(self,name,n):
        self.name=name
        self.n=n
a=Info('XingXing',35)
print(vars(a))  # ->{'name': 'XingXing', 'n': 35}
print(s.format_map(vars(a)))  # ->XingXing has 35 message

format和format_map()的一个缺陷就是他阿门不能很好的处理变量缺失的情况。

print(s.format(name='XingXing'))

输出结果:

Traceback (most recent call last): File "D:/study/workspace/python学习/python3高级/第二章:字符串和文本/2-15字符串中插入变量.py", line 16, in <module> print(s.format(name='XingXing')) KeyError: 'n'

一种避免这种错误的方法是另外定义一个含有__missing__()方法的字典的对象,就像下面这样:

name='XingXing'
class safesub(dict):
    def __missing__(self, key):
        return "{"+key+"}"  # ->XingXing has {n} message

print(s.format_map(safesub(vars())))
n=35

如果你发现自己的代码中频繁的执行这些步骤,你可以将变量替换步骤用一个工具函数封装起来,就像下面这样写

import sys
def sub(text):
    return text.format_map(safesub(sys._getframe(1).f_locals))
name='XingXing'
n=37
print(sub('Hello {name}'))  # ->Hello XingXing
print(sub('You have {n} message.'))  # ->You have 37 message.
print(sub('Your favorite color is {color}'))  # ->Your favorite color is {color}

讨论

多年以来由于python缺乏对变量替换的内置支持而导致了各种不同的解决方案。作为本节中展示的一个可能方案,你可以有时候看到像下面这样的字符串格式:

import string
s=string.Template("$name has $n message.")
print(s.substitute(vars()))# XingXing has 37 message.

format()方法还有一个好处就是你尅获得对字符串格式化的所有支持(对齐,填充,数字格式化等),而这些特性是使用像模板字符串之类的方案不可能获得的。

__missing__()方法可以让你定义如何处理缺失的值,在SafeSub类中,这个犯法被定义为缺失的值返回一个占位符。你可以发现缺失的值会出现在结果字符串中(在调试的时候可能很有用),而不是产生一个KeyError异常。

sub()函数使用功能sys._getframe(1)返回调用者的栈帧。可以从中访问属性f_locals来获得局部变量。毫无疑问绝大部分情况下在代码中去直接操作栈帧应该是不推荐的。但是对于像字符串替换工具函数而言它是非常有用的。另外,值得注意的是f_locals是一个复制调用函数的本地变量 的字典。尽管你可以改变f_locals的内容,但是这个修改对于后面的变量访问没有任何影响。所以,虽说访问一个栈帧看上去是很邪恶的,大事对它的任何操作不会覆盖和改变调用者本地变量的值。