如何根据任意条件函数过滤字典?

问题:如何根据任意条件函数过滤字典?

我有一个要点词典,说:

>>> points={'a':(3,4), 'b':(1,2), 'c':(5,5), 'd':(3,3)}

我想创建一个新字典,其中所有x和y值均小于5的点,即点“ a”,“ b”和“ d”。

根据这本书,每个字典都有该items()函数,该函数返回一个(key, pair) 元组列表:

>>> points.items()
[('a', (3, 4)), ('c', (5, 5)), ('b', (1, 2)), ('d', (3, 3))]

所以我写了这个:

>>> for item in [i for i in points.items() if i[1][0]<5 and i[1][1]<5]:
...     points_small[item[0]]=item[1]
...
>>> points_small
{'a': (3, 4), 'b': (1, 2), 'd': (3, 3)}

有没有更优雅的方式?我期待Python具有一些超棒的dictionary.filter(f)功能…

I have a dictionary of points, say:

>>> points={'a':(3,4), 'b':(1,2), 'c':(5,5), 'd':(3,3)}

I want to create a new dictionary with all the points whose x and y value is smaller than 5, i.e. points ‘a’, ‘b’ and ‘d’.

According to the the book, each dictionary has the items() function, which returns a list of (key, pair) tuple:

>>> points.items()
[('a', (3, 4)), ('c', (5, 5)), ('b', (1, 2)), ('d', (3, 3))]

So I have written this:

>>> for item in [i for i in points.items() if i[1][0]<5 and i[1][1]<5]:
...     points_small[item[0]]=item[1]
...
>>> points_small
{'a': (3, 4), 'b': (1, 2), 'd': (3, 3)}

Is there a more elegant way? I was expecting Python to have some super-awesome dictionary.filter(f) function…


回答 0

如今,在Python 2.7及更高版本中,您可以使用dict理解:

{k: v for k, v in points.iteritems() if v[0] < 5 and v[1] < 5}

在Python 3中:

{k: v for k, v in points.items() if v[0] < 5 and v[1] < 5}

Nowadays, in Python 2.7 and up, you can use a dict comprehension:

{k: v for k, v in points.iteritems() if v[0] < 5 and v[1] < 5}

And in Python 3:

{k: v for k, v in points.items() if v[0] < 5 and v[1] < 5}

回答 1

dict((k, v) for k, v in points.items() if all(x < 5 for x in v))

如果您使用的是Python 2,并且您可能有很多条目.iteritems().items()则可以选择调用而不是。points

all(x < 5 for x in v)如果您确定每个点始终都是二维的,则可能会过大(在这种情况下,您可能会用表示相同的约束and),但效果很好;-)。

dict((k, v) for k, v in points.items() if all(x < 5 for x in v))

You could choose to call .iteritems() instead of .items() if you’re in Python 2 and points may have a lot of entries.

all(x < 5 for x in v) may be overkill if you know for sure each point will always be 2D only (in that case you might express the same constraint with an and) but it will work fine;-).


回答 2

points_small = dict(filter(lambda (a,(b,c)): b<5 and c < 5, points.items()))
points_small = dict(filter(lambda (a,(b,c)): b<5 and c < 5, points.items()))

回答 3

>>> points = {'a': (3, 4), 'c': (5, 5), 'b': (1, 2), 'd': (3, 3)}
>>> dict(filter(lambda x: (x[1][0], x[1][1]) < (5, 5), points.items()))

{'a': (3, 4), 'b': (1, 2), 'd': (3, 3)}
>>> points = {'a': (3, 4), 'c': (5, 5), 'b': (1, 2), 'd': (3, 3)}
>>> dict(filter(lambda x: (x[1][0], x[1][1]) < (5, 5), points.items()))

{'a': (3, 4), 'b': (1, 2), 'd': (3, 3)}

回答 4

dict((k, v) for (k, v) in points.iteritems() if v[0] < 5 and v[1] < 5)
dict((k, v) for (k, v) in points.iteritems() if v[0] < 5 and v[1] < 5)

回答 5

我认为Alex Martelli的答案绝对是做到这一点的最优雅的方法,但只是想添加一种dictionary.filter(f)方法,以Pythonic的方式满足您对超棒方法的需求:

class FilterDict(dict):
    def __init__(self, input_dict):
        for key, value in input_dict.iteritems():
            self[key] = value
    def filter(self, criteria):
        for key, value in self.items():
            if (criteria(value)):
                self.pop(key)

my_dict = FilterDict( {'a':(3,4), 'b':(1,2), 'c':(5,5), 'd':(3,3)} )
my_dict.filter(lambda x: x[0] < 5 and x[1] < 5)

基本上,我们创建一个继承自的类dict,但添加了filter方法。我们确实需要使用.items()过滤,因为.iteritems()在破坏性迭代时使用会引发异常。

I think that Alex Martelli’s answer is definitely the most elegant way to do this, but just wanted to add a way to satisfy your want for a super awesome dictionary.filter(f) method in a Pythonic sort of way:

class FilterDict(dict):
    def __init__(self, input_dict):
        for key, value in input_dict.iteritems():
            self[key] = value
    def filter(self, criteria):
        for key, value in self.items():
            if (criteria(value)):
                self.pop(key)

my_dict = FilterDict( {'a':(3,4), 'b':(1,2), 'c':(5,5), 'd':(3,3)} )
my_dict.filter(lambda x: x[0] < 5 and x[1] < 5)

Basically we create a class that inherits from dict, but adds the filter method. We do need to use .items() for the the filtering, since using .iteritems() while destructively iterating will raise exception.


回答 6

dict((k, v) for (k, v) in points.iteritems() if v[0] < 5 and v[1] < 5)
dict((k, v) for (k, v) in points.iteritems() if v[0] < 5 and v[1] < 5)