Python 中的逆字典查找

2025-02-10 08:57:00
admin
原创
49
摘要:问题描述:有没有直接的方法可以通过了解字典中的值来找到键?我能想到的只有这个:key = [key for key, value in dict_obj.items() if value == 'value'][0] 解决方案 1:列表推导会遍历字典中的所有项,找到所有匹配项,然后返回第一个键。此生成器表达式...

问题描述:

有没有直接的方法可以通过了解字典中的值来找到键?

我能想到的只有这个:

key = [key for key, value in dict_obj.items() if value == 'value'][0]

解决方案 1:

列表推导会遍历字典中的所有项,找到所有匹配项,然后返回第一个键。此生成器表达式只会在需要时进行迭代以返回第一个值:

key = next(key for key, value in dd.items() if value == 'value')

dd字典在哪里。StopIteration如果未找到匹配项,则会引发,因此您可能希望捕获该异常并返回更合适的异常,例如ValueErrorKeyError

解决方案 2:

在某些情况下,字典是一对一映射

例如,

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())

解决方案 3:

此版本比您的版本短 26% ,但功能相同,即使对于冗余/模糊值也是如此(返回第一个匹配项,就像您的版本一样)。但是,它的速度可能比您的版本慢两倍,因为它从字典中创建了两次列表。

key = dict_obj.keys()[dict_obj.values().index(value)]

或者,如果你更喜欢简洁而不是易读,你可以使用

key = list(dict_obj)[dict_obj.values().index(value)]

如果你更看重效率,@PaulMcGuire 的方法更好。如果有很多键共享相同的值,那么更有效的方法是不要使用列表推导来实例化该键列表,而是使用生成器:

key = (key for key, value in dict_obj.items() if value == 'value').next()

解决方案 4:

由于这仍然非常相关,第一个谷歌命中,我只是花了一些时间来解决这个问题,我将发布我的(使用 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'

它将为您提供第一个匹配的值。

解决方案 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 != \n               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') \n           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') \n           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))

解决方案 6:

制作反向词典

reverse_dictionary = {v:k for k,v in dictionary.items()} 

如果你有很多反向查找要做

解决方案 7:

# oneline solution using zip
>> x = {'a':100, 'b':999}
>> y = dict(zip(x.values(), x.keys()))  
>> y
{100: 'a', 999: 'b'}

解决方案 8:

不,如果不查看所有键并检查所有值,您就无法有效地完成此操作。因此,您需要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])
}

解决方案 9:

据我所知,没有这样的方法,但是有一种方法是创建一个字典用于按键的正常查找,并创建另一个字典用于按值进行反向查找。

这里有一个这种实现的示例:

http://code.activestate.com/recipes/415903-two-dict-classes-which-can-lookup-keys-by-value-an/

这确实意味着查找某个值的键可能会产生多个结果,这些结果可以作为简单列表返回。

解决方案 10:

我知道这可能被认为是“浪费”,但在这种情况下,我经常将键存储为值记录中的附加列:

d = {'key1' : ('key1', val, val...), 'key2' : ('key2', val, val...) }

这是一种权衡并且感觉不对,但它很简单并且有效,当然取决于值是元组而不是简单值。

解决方案 11:

虽然字典中的值可以是任何类型的对象,但它们不能以其他方式进行哈希处理或索引。因此,对于这种集合类型,通过值查找键是不自然的。任何这样的查询都只能在 O(n) 时间内执行。因此,如果这是一项频繁的任务,您应该查看一些键索引,如 Jon sujjested,或者甚至是一些空间索引(DB 或http://pypi.python.org/pypi/Rtree/)。

解决方案 12:

我将字典用作一种“数据库”,因此我需要找到一个可以重复使用的键。就我的情况而言,如果键的值是None,那么我可以获取并重复使用它,而不必“分配”另一个 ID。我只是觉得我应该分享它。

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]

我喜欢这个,因为我不必尝试捕获任何错误,例如StopIterationIndexError。如果有可用的键,则将free_id包含一个。如果没有,则它将只是None。可能不是 Python 风格,但我真的不想try在这里使用...

解决方案 13:

没有。请不要忘记,该值可能出现在任意数量的键上,包括 0 个或 1 个以上。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用