2016-09-06 65 views
15

所以,我有一个想法,我可以使用一系列数字作为字典中单个值的键。作为Python中的字典键的范围

我写了下面的代码,但我不能得到它的工作。它甚至有可能吗?

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] 
+3

而不是试图使用范围的关键为什么不根据字典的大小滚动? – TheLazyScripter

+1

作为一个旁注,这样的字典是可能的python3。由于键是范围,因此您必须相应地访问该词典:'stealth_check [range(6,11)]'将起作用。这对你的目的来说完全没用,只是想显示对象模型是一致的。 – spectras

+0

@TheLazyScripter我在整个脚本中采用了(1,20)惯例,我通常也使用随机值作为乘数以及场景选择器。另外,如果这个工作,我可以对每个可能的结果应用不同的权重。 –

回答

5

是的,你可以,只要您将您的range列为不可变tuple,所以他们可哈希和接受你的字典的键:

stealth_check = { 
       tuple(range(1, 6)) : 'You are about as stealthy as thunderstorm.', 

编辑:实际上它工作在Python 3因为012vi是一个不可变的序列类型,并且如L3viathan所述,生成一个不可变的tuple而不是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]]) 
+2

也许这是迂腐,但'范围'不是Python 3中的生成器函数。 –

+4

当你回答:)你必须是迂腐的。谢谢。 –

10

它是可能的Python 3 - 和pytho N 2,如果你使用xrange代替range

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' 
+2

使用ipython'''timeit'''来测量这些数据的'''RangeDict'''的执行时间,证明它是迄今为止所提及的最快速的技术:'''最好的3:每循环6.47μs,最慢的运行时间比最快的运行时间长6.15倍,而其他最好的技术返回数字如“最好的3:每循环17μs”,最慢的运行时间比“最快的运行时间长20倍”最快的''' – raratiru

1
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] 
+2

是的。但是,只有在概率平衡的情况下才有效。 –

+3

...虽然我根本不明白使用字典的意义。基于索引的,基于0的连续集合存在于python中,它们被称为列表:p – spectras

5

不能直接构建一个字典,从一个范围,除非你想的范围本身是键。我不认为你想要那样。要获得个人条目范围内的每一可能性:

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

这种方法将完成你想要的,最后一行将工作(假定的range PY3行为和print):

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))。

+0

'randint'返回1到20之间的值。最后一行应该是'range(16,21)'。这是你的意思吗? –

+1

是的,random.randint(1,20)返回1到20之间的一个值(包含);但范围(1,20)仅从1到19,包括1和19。所以他的代码将会运行,但这个可能性不会是他所期望的。我知道这一点,因为我不止一次犯了这个错误:-) –

+0

@PaulCornelius谢谢。接得好! –

1

将randint映射到具有固定概率的一组固定类别字符串中的一个可能是最有效的。

from random import randint 
stealth_map = (None, 0,0,0,0,0,0,1,1,1,1,1,2,2,2,2,2,3,3,3,3) 
stealth_type = (
    '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.', 
    ) 
for i in range(10): 
    stealth_roll = randint(1, 20) 
    print(stealth_type[stealth_map[stealth_roll]]) 
-2

谢谢大家的回复。我一直在进行黑客攻击,并且提出了一个适合我的目的的解决方案。这与@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. 
+1

很好的解决方案。然而,我已经使用''timeit'''来测量每种技术执行所需的时间,并且到目前为止,最有效的结果是[子类''dict'''] (http://stackoverflow.com/a/39358140/2996101) – raratiru

+0

这样使用'dict'几乎没有任何好处。只要使它成为'if' /'elif' /'else'块。 – jpmc26

1

我写了一个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' 

https://github.com/albertmenglongli/range-key-dict

0

停止试图使这个复杂得多,它需要。

对于值的小单子,只需使用明显和直接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: 
     throw ValueError('Unsupported roll: {}'.format(roll)) 

stealth_roll = randint(1, 20) 
print get_stealthiness(stealth_roll) 

它确实并不需要任何比这更复杂。这是很多更直观,更容易弄清楚,并且比尝试在这里使用dict更有效率。

这样做也会使边界处理更加明显。在上面提供的代码中,您可以快速发现范围在每个地方是使用<还是<=。上面的代码也会为1到20之外的值引发一个有意义的错误消息。它也支持非整数输入,尽管您可能并不在意。

“Pythonic”意味着保持您的代码直接和平易近人。这意味着使用结构来达到他们设计的目的。 dict不是为你正在做的事而设计的。 if陈述分别为

+0

很想听听downvoter的解释。 – jpmc26