Python 的“开关语句”和字符串格式化

1 投票
3 回答
1177 浏览
提问于 2025-04-16 04:32

我正在尝试使用字典来做一个 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 个回答

1

我觉得你想做的是增加一层功能,根据字典中是否有某些键来选择格式化方式。

你可以使用类似下面的代码

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,把这个功能做成那个类的方法。

1

我觉得你想要的描述字典应该只包含格式字符串,比如:

descriptions = {
   'player_joined_clan': "%(player)s joined clan %(clan)s.",
   #etc...
  }

然后你可以有一个函数,它接收一个描述的键和一个特定事件的数据字典,这样就能生成格式化的消息,类似于:

def getMessage( key, eventDataDict ):
  return descriptions[key] % eventDataDict

实际上,我认为你写的例子中,token1 等等会在声明 descriptions 的时候被计算出来——而我猜你想要的是在不同时间用这些变量的不同值来格式化消息。

3

如果我理解得没错,我建议你使用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

撰写回答