2016-09-06 64 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