【问题标题】:Python: Filter iterable classPython:过滤可迭代类
【发布时间】:2020-09-12 03:10:18
【问题描述】:

是否存在 Iterable 对象可以持有的钩子/dunder,以便内置 filter 函数可以扩展到 Iterable 类(不仅仅是实例)?

当然也可以写一个自定义的filter_iter函数,比如:

def filter_iter(filt_func: callable, collection_cls: type):
    name = 'Filtered' + collection_cls.__name__  # would this automatic scheme lead to namespace conflicts?
    wrapped_cls = type(name, (collection_cls,), {'_filt_func': staticmethod(filt_func)})
    def __iter__(self):
        yield from filter(self._filt_func, super(wrapped_cls, self).__iter__())
    wrapped_cls.__iter__ = __iter__
    return wrapped_cls

这会产生预期的效果。例如,

from collections import Collection, Iterable
class Chunker(Iterable):
    def __init__(self, source: Iterable, chk_size: int=2):
        self._source = source
        self._chk_size = chk_size
    def __iter__(self):
        yield from zip(*([iter(self._source)] * self._chk_size))


chunker = Chunker(range(12), 2)
assert list(chunker) == [(0, 1), (2, 3), (4, 5), (6, 7), (8, 9), (10, 11)]
FilteredChunker = filter_iter(lambda x: sum(x) % 3 == 0, Chunker)
filtered_chunker = FilteredChunker(range(12))
assert list(filtered_chunker) == [(4, 5), (10, 11)]

但是,就像有一个 __iter__ 钩子决定如何迭代一个对象(例如,list 在对象上调用时应该如何表现),是否有一种 __filter__ 钩子来确定如何filter 在该对象上调用时应该表现得如何?

如果不是,关于过滤迭代的最佳实践或标准是什么?

【问题讨论】:

  • 我希望过滤器调用 iter 以访问每个项目。然后过滤器会发挥它的魔力。
  • 不,因为filter 的实例(是的,filter 是一个类,而不是一个函数)可以通过迭代原始可迭代参数并选择它实际产生的项目来迭代。你为什么要首先过滤Chunker(这是不可可迭代的;Chunker实例是可迭代的)?
  • 你好@chepner!是的,对不起,我应该说 callable (我确实发现了这一点,因为我实际上做了filter.mro() 看看那里是否有任何我可以用来破解的好东西)。至于“为什么不在实例上使用filter”;我认为通常可以提出和回答同样的问题。一个这样的普遍答案是:因为您想要一个实例具有特定行为的类:无论是缓存、过滤还是其他任何行为。
  • 啊,我明白你在说什么了。我虽然你试图迭代这个类,而不是为这个类重新定义现有的__iter__。看我的回答。

标签: python python-3.x python-datamodel


【解决方案1】:

list(例如__iter__)不同,filter 没有这样的钩子。后者只是迭代器协议的一个应用,本身并不是一个单独的协议。

为了不让您空手而归,这里是您提出的filtered_iter 的更简洁版本,它动态地继承给定类,将其__iter__ 方法与filter 组合在一起。

def filter_iter(p, cls):
    class _(cls):
        def __iter__(self):
            yield from filter(p, super().__iter__())
    return _

【讨论】:

  • 您提出的解决方案不只是filter_iter 的更优雅版本吗?我已经在我的问题中包含了我 可以 做的事情,但更喜欢使用内置filter 函数本身。这样的解决方案可能需要在通过某种魔术方法应用于类时定义其行为(例如如何定义iterlist__iter__len__len__ 的行为) .
  • 另一种可能的解决方案是创建一个菱形 mro,以便能够拦截内置 filter__init__ 并为类注入所需的行为。像class filter(Filtered, builtins.filter): ... 这样的东西。但它开始接近我不习惯的黑暗艺术。
  • 没有这样的钩子。 filter 只是迭代器协议的一个应用程序,本身并不是一个单独的协议。
  • 不确定这样做的正确方法是什么 - 但我会编辑您的答案以使其更符合我的问题,并接受它作为答案。
猜你喜欢
  • 2018-07-30
  • 2012-03-23
  • 1970-01-01
  • 2018-08-31
  • 2023-02-01
  • 2019-01-21
  • 2011-03-03
  • 1970-01-01
相关资源
最近更新 更多