【发布时间】: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