首先,type(seq)( f(x) for x in seq )
真的只是type(seq)(imap(f, seq))
。为什么不直接使用它?
其次,您尝试做的事情通常没有意义。map
接受任何可迭代的,而不仅仅是一个序列。基本上,不同之处在于,序列具有 alen
并且是随机可访问的。
没有规则可以通过调用从 Y 类型的值构造 X 类型的迭代type(X)(y_iter)
。事实上,虽然序列通常是正确的,但很少有其他例子是正确的。
如果您想要专门处理一些特殊类型,您可以这样做:
def map2(f, seq):
it = imap(f, seq)
if isinstance(seq, (tuple, list)):
return type(seq)(it)
else:
return it
或者,如果您想假设所有序列都可以以这种方式构建(对于大多数内置序列来说都是如此,但请考虑,例如xrange
- 它不是设计为序列但确实符合协议 - 当然有除了内置的内容之外,没有任何保证):
def map2(f, seq):
it = imap(f, seq)
try:
len(seq)
except:
return it
else:
return type(seq)(it)
您可以假设可以从可迭代构造的任何可迭代类型都是序列(正如您在问题中所建议的那样)......但这可能会导致更多的误报而不是好处,所以我不会。同样,请记住这len
是序列定义的一部分,而“可从迭代器构造”不是,并且存在完全合理的可迭代类型,当给定迭代器时它们会做一些完全不同的事情。
无论你做什么都将是 hack,因为其意图就是 hack,并且违背了 Python 开发人员明确的设计意愿。迭代器/可迭代协议的全部意义在于您应该尽可能少地关心可迭代对象的类型。这就是为什么 Python 3.x 更进一步并用基于迭代器的函数代替了基于列表的函数,例如map
和。filter
那么,我们如何将这些转换之一变成装饰器呢?
好吧,首先,让我们跳过装饰器位,只编写一个高阶函数,它接受imap
一个类似 - 的函数并返回一个应用了这个转换的等效函数:
def sequify(func):
def wrapped(f, seq):
it = func(f, seq)
try:
len(seq)
except:
return it
else:
return type(seq)(it)
return wrapped
所以:
>>> seqmap = sequify(itertools.imap)
>>> seqmap(int, (1.2, 2.3))
(1, 2)
>>> sequify(itertools.ifilter)(lambda x: x>0, (-2, -1, 0, 1, 2))
(1, 2)
现在,我们如何把它变成装饰器?嗯,一个返回函数的函数已经是一个装饰器。您可能想要添加functools.wraps
(尽管即使在非装饰器的情况下您也可能想要添加),但这是唯一的变化。例如,我可以编写一个类似于 imap 的生成器,或者一个返回迭代器的函数,并自动转换为类似 seqmap 的函数:
@sequify
def map_and_discard_none(func, it):
for elem in imap(func, it):
if elem is not None:
yield elem
现在:
>>> map_and_discard_none(lambda x: x*2 if x else x, (1, 2, None))
(2, 4)
当然,这仅适用于具有map
类似语法的函数——也就是说,它们接受一个函数和一个可迭代对象。(好吧,它会意外地适用于采用各种错误类型的函数——例如,你可以调用sequify(itertools.count(10, 5))
它,它会成功检测到这5
不是一个序列,因此只需将迭代器原封不动地传回去。)为了使其更通用,你可以做类似的事情:
def sequify(func, type_arg=1):
def wrapped(*args, **kwargs):
it = func(f, seq)
try:
len(args[type_arg])
except:
return it
else:
return type(seq)(it)
return wrapped
而现在,你可以疯狂地sequify(itertools.combinations, 0)
选择任何你喜欢的东西。在这种情况下,要使其成为有用的装饰器,您可能需要更进一步:
def sequify(type_arg=1):
def wrapper(func):
def wrapped(*args, **kwargs):
it = func(f, seq)
try:
len(args[type_arg])
except:
return it
else:
return type(seq)(it)
return wrapped
return wrapper
所以你可以这样做:
@sequify(3)
def my_silly_function(pred, defval, extrastuff, main_iterable, other_iterable):