通过了解字典中的值,有没有直接找到密钥的方法?
我能想到的就是:
key = [key for key, value in dict_obj.items() if value == 'value'][0]
答案 0 :(得分:76)
你的列表理解通过查找所有匹配的所有dict项目,然后只返回第一个键。此生成器表达式仅在必要时迭代以返回第一个值:
key = next(key for key, value in dd.items() if value == 'value')
其中dd
是dict。如果未找到匹配项,则会引发StopIteration
,因此您可能希望捕获该内容并返回更合适的异常,例如ValueError
或KeyError
。
答案 1 :(得分:45)
有些情况下,字典是一个映射
例如,
d = {1: "one", 2: "two" ...}
如果您只进行一次查找,那么您的方法就可以了。但是,如果您需要执行多个查找,则创建逆字典
会更有效ivd = {v: k for k, v in d.items()}
如果有多个键具有相同值的可能性,则需要在这种情况下指定所需的行为。
如果你的Python是2.6或更早,你可以使用
ivd = dict((v, k) for k, v in d.items())
答案 2 :(得分:30)
此版本比yours短26%,但功能相同,即使是冗余/模糊值(返回第一个匹配,就像你的那样)。但是,它可能比你的慢两倍,因为它会从字典中创建一个列表两次。
key = dict_obj.keys()[dict_obj.values().index(value)]
或者,如果您希望简洁而不是可读性,则可以使用
再保存一个字符key = list(dict_obj)[dict_obj.values().index(value)]
如果您更喜欢效率,@ PaulMcGuire的approach会更好。如果有许多密钥共享相同的值,那么使用列表解析来实例化该密钥列表会更有效,而是使用生成器:
key = (key for key, value in dict_obj.items() if value == 'value').next()
答案 3 :(得分:16)
没有。不要忘记,可以在任意数量的键上找到该值,包括0或大于1。
答案 4 :(得分:5)
也许你想要的是类似字典的课程,例如下面的DoubleDict
?您可以将任何一个提供的元类与DoubleDict
结合使用,或者可以避免使用任何元类。
import functools
import threading
################################################################################
class _DDChecker(type):
def __new__(cls, name, bases, classdict):
for key, value in classdict.items():
if key not in {'__new__', '__slots__', '_DoubleDict__dict_view'}:
classdict[key] = cls._wrap(value)
return super().__new__(cls, name, bases, classdict)
@staticmethod
def _wrap(function):
@functools.wraps(function)
def check(self, *args, **kwargs):
value = function(self, *args, **kwargs)
if self._DoubleDict__forward != \
dict(map(reversed, self._DoubleDict__reverse.items())):
raise RuntimeError('Forward & Reverse are not equivalent!')
return value
return check
################################################################################
class _DDAtomic(_DDChecker):
def __new__(cls, name, bases, classdict):
if not bases:
classdict['__slots__'] += ('_DDAtomic__mutex',)
classdict['__new__'] = cls._atomic_new
return super().__new__(cls, name, bases, classdict)
@staticmethod
def _atomic_new(cls, iterable=(), **pairs):
instance = object.__new__(cls, iterable, **pairs)
instance.__mutex = threading.RLock()
instance.clear()
return instance
@staticmethod
def _wrap(function):
@functools.wraps(function)
def atomic(self, *args, **kwargs):
with self.__mutex:
return function(self, *args, **kwargs)
return atomic
################################################################################
class _DDAtomicChecker(_DDAtomic):
@staticmethod
def _wrap(function):
return _DDAtomic._wrap(_DDChecker._wrap(function))
################################################################################
class DoubleDict(metaclass=_DDAtomicChecker):
__slots__ = '__forward', '__reverse'
def __new__(cls, iterable=(), **pairs):
instance = super().__new__(cls, iterable, **pairs)
instance.clear()
return instance
def __init__(self, iterable=(), **pairs):
self.update(iterable, **pairs)
########################################################################
def __repr__(self):
return repr(self.__forward)
def __lt__(self, other):
return self.__forward < other
def __le__(self, other):
return self.__forward <= other
def __eq__(self, other):
return self.__forward == other
def __ne__(self, other):
return self.__forward != other
def __gt__(self, other):
return self.__forward > other
def __ge__(self, other):
return self.__forward >= other
def __len__(self):
return len(self.__forward)
def __getitem__(self, key):
if key in self:
return self.__forward[key]
return self.__missing_key(key)
def __setitem__(self, key, value):
if self.in_values(value):
del self[self.get_key(value)]
self.__set_key_value(key, value)
return value
def __delitem__(self, key):
self.pop(key)
def __iter__(self):
return iter(self.__forward)
def __contains__(self, key):
return key in self.__forward
########################################################################
def clear(self):
self.__forward = {}
self.__reverse = {}
def copy(self):
return self.__class__(self.items())
def del_value(self, value):
self.pop_key(value)
def get(self, key, default=None):
return self[key] if key in self else default
def get_key(self, value):
if self.in_values(value):
return self.__reverse[value]
return self.__missing_value(value)
def get_key_default(self, value, default=None):
return self.get_key(value) if self.in_values(value) else default
def in_values(self, value):
return value in self.__reverse
def items(self):
return self.__dict_view('items', ((key, self[key]) for key in self))
def iter_values(self):
return iter(self.__reverse)
def keys(self):
return self.__dict_view('keys', self.__forward)
def pop(self, key, *default):
if len(default) > 1:
raise TypeError('too many arguments')
if key in self:
value = self[key]
self.__del_key_value(key, value)
return value
if default:
return default[0]
raise KeyError(key)
def pop_key(self, value, *default):
if len(default) > 1:
raise TypeError('too many arguments')
if self.in_values(value):
key = self.get_key(value)
self.__del_key_value(key, value)
return key
if default:
return default[0]
raise KeyError(value)
def popitem(self):
try:
key = next(iter(self))
except StopIteration:
raise KeyError('popitem(): dictionary is empty')
return key, self.pop(key)
def set_key(self, value, key):
if key in self:
self.del_value(self[key])
self.__set_key_value(key, value)
return key
def setdefault(self, key, default=None):
if key not in self:
self[key] = default
return self[key]
def setdefault_key(self, value, default=None):
if not self.in_values(value):
self.set_key(value, default)
return self.get_key(value)
def update(self, iterable=(), **pairs):
for key, value in (((key, iterable[key]) for key in iterable.keys())
if hasattr(iterable, 'keys') else iterable):
self[key] = value
for key, value in pairs.items():
self[key] = value
def values(self):
return self.__dict_view('values', self.__reverse)
########################################################################
def __missing_key(self, key):
if hasattr(self.__class__, '__missing__'):
return self.__missing__(key)
if not hasattr(self, 'default_factory') \
or self.default_factory is None:
raise KeyError(key)
return self.__setitem__(key, self.default_factory())
def __missing_value(self, value):
if hasattr(self.__class__, '__missing_value__'):
return self.__missing_value__(value)
if not hasattr(self, 'default_key_factory') \
or self.default_key_factory is None:
raise KeyError(value)
return self.set_key(value, self.default_key_factory())
def __set_key_value(self, key, value):
self.__forward[key] = value
self.__reverse[value] = key
def __del_key_value(self, key, value):
del self.__forward[key]
del self.__reverse[value]
########################################################################
class __dict_view(frozenset):
__slots__ = '__name'
def __new__(cls, name, iterable=()):
instance = super().__new__(cls, iterable)
instance.__name = name
return instance
def __repr__(self):
return 'dict_{}({})'.format(self.__name, list(self))
答案 5 :(得分:5)
由于这仍然非常重要,第一次Google热播,我只是花了一些时间搞清楚这一点,我将发布我的(使用Python 3)解决方案:
testdict = {'one' : '1',
'two' : '2',
'three' : '3',
'four' : '4'
}
value = '2'
[key for key in testdict.items() if key[1] == value][0][0]
Out[1]: 'two'
它将为您提供匹配的第一个值。
答案 6 :(得分:2)
据我所知,没有一种方法可以做到这一点,一种方法是通过键创建一个正常查找的字典,另一种方法是按值反向查找。
这里有一个这样的实现的例子:
http://code.activestate.com/recipes/415903-two-dict-classes-which-can-lookup-keys-by-value-an/
这确实意味着查找值的键可能会产生多个结果,这些结果可以作为简单列表返回。
答案 7 :(得分:2)
不,如果不查看所有键并检查所有值,就无法有效地执行此操作。因此,您需要O(n)
时间来完成此操作。如果你需要做很多这样的查找,你需要通过构建一个反向字典(也可以在O(n)
中完成)然后在这个反向字典中进行搜索来有效地执行此操作(每次搜索都将采用平均O(1)
)。
以下是如何从普通字典构建反向字典(可以执行一对多映射)的示例:
for i in h_normal:
for j in h_normal[i]:
if j not in h_reversed:
h_reversed[j] = set([i])
else:
h_reversed[j].add(i)
例如,如果你的
h_normal = {
1: set([3]),
2: set([5, 7]),
3: set([]),
4: set([7]),
5: set([1, 4]),
6: set([1, 7]),
7: set([1]),
8: set([2, 5, 6])
}
您的h_reversed
将
{
1: set([5, 6, 7]),
2: set([8]),
3: set([1]),
4: set([5]),
5: set([8, 2]),
6: set([8]),
7: set([2, 4, 6])
}
答案 8 :(得分:1)
reverse_dictionary = {v:k for k,v in dictionary.items()}
如果您要进行很多反向查找
答案 9 :(得分:1)
# oneline solution using zip
>> x = {'a':100, 'b':999}
>> y = dict(zip(x.values(), x.keys()))
>> y
{100: 'a', 999: 'b'}
答案 10 :(得分:0)
通过字典中的值可以是任何类型的对象,它们不能以其他方式进行散列或索引。因此,对于此集合类型,通过值查找关键字是不自然的。任何类似的查询都只能在O(n)时间内执行。因此,如果这是一项频繁的任务,您应该查看一些关键字索引,如Jon sujjested或甚至某些空间索引(DB或http://pypi.python.org/pypi/Rtree/)。
答案 11 :(得分:0)
我知道这可能被认为是“浪费”,但在这种情况下,我经常将密钥存储为值记录中的附加列:
d = {'key1' : ('key1', val, val...), 'key2' : ('key2', val, val...) }
这是一种权衡并且感觉不对,但它很简单且有效,当然还取决于值是元组而不是简单值。
答案 12 :(得分:0)
我使用字典作为一种&#34;数据库&#34;,所以我需要找到一个可以重复使用的密钥。对于我的情况,如果一个键的值是None
,那么我可以接受并重复使用它,而不必分配&#34;另一个身份。我只是想分享它。
db = {0:[], 1:[], ..., 5:None, 11:None, 19:[], ...}
keys_to_reallocate = [None]
allocate.extend(i for i in db.iterkeys() if db[i] is None)
free_id = keys_to_reallocate[-1]
我喜欢这个,因为我不必尝试捕捉StopIteration
或IndexError
等错误。如果有可用密钥,则free_id
将包含一个密钥。如果没有,那么它只是None
。可能不是pythonic,但我真的不想在这里使用try
...
答案 13 :(得分:0)
由于dict中的值可能不存在,因此更加pythonic和自动记录的代码将是:
a # Value to search against
x = None # Searched key
for k, v in d.items():
if v == a:
x = k
break
x # Now contains the key or None if not found.
事实上,如果你在一个新的设计程序中遇到这个问题,那么就不会回答这些问题,那么你应该检查一下你的设计。
答案 14 :(得分:0)
电话簿反向查找示例:
normDic = {'KVOTHE':['789-2583']
,'DENNA':['987-6453']}
revDic = {'789-2583':['KVOTHE']
,'987-6453':['DENNA']}
numInput = str(input('Enter number:\n>>'))
if numInput in revDic:
print('Contact found:', revDic[numInput], numInput)
答案 15 :(得分:-3)
key in dict.values()
这就是字面意思