python:filter()一个可迭代对象,计数已过滤和未过滤的项

nuypyhwy  于 2023-02-01  发布在  Python
关注(0)|答案(3)|浏览(121)

我有一个很大的Iterable
我想用filter()函数过滤它。
我如何计算(以某种优雅的方式)有多少项被过滤?
(same问题可以针对map()reduce()等)
我肯定我能做到:

items = get_big_iterable()
count_good = 0
count_all = 0
for item in items:
    if should_keep(item):
        count_good += 1
    count_all += 1

print('keep: {} of {}'.format(count_good, count_all))

filter()有可能实现吗?

items = filter(should_keep, get_big_iterable()) 
for item in items:
    #... using values here ..
    #possible count not filtered items here too?

我不应该重复两次,并且希望使用filter()或类似的解决方案

e5njpo68

e5njpo681#

使用enumerate和一些基本的算术应该非常简单:

def should_keep(x):
    return x % 3 == 0

items = range(1, 28)

def _wrapper(x):
    return should_keep(x[1])

filtered_with_counts = enumerate(filter(_wrapper, enumerate(items, 1)), 1)

for i, (j, item) in filtered_with_counts:
    # do something with item
    print(f"Item is {item}, total: {j}, good: {i}, bad: {j-i}")

count_all = j
count_good = i
count_bad = count_all - count_good
print(f"Final: {count_all}, {count_good}, {count_bad}")

输出:

Item is 3, total: 3, good: 1, bad: 2
Item is 6, total: 6, good: 2, bad: 4
Item is 9, total: 9, good: 3, bad: 6
Item is 12, total: 12, good: 4, bad: 8
Item is 15, total: 15, good: 5, bad: 10
Item is 18, total: 18, good: 6, bad: 12
Item is 21, total: 21, good: 7, bad: 14
Item is 24, total: 24, good: 8, bad: 16
Item is 27, total: 27, good: 9, bad: 18
Final: 27, 9, 18

不过我可能不会使用它。注意,我假设您可能不想修改should_keep,但您总是可以将其 Package 。

8ftvxx2r

8ftvxx2r2#

我能想到两种方法:第一个比较短,但可能对性能不好,而且无法达到使用迭代器的目的:

count=len(list(your_filtered_iterable))

另一种方法是编写自己的过滤器,根据Python文档:
请注意,如果函数不是None,则filter(function, iterable)等效于生成器表达式(item for item in iterable if function(item));如果函数不是None,则filter(function, iterable)等效于生成器表达式(item for item in iterable if item)
所以你可以这样写:

class Filter:
    def __init__(self, func, iterable):
        self.count_good = 0
        self.count_all = 0
        self.func = func
        self.iterable = iterable

    def __iter__(self):
        if self.func is None:
            for obj in self.iterable:
                if obj:
                    self.count_good += 1
                    self.count_all += 1
                    yield obj
                else:
                    self.count_all += 1
        else:
            for obj in self.iterable:
                if self.func(obj):
                    self.count_good += 1
                    self.count_all += 1
                    yield obj
                else:
                    self.count_all += 1

然后,您可以从Filter示例访问count_goodcount_all

items = Filter(should_keep, get_big_terable()) 
    for item in items:
        # do whatever you need with item
        print('keep: {} of {}'.format(items.count_good, items.count_all))
ztmd8pv5

ztmd8pv53#

内置的filter没有提供这个功能,您需要编写自己的filter类,实现它的__next____iter__方法。

代码

class FilterCount:
    def __init__(self, function, iterable):
        self.function = function
        self.iterable = iter(iterable)
        self.countTrue, self.countFalse = 0, 0

    def __iter__(self):
        return self

    def __next__(self):
        nxt = next(self.iterable)
        while not self.function(nxt):
            self.countFalse += 1
            nxt = next(self.iterable)

        self.countTrue += 1
        return nxt

示例

lst = ['foo', 'foo', 'bar']
filtered_lst = FilterCount(lambda x: x == 'foo', lst)

for x in filtered_lst:
    print(x)
print(filtered_lst.countTrue)
print(filtered_lst.countFalse)

输出

foo
foo
2
1

相关问题