Cuy*_*nly 23 python dictionary range
所以,我有一个想法,我可以使用一系列数字作为字典中单个值的键.
我写了下面的代码,但我无法让它工作.它甚至可能吗?
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]
Run Code Online (Sandbox Code Playgroud)
L3v*_*han 16
它可以在Python 3上使用 - 如果你使用Python 2 xrange而不是range:
stealth_check = {
xrange(1, 6) : 'You are about as stealthy as thunderstorm.', #...
}
Run Code Online (Sandbox Code Playgroud)
但是,您尝试使用它的方式将无法正常工作.您可以迭代键,如下所示:
for key in stealth_check:
if stealth_roll in key:
print stealth_check[key]
break
Run Code Online (Sandbox Code Playgroud)
这样的表现并不好(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]
raise KeyError(item)
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'
Run Code Online (Sandbox Code Playgroud)
jpm*_*c26 10
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))
Run Code Online (Sandbox Code Playgroud)
这种方法绝对没有错。它真的不需要更复杂。这是很多更直观,比试图用一个更容易弄清楚,并更有效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))
Run Code Online (Sandbox Code Playgroud)
在这种情况下,我们使用范围来生成dict我们可以在其中查找结果的 a。我们将每个滚动映射到一个结果,多次重复使用相同的结果。这不那么简单;从中辨别每个结果的概率并不那么容易。但至少它使用dict得当:它将一个键映射到一个值。
您可以根据概率计算选择结果。基本思想是计算“累积”概率(滚动值的顶端已经有了),然后循环直到累积概率超过随机值。有大量的如何去了解它的想法在这里。
一些简单的选项是:
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))
Run Code Online (Sandbox Code Playgroud)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())
Run Code Online (Sandbox Code Playgroud)有些缺点是不需要实际的数字滚动,但它们的实现和维护要简单得多。
“Pythonic”意味着让你的代码简单易懂。这意味着将结构用于其设计目的。dict不是为你正在做的事情而设计的。
所有这些选项都相对较快。根据raratiru的评论,这RangeDict是当时最快的答案。但是,我的测试脚本显示,除了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
Run Code Online (Sandbox Code Playgroud)
如果你一次得到一个结果,numpy 会慢一个数量级;但是,如果您批量生成结果,速度会快一个数量级。
除非您希望范围本身成为关键,否则无法直接从范围构建字典.我不认为你想要那个.要获得范围内每种可能性的单独条目:
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)]
)
Run Code Online (Sandbox Code Playgroud)
如果你有一个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.']
Run Code Online (Sandbox Code Playgroud)
是的,只有当您将range列表转换为不可变列表时才可以,tuple因此它们可以清除并被接受为字典的键:
stealth_check = {
tuple(range(1, 6)) : 'You are about as stealthy as thunderstorm.',
Run Code Online (Sandbox Code Playgroud)
编辑:实际上它在Python 3中工作,因为它range是一个不可变的序列类型,并生成一个不可变的tuple而不是一个listL3viathan声明.
但是你不能用一个整数作为键来访问它们.你的最后一行不起作用.
我花了一些时间来创建一个无论值是什么都可以工作的解决方案(只要这些行没有被更大的范围"加权",就可以在字典中选择一个条目.
它调用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]])
Run Code Online (Sandbox Code Playgroud)
我编写了一个 RangeKeyDict 类来处理这样的情况,它更通用且易于使用。使用方法请查看__main__中的代码
使用以下命令安装它:
pip install range-key-dict
Run Code Online (Sandbox Code Playgroud)
用法:
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'
Run Code Online (Sandbox Code Playgroud)
https://github.com/albertmenglongli/range-key-dict
| 归档时间: |
|
| 查看次数: |
29490 次 |
| 最近记录: |