反向`__getitem ___`

时间:2014-09-22 11:38:19

标签: python dictionary

d[x]其中d是dict,调用d.__getitem__(x)。有没有办法创建一个类F,以便y=F(X); d[y]可以调用F中的某个方法:y.someMethod(d)

背景:我正在尝试用“别名”键创建一个dict,这样如果我有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

2 个答案:

答案 0 :(得分:1)

内置dict提供了非常简单的查找语义:给定一个可散列对象x,返回y之前映射到的对象x。如果你想要多个映射到同一个对象的键,你需要明确地设置它:

# 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']

考虑包含在Python 3.5中的TransformDict类可以通过允许您将第2步替换为" secondary"来简化这一过程。查找函数,在主查找之前将给定键映射到其规范表示。像

这样的东西
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']

答案 1 :(得分: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'

同样,你必须这样做的事实意味着在实践中它是不可能的。它也是一种语言扩展,因此不可移植。