所以,我有一个想法,我可以使用一系列数字作为字典中单个值的关键。
我写了下面的代码,但我无法让它工作。它甚至可能吗?
stealth_roll = randint(1, 20)
# select from a dictionary of 4 responses using one of four ranges.
## not working.
stealth_check = {
range(1, 6) : 'You are about as stealthy as thunderstorm.',
range(6, 11) : 'You tip-toe through the crowd of walkers, while loudly calling them names.',
range(11, 16) : 'You are quiet, and deliberate, but still you smell.',
range(16, 20) : 'You move like a ninja, but attracting a handful of walkers was inevitable.'
}
print stealth_check[stealth_roll]
答案 0 :(得分:14)
可以在Python 3上使用 - 如果使用xrange
而不是range
,则可以在Python 2上使用:
stealth_check = {
xrange(1, 6) : 'You are about as stealthy as thunderstorm.', #...
}
然而,您尝试使用它的方式却无法奏效。您可以迭代键,如下所示:
for key in stealth_check:
if stealth_roll in key:
print stealth_check[key]
break
这不是很好的表现(O(n)),但如果它是一个像你这样的小字典就可以了。如果您真的想这样做,我会将dict
子类化为自动工作:
class RangeDict(dict):
def __getitem__(self, item):
if type(item) != range: # or xrange in Python 2
for key in self:
if item in key:
return self[key]
else:
return super().__getitem__(item)
stealth_check = RangeDict({range(1,6): 'thunderstorm', range(6,11): 'tip-toe'})
stealth_roll = 8
print(stealth_check[stealth_roll]) # prints 'tip-toe'
答案 1 :(得分:7)
除非您希望范围本身成为关键,否则您无法直接从范围构建字典。我不认为你想要那个。要获得范围内每种可能性的单独条目:
stealth_check = dict(
[(n, 'You are about as stealthy as thunderstorm.')
for n in range(1, 6)] +
[(n, 'You tip-toe through the crowd of walkers, while loudly calling them names.')
for n in range(6, 11)] +
[(n, 'You are quiet, and deliberate, but still you smell.')
for n in range(11, 16)] +
[(n, 'You move like a ninja, but attracting a handful of walkers was inevitable.')
for n in range(16, 20)]
)
如果你的dict
被一小部分整数索引,你真的应该考虑使用list
代替:
stealth_check = [None]
stealth_check[1:6] = (6 - 1) * ['You are about as stealthy as thunderstorm.']
stealth_check[6:11] = (11 - 6) * ['You tip-toe through the crowd of walkers, while loudly calling them names.']
stealth_check[11:16] = (16 - 11) * ['You are quiet, and deliberate, but still you smell.']
stealth_check[16:20] = (20 - 16) * ['You move like a ninja, but attracting a handful of walkers was inevitable.']
答案 2 :(得分:6)
是的,只有当您将range
列表转换为不可变tuple
时才可以,因此它们可以播放并作为词典的键接受:
stealth_check = {
tuple(range(1, 6)) : 'You are about as stealthy as thunderstorm.',
编辑:实际上它在Python 3中有效,因为range
是一个不可变的序列类型,并且生成一个不可变的tuple
,而不是像L3viathan所说的那样list
。
但是你不能用一个整数作为键来访问它们。你的最后一行不会起作用。
我花了一些时间来创建一个无论值是什么都可以工作的解决方案(只要行不是"加权"选择更大的范围,选择字典中的一个条目就可以工作。
它在排序的键上调用bisect
来查找插入点,稍微破解它,并在字典中找到最佳值,O(log(N))
复杂度,这意味着它可以处理一个非常大的列表(这里可能有点太多:)但在这种情况下字典也太多了)
from random import randint
import bisect
stealth_roll = randint(1, 20)
# select from a dictionary of 4 responses using one of four thresholds.
stealth_check = {
1 : 'You are about as stealthy as thunderstorm.',
6 : 'You tip-toe through the crowd of walkers, while loudly calling them names.',
11 : 'You are quiet, and deliberate, but still you smell.',
16 : 'You move like a ninja, but attracting a handful of walkers was inevitable.'
}
sorted_keys = sorted(stealth_check.keys())
insertion_point = bisect.bisect_left(sorted_keys,stealth_roll)
# adjust, as bisect returns not exactly what we want
if insertion_point==len(sorted_keys) or sorted_keys[insertion_point]!=stealth_roll:
insertion_point-=1
print(insertion_point,stealth_roll,stealth_check[sorted_keys[insertion_point]])
答案 3 :(得分:3)
我编写了一个RangeKeyDict类来处理这样的情况,它更通用且易于使用。如需使用,请检查__main __
中的代码使用以下方式安装它:
pip install range-key-dict
用法:
from range_key_dict import RangeKeyDict
if __name__ == '__main__':
range_key_dict = RangeKeyDict({
(0, 100): 'A',
(100, 200): 'B',
(200, 300): 'C',
})
# test normal case
assert range_key_dict[70] == 'A'
assert range_key_dict[170] == 'B'
assert range_key_dict[270] == 'C'
# test case when the number is float
assert range_key_dict[70.5] == 'A'
# test case not in the range, with default value
assert range_key_dict.get(1000, 'D') == 'D'
答案 4 :(得分:3)
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())
有些人有把握实际数字卷的缺点,但实施和维护起来要简单得多。
“Pythonic”意味着保持您的代码简单易用。它意味着将结构用于其设计目的。 dict
并非专为你正在做的事而设计。
所有这些选项都相对较快。根据{{3}}的raratiru,RangeDict
是当时最快的答案。但是,我的comment显示除了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会慢一个数量级;但是,如果您批量生成结果,它会快一个数量级。
答案 5 :(得分:2)
stealth_check = {
0 : 'You are about as stealthy as thunderstorm.',
1 : 'You tip-toe through the crowd of walkers, while loudly calling them names.',
2 : 'You are quiet, and deliberate, but still you smell.',
3 : 'You move like a ninja, but attracting a handful of walkers was inevitable.'
}
stealth_roll = randint(0, len(stealth_check))
return stealth_check[stealth_roll]
答案 6 :(得分:2)
这种方法可以达到你想要的效果,最后一行可行(假设range
和print
的Py3行为):
def extend_dict(d, value, x):
for a in x:
d[a] = value
stealth_roll = randint(1, 20)
# select from a dictionary of 4 responses using one of four ranges.
## not working.
stealth_check = {}
extend_dict(stealth_check,'You are about as stealthy as thunderstorm.',range(1,6))
extend_dict(stealth_check,'You tip-toe through the crowd of walkers, while loudly calling them names.',range(6,11))
extend_dict(stealth_check,'You are quiet, and deliberate, but still you smell.',range(11,16))
extend_dict(stealth_check,'You move like a ninja, but attracting a handful of walkers was inevitable.',range(16,20))
print(stealth_check[stealth_roll])
顺便说一句,如果你要模拟一个20边的骰子,你需要最终的指数为21而不是20(因为20不在范围内(1,20))。
答案 7 :(得分:1)
以下可能是最有效的将randint映射到具有固定概率的一组固定类别字符串之一。
$start = strtotime('2016-09-05');
$end = strtotime('2016-09-30');
while($start <= $end) {
if(date("D", $start) == "Mon"){
$mondays[] = $start;
}
elseif(date("D", $start) == "Tue"){
$tuesdays[] = $start;
}
$start += 86400;
}
foreach ($mondays as $item){
echo date("D. Y-m-d", $item);
echo "<br>";
}
echo "<br>";
foreach ($tuesdays as $item){
echo date("D. Y-m-d", $item);
echo "<br>";
}
答案 8 :(得分:0)
我参加聚会可能会迟到,但是在这里我如何解决类似的问题。
import bisect
outcomes = ["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."]
ranges = [6, 11, 16]
outcome_index = bisect.bisect(ranges, 20)
print(outcomes[outcome_index])
答案 9 :(得分:-1)
感谢大家的回复。我一直在乱砍,我提出了一个很适合我的目的的解决方案。这与@PaulCornelius的建议最为相似。
stealth_roll = randint(1, 20)
# select from a dictionary of 4 responses using one of four ranges.
# only one resolution can be True. # True can be a key value.
def check(i, a, b): # check if i is in the range. # return True or False
if i in range(a, b):
return True
else:
return False
### can assign returned object as dictionary key! # assign key as True or False.
stealth_check = {
check(stealth_roll, 1, 6) :
'You are about as stealthy as a thunderstorm.',
check(stealth_roll, 6, 11) :
'You tip-toe through the crowd of walkers, while loudly calling them names.',
check(stealth_roll, 11, 16) :
'You are quiet, and deliberate, but still you smell.',
check(stealth_roll, 15, 21) :
'You move like a ninja, but attracting a handful of walkers was inevitable.'
}
print stealth_check[True] # print the dictionary value that is True.