Python格式化字符串中标点符号的使用

5

我很困惑Python中的.format机制。(我目前使用的是2.7.6版本)

这个显然可以工作:

>>> "hello {test1}".format(**{'test1': 'world'})
'hello world'

同样适用于:

>>> "hello {test_1}".format(**{'test_1': 'world'})
'hello world'

但是两者都不:

>>> "hello {test:1}".format(**{'test:1': 'world'})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'test'

nor:

>>> "hello {test.1}".format(**{'test.1': 'world'})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'test'

工作。

但由于某些原因,以下内容可以:

>>> "hello {test:1}".format(**{'test': 'world'})
'hello world'

看起来在被替换的字符串中,变量名不能包含冒号:或句点.。有没有办法转义这些字符?我想要从字典中替换的字符串偶尔会包含句点、冒号或两者都有。

3个回答

4

这是因为您可以使用格式化迷你语言来访问对象的属性。例如,在自定义类上进行工作时,我经常使用它。假设我已经为需要工作的每台计算机定义了一个类。

class Computer(object):
    def __init__(self,IP):
        self.IP = IP

现在我想对整个计算机范围执行某些操作。

list_comps = [Computer(name,"192.168.1.{}".format(IP)) for IP in range(12)]

for comp in list_comps:
    frobnicate(comp) # do something to it
    print("Frobnicating the computer located at {comp.IP}".format(comp=comp))

现在它将打印出来。
Frobnicating the computer located at 192.168.1.0
Frobnicating the computer located at 192.168.1.1
Frobnicating the computer located at 192.168.1.2 # etc etc

因为每次它都会找到我传递给格式化程序的对象(comp),获取其属性IP,然后使用该属性代替。在你的示例中,你给了格式化程序一个看起来像是属性访问器(.)的东西,因此它尝试访问给定访问器之前的对象,然后查找其定义的属性。
你的最终示例有效,因为它正在寻找test,并且找到了它!:符号对于格式化程序是特殊的,因为它标记了kwarg的结尾和格式化迷你语言的开始。例如:
>>> x = 12.34567
>>> print("{x:.2f}".format(x))
12.34
:后面的.2f表示字符串格式化程序应将参数x视为float类型,并在小数点后保留2位。这个用法在官方文档中有详细的介绍,我强烈建议您花一些时间看一下,收藏起来以备将来使用!它非常有帮助!

谢谢@adsmith。我理解你所举的用例,但这是否意味着无法避免那些 :. ?在使用.format()之前,我需要以某种方式重新格式化我的文本以将它们删除吗? - not link
@notlink 老实说,我不知道!我从来没有尝试过在格式化字符串中转义 :。我的直觉告诉我你不能这样做,但是你可以试一下看看! - Adam Smith
哎呀,看起来这个用例已经有文档了。"由于arg_name没有引号包围,所以无法在格式化字符串中指定任意字典键(例如字符串'10'或':-]')"。 - Adam Smith
@notlink 只是为了好玩,试试这个代码 print "你好 {test:20},最近怎么样?".format(**{'test':'世界'}),你就会知道 {test:1} 到底是做什么的了。 - Adam Smith

3
@adsmith没有提到的是格式名称必须是有效的Python标识符。因此,您可以包含下划线和数字,但不能包含(带引号的)冒号,因为它不能成为标识符的一部分。
您能做什么?我会考虑重命名您的字典对象,或者调整您的格式字符串以使用位置参数。
如果由于数据的性质而不切实际,或者如果您只想按照自己的方式进行操作,则Formatter类就是您的朋友。它允许您使用自己的parse()方法覆盖Python的模板字符串解析器。既然您似乎实际上不需要花括号内的格式规范(例如将某些内容填充到6个空格中的{test:6d}),那么您所要做的就是构建一个将整个格式字符串视为字段名称的parse方法,其中包含空的format_spec和conversion。
编辑:这似乎是一个有趣的尝试,所以这里是一个简单的工作实现。
import string

class myFormatter(string.Formatter):
    def parse(self, fstring):
        if fstring is None:  # we also get called with the (null) format spec, for some reason
            return
        parts = fstring.split("}")
        for part in parts:
            if "{" in part:
                literal, fieldname = part.split("{")
                yield (literal, fieldname, None, None)
            else:
                yield (part, None, None, None)

使用方法如下:

>>> custom = myFormatter()
>>> custom.format("hello {test:1}", [], **{'test:1': 'world'})
'hello world'

更好的方式是这样的:
print custom.vformat(templatestring, [], valuedict)

谢谢@alexis。短期内,我想我会做类似这样的事情"{test.1}".replace('.','_').format(**{'test_1':'hello'}),但对于我的目的来说,深入挖掘parse()可能是更好的长期解决方案。 我很感谢你的建议。 - not link

1

好的,您查看了文档吗?

replacement_field ::=  "{" [field_name] ["!" conversion] [":" format_spec] "}"
field_name        ::=  arg_name ("." attribute_name | "[" element_index "]")*

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接