Python 的“开关语句”和字符串格式化
我正在尝试使用字典来做一个 switch 语句,并且答案需要是一个格式化的字符串,比如说:
descriptions = {
'player_joined_clan': "%(player)s joined clan %(clan)s." % {"player": token1, "clan": token2},
#etc...
}
现在,如果这两个标记总是被定义,那这个方法是可以工作的,但实际上并不是这样。而且我觉得它会格式化字典里的所有字符串,这其实是不必要的,应该只格式化需要的那个。所以,我想出了一个非常不寻常且有点傻的解决方案,使用了 lambda。
descriptions = {
'player_joined_clan': lambda x: "%(player)s joined clan %(clan)s." % {"player": token1, "clan": token2},
}
我可以用 descriptions["player_joined_clan"](0)
来调用它,效果是如我所愿,但这看起来太丑了,而且不太直观……我明显在这里漏掉了什么。
任何建议都很感激,提前谢谢大家。
3 个回答
我觉得你想做的是增加一层功能,根据字典中是否有某些键来选择格式化方式。
你可以使用类似下面的代码
formatters = {
set('player', 'team'): "{player} joined {team}".format,
set('player'): "Hello {player}.".format,
set('team'): "{team} FTW!".format,
set(): "Something happened.".format}
来确定使用哪个格式字符串。注意,我用的是新式格式字符串,它适用于str.format
,而不是旧式的格式方法。新式的格式字符串比旧的template % data
方式更推荐。
然后你可以这样获取一个格式化函数
fmt = descriptions[set(eventDataDict.keys())]
接着调用
formatted_greeting = fmt(eventDataDict)
这种方法比起使用条件语句要差,因为没有default
的情况;如果你需要这种情况,可以把访问descriptions
的部分放在一个try
... except KeyError
的结构里,可能都放在一个叫format_description
的函数里。根据你的代码结构,你可能还想继承dict
,把这个功能做成那个类的方法。
我觉得你想要的描述字典应该只包含格式字符串,比如:
descriptions = {
'player_joined_clan': "%(player)s joined clan %(clan)s.",
#etc...
}
然后你可以有一个函数,它接收一个描述的键和一个特定事件的数据字典,这样就能生成格式化的消息,类似于:
def getMessage( key, eventDataDict ):
return descriptions[key] % eventDataDict
实际上,我认为你写的例子中,token1
等等会在声明 descriptions
的时候被计算出来——而我猜你想要的是在不同时间用这些变量的不同值来格式化消息。
如果我理解得没错,我建议你使用collections.defaultdict。这其实不算是一个“switch”语句,但我觉得最终效果和你想要的差不多。
我可以通过完整的代码、数据和应用来更好地解释。显然,关键的那一行就是 defaultdict 的定义。
>>> import collections
>>>
>>> descriptions = {
... 'player_joined_clan' : '%(player)s joined clan %(clan)s',
... 'player_left' : '%(player)s left',
... 'player_hit_player' : '%(player)s (of %(clan)s) hit %(player2)s (of %(clan2)s)',
... }
>>>
>>> data = [
... {'player': 'PlayerA'},
... {'player': 'PlayerB', 'clan' : 'ClanB'},
... {'clan' : 'ClanC'},
... {'clan' : 'ClanDA', 'player2': 'PlayerDB'},
... ]
>>>
>>> for item in data:
... print item
... item = collections.defaultdict(lambda : '"<unknown>"', **item)
... for key in descriptions:
... print ' %s: %s' % (key, descriptions[key] % item)
... print
...
{'player': 'PlayerA'}
player_joined_clan: PlayerA joined clan "<unknown>"
player_left: PlayerA left
player_hit_player: PlayerA (of "<unknown>") hit "<unknown>" (of "<unknown>")
{'clan': 'ClanB', 'player': 'PlayerB'}
player_joined_clan: PlayerB joined clan ClanB
player_left: PlayerB left
player_hit_player: PlayerB (of ClanB) hit "<unknown>" (of "<unknown>")
{'clan': 'ClanC'}
player_joined_clan: "<unknown>" joined clan ClanC
player_left: "<unknown>" left
player_hit_player: "<unknown>" (of ClanC) hit "<unknown>" (of "<unknown>")
{'clan': 'ClanDA', 'player2': 'PlayerDB'}
player_joined_clan: "<unknown>" joined clan ClanDA
player_left: "<unknown>" left
player_hit_player: "<unknown>" (of ClanDA) hit PlayerDB (of "<unknown>")
或者,如果你想要比简单的一个字符串的 lambda 更灵活一些,你可以定义自己的 defaultdict 类,比如:
class my_defaultdict(collections.defaultdict):
def __missing__(self, key):
return '<unknown %s>' % key
把那一行改成使用你自己的类,而不是默认的那个:
#item = collections.defaultdict(lambda : '"<unknown>"', **item)
item = my_defaultdict(**item)
然后,瞧,输出结果就是:
{'player': 'PlayerA'}
player_joined_clan: PlayerA joined clan <unknown clan>
player_left: PlayerA left
player_hit_player: PlayerA (of <unknown clan>) hit <unknown player2> (of <unknown clan2>)
{'clan': 'ClanB', 'player': 'PlayerB'}
player_joined_clan: PlayerB joined clan ClanB
player_left: PlayerB left
player_hit_player: PlayerB (of ClanB) hit <unknown player2> (of <unknown clan2>)
{'clan': 'ClanC'}
player_joined_clan: <unknown player> joined clan ClanC
player_left: <unknown player> left
player_hit_player: <unknown player> (of ClanC) hit <unknown player2> (of <unknown clan2>)
{'clan': 'ClanDA', 'player2': 'PlayerDB'}
player_joined_clan: <unknown player> joined clan ClanDA
player_left: <unknown player> left
player_hit_player: <unknown player> (of ClanDA) hit PlayerDB (of <unknown clan2>)
想要更多例子,可以查看collections.defaultdict的文档。
编辑:
我忘了提到这个__missing__
功能在 Python 2.5 中就已经加入到标准的dict
类里了。所以其实有一个更简单的方法,根本不需要用到collections.defaultdict
——只需继承dict
类:
class my_defaultdict(dict):
def __missing__(self, key):
return '<unknown %s>' % key