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
。
答案 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'
同样,你必须这样做的事实意味着在实践中它是不可能的。它也是一种语言扩展,因此不可移植。