2014-09-22 56 views
1

d[x]其中d是一個字典,調用d.__getitem__(x)。有沒有辦法創建一個類F,這樣y=F(X); d[y]會調用F中的某種方法代替:y.someMethod(d)`__getitem ___'的反轉

背景:我試圖用「別名」鍵做一個字典,所以如果我有d[a]=42,那麼d[alias_of_a]也會返回42。這是自定義__getitem__非常簡單,例如:

class oneOf(object): 
    def __init__(self, *keys): 
     self.keys = keys 

class myDict(dict): 
    def __getitem__(self, item): 
     if isinstance(item, oneOf): 
      for k in item.keys: 
       if k in self: 
        return self[k] 
     return dict.__getitem__(self, item) 


a = myDict({ 
    'Alpha': 1, 
    'B': 2, 
}) 

print a[oneOf('A', 'Alpha')] 
print a[oneOf('B', 'Bravo')] 

不過,我想知道是否可以在不重寫dict可能:

a = { 
    'Alpha': 1, 
    'B': 2, 
} 

print a[???('A', 'Alpha')] 
print a[???('B', 'Bravo')] 

如果這是不可能的,如何使它的工作倒過來:

a = { 
    ???('A', 'Alpha'): 1, 
    ???('B', 'Bravo'): 2, 
} 

print a['A'] 
print a['Bravo'] 

什麼,重要的是我是我想避免延長dict

+1

不,沒有這樣的方法。傳入'__getitem__'的對象不會被通知使用。 – 2014-09-22 11:39:16

+2

您可以通過映射到規範值來使您的鍵等價*,然後使用'__eq__'和'__hash__'插入相同的字典哈希表插槽。但是那需要你有一個映射到規範的方法。 – 2014-09-22 11:41:12

+0

您可能對[PEP-455](http://legacy.python.org/dev/peps/pep-0455/)感興趣,但尚未被接受但似乎適用於您的用例。 – chepner 2014-09-22 12:34:43

回答

1

這個用例的東西是不可能的:

a = { 
    'Alpha': 1, 
    'B': 2, 
} 

a[???('A', 'Alpha')] 
a[???('B', 'Bravo')] 

這是因爲dict將第一哈希對象。爲了強制碰撞,這將允許壓倒性的平等,哈希值需要匹配。但???('A', 'Alpha')只能散列到一個'A''Alpha',如果它做出了錯誤的選擇,它已經失敗。

其他用例應用了類似的推論:

a = { 
    ???('A', 'Alpha'): 1, 
    ???('B', 'Bravo'): 2, 
} 

a['A'] 
a['Bravo'] 

a['A']將查找與不同的哈希a['Alpha'],如此反覆???('A', 'Alpha')需要有兩個哈希值,這是不可能的。

您需要合作鍵和值以使其工作。


理論上你可以使用inspect.getouterframes__hash__方法來檢查字典的值,但如果字典的Python框架這隻會工作。如果你的意圖是猴子補丁的功能,這種功能可以做你想做但不完全的事情,這可能(僅僅是)工作(ish,有點)。

import inspect 

class VeryHackyAnyOfHack: 
    def __init__(self, variable_name_hack, *args): 
     self.variable_name_hack = variable_name_hack 
     self.equal_to = args 

    def __eq__(self, other): 
     return other in self.equal_to 

    def __hash__(self): 
     outer_frame = inspect.getouterframes(inspect.currentframe())[1] 

     assumed_target_dict = outer_frame[0].f_locals[self.variable_name_hack] 

     for item in self.equal_to: 
      if item in assumed_target_dict: 
       return hash(item) 

     # Failure 
     return hash(item[0]) 

這是使用像這樣:

import random 

def check_thing_agains_dict(item): 
    if random.choice([True, False]): 
     internal_dict = {"red": "password123"} 
    else: 
     internal_dict = {"blue": "password123"} 

    return internal_dict[item] 

myhack = VeryHackyAnyOfHack('internal_dict', "red", "blue") 
check_thing_agains_dict(myhack) 
#>>> 'password123' 

此外,張女士,你所要做的,這意味着,在實踐中這是不可能的。這也是一種語言擴展,所以這不是便攜式的。

+0

我正在考慮沿着同一行(使一個裝飾器可以解析ast或字節碼等),但顯然不值得。 – georg 2014-09-23 21:34:48

1

內置dict提供了非常簡單的查找語義:給定一個哈希的對象x,返回yx先前映射到對象。如果你想映射到同一對象的多個按鍵,你就需要設置了明確:

# First, initialize the dictionary with one key per equivalence class 
a = { 'a': 1, 'b': 2 } 
# Then, set up any aliases. 
a['Alpha'] = a['a'] 
a['Bravo'] = a['b'] 

TransformDict類被考慮包含在Python 3.5將簡化這個有點允許您更換第2步具有「二級」查找功能,可在給定查詢之前將給定密鑰映射到其規範表示。像

def key_transform(key): 
    if key in {'Alpha', 'Aleph'}: 
     return 'a' 
    elif key in {'Bravo', 'Beta', 'Beth'}: 
     return 'b' 

a = TransformDict(key_transform, a=1, b=2) 
assert a['Alpha'] is a['a'] 
+0

謝謝! 'TransformDict'看起來很有趣。 – georg 2014-09-23 21:37:35