dict 是这个工作的错误工具。 dict 用于将特定键映射到特定值。那不是你正在做的;您正在尝试映射范围。这里有一些更直接的选项。
使用if 块
对于一小部分值,请使用明显而直接的 if 块:
def get_stealthiness(roll):
if 1 <= roll < 6:
return 'You are about as stealthy as thunderstorm.'
elif 6 <= roll < 11:
return 'You tip-toe through the crowd of walkers, while loudly calling them names.'
elif 11 <= roll < 16:
return 'You are quiet, and deliberate, but still you smell.'
elif 16 <= roll <= 20:
return 'You move like a ninja, but attracting a handful of walkers was inevitable.'
else:
raise ValueError('Unsupported roll: {}'.format(roll))
stealth_roll = randint(1, 20)
print(get_stealthiness(stealth_roll))
这种方法绝对没有错。它真的不需要更复杂。这比在此处尝试使用dict 更直观、更容易理解、更高效。
这样做也使边界处理更加明显。在我上面提供的代码中,您可以快速发现范围是否在每个位置使用< 或<=。上面的代码还会为 1 到 20 之外的值抛出有意义的错误消息。它还免费支持非整数输入,尽管您可能并不关心。
将每个值映射到结果
您可以将问题重新表述为确实将特定键映射到特定值的问题,而不是尝试使用键的范围。为此,您可以遍历范围并生成包含所有可能值的完整 dict:
OUTCOMES = {}
for i in range(1, 6):
OUTCOMES[i] = 'You are about as stealthy as thunderstorm.'
for i in range(6, 11):
OUTCOMES[i] = 'You tip-toe through the crowd of walkers, while loudly calling them names.'
for i in range(11, 16):
OUTCOMES[i] = 'You are quiet, and deliberate, but still you smell.'
for i in range(16, 21):
OUTCOMES[i] = 'You move like a ninja, but attracting a handful of walkers was inevitable.'
def get_stealthiness(roll):
if roll not in OUTCOMES.keys():
raise ValueError('Unsupported roll: {}'.format(roll))
return OUTCOMES[roll]
stealth_roll = randint(1, 20)
print(get_stealthiness(stealth_roll))
在这种情况下,我们使用范围来生成一个dict,我们可以在其中查找结果。我们将每次滚动映射到一个结果,多次重复使用相同的结果。这不那么简单。从中辨别每个结果的概率并不容易。但至少它正确地使用了dict:它将一个键映射到一个值。
根据概率计算
您可以根据概率计算选择结果。基本思想是计算“累积”概率(您已经拥有滚动值的顶端),然后循环直到累积概率超过随机值。有很多关于如何去做的想法here。
一些简单的选项是:
-
numpy.random.choice
-
一个循环:
# Must be in order of cummulative weight
OUTCOME_WITH_CUM_WEIGHT = [
('You are about as stealthy as thunderstorm.', 5),
('You tip-toe through the crowd of walkers, while loudly calling them names.', 10),
('You are quiet, and deliberate, but still you smell.', 15),
('You move like a ninja, but attracting a handful of walkers was inevitable.', 20),
]
def get_stealthiness(roll):
if 1 > roll or 20 < roll:
raise ValueError('Unsupported roll: {}'.format(roll))
for stealthiness, cumweight in OUTCOME_WITH_CUM_WEIGHT:
if roll <= cumweight:
return stealthiness
raise Exception('Reached end of get_stealthiness without returning. This is a bug. roll was ' + str(roll))
stealth_roll = randint(1, 20)
print(get_stealthiness(stealth_roll))
-
random.choices(需要 Python 3.6 或更高版本)
OUTCOMES_SENTENCES = [
'You are about as stealthy as thunderstorm.',
'You tip-toe through the crowd of walkers, while loudly calling them names.',
'You are quiet, and deliberate, but still you smell.',
'You move like a ninja, but attracting a handful of walkers was inevitable.',
]
OUTCOME_CUMULATIVE_WEIGHTS = [5, 10, 15, 20]
def make_stealth_roll():
return random.choices(
population=OUTCOMES_SENTENCES,
cum_weights=OUTCOME_CUMULATIVE_WEIGHTS,
)
print(make_stealth_roll())
有些方法的缺点是无法控制实际的数字滚动,但它们的实现和维护要简单得多。
Python 风格
“Pythonic”意味着让你的代码简单易懂。这意味着将结构用于其设计目的。 dict 不是为你正在做的事情而设计的。
速度
所有这些选项都比较快。根据raratiru的comment,RangeDict是当时最快的答案。但是,我的testing script 表明,除了numpy.random.choice,我建议的所有选项都快了大约 40% 到 50%:
get_stealthiness_rangedict(randint(1, 20)): 3.4458323369617574 µs per loop
get_stealthiness_ifs(randint(1, 20)): 1.8013543629786 µs per loop
get_stealthiness_dict(randint(1, 20)): 1.9512669100076891 µs per loop
get_stealthiness_cumweight(randint(1, 20)): 1.9908560069743544 µs per loop
make_stealth_roll_randomchoice(): 2.037966169009451 µs per loop
make_stealth_roll_numpychoice(): 38.046008297998924 µs per loop
numpy.choice all at once: 0.5016623589908704 µs per loop
如果你一次得到一个结果,numpy 会慢一个数量级;但是,如果您批量生成结果,速度会快一个数量级。