Python `映射类保函数序列类型
我想实现一个类似于map的Python `映射类保函数序列类型,python,list,python-2.7,generator,Python,List,Python 2.7,Generator,我想实现一个类似于map的函数,它保留了输入序列的类型映射不保留它: map(str, (8, 9)) # input is a tuple => ['8', '9'] # output is a list 我想到的一个方法是: def map2(f, seq): return type(seq)( f(x) for x in seq ) map2(str, (1,2)) => ('1', '2') map2(str, [3,4]) => ['3', '4'
函数,它保留了输入序列的类型<代码>映射
不保留它:
map(str, (8, 9)) # input is a tuple
=> ['8', '9'] # output is a list
我想到的一个方法是:
def map2(f, seq):
return type(seq)( f(x) for x in seq )
map2(str, (1,2))
=> ('1', '2')
map2(str, [3,4])
=> ['3', '4']
map2(str, deque([5,6]))
=> deque(['5', '6'])
但是,如果seq
是迭代器/生成器,则这不起作用<代码>imap在这种情况下起作用
因此,我的问题是:
map2
map2
以同时支持生成器(比如imap
does)?显然,我希望避免:try:returnmap2(…),TypeError:returnimap(…)
您可能已经意识到,我使用的是python 2.7,但python 3也很有趣。您的形式主义也不适用于
map(str,'12')
最终,您不知道iterable类型在构造函数/初始值设定项中实际将采用什么参数,因此通常无法做到这一点。还要注意的是,imap
没有提供与生成器相同的类型:
>>> type(x for x in range(10))
<type 'generator'>
>>> type(imap(str,range(10)))
<type 'itertools.imap'>
>>> isinstance((x for x in range(10)),type(imap(str,range(10))))
False
>>类型(x代表范围(10)内的x)
>>>类型(imap(str,范围(10)))
>>>isinstance((x代表范围(10)中的x),类型(imap(str,范围(10)))
假的
您可能会想,“当然,通过python的内省,我可以检查初始化器的参数”——您是对的!然而,即使您知道有多少参数进入初始值设定项,它们的名称是什么,您仍然无法获得任何关于您实际应该传递给它们的信息。我想你可以写一些机器学习算法,从文档串中找出答案。。。但我认为这远远超出了这个问题的范围(它假设作者表现良好,并从一开始就创建了良好的docstring)。您的形式主义也不适用于
map(str,'12')
最终,您不知道iterable类型在构造函数/初始值设定项中实际将采用什么参数,因此通常无法做到这一点。还要注意的是,imap
没有提供与生成器相同的类型:
>>> type(x for x in range(10))
<type 'generator'>
>>> type(imap(str,range(10)))
<type 'itertools.imap'>
>>> isinstance((x for x in range(10)),type(imap(str,range(10))))
False
>>类型(x代表范围(10)内的x)
>>>类型(imap(str,范围(10)))
>>>isinstance((x代表范围(10)中的x),类型(imap(str,范围(10)))
假的
您可能会想,“当然,通过python的内省,我可以检查初始化器的参数”——您是对的!然而,即使您知道有多少参数进入初始值设定项,它们的名称是什么,您仍然无法获得任何关于您实际应该传递给它们的信息。我想你可以写一些机器学习算法,从文档串中找出答案。。。但我认为这远远超出了这个问题的范围(它假设作者表现良好,并从一开始就创建了好的docstring)。首先,类型(seq)(f(x)代表seq中的x)
实际上就是类型(seq)(imap(f,seq))
。为什么不直接用它呢
第二,你试图做的事情通常没有意义<代码>映射可以接受任何值,而不仅仅是一个值。基本上,区别在于序列具有len
,并且可以随机访问
没有规则可以通过调用type(X)(Y_iter)
从Y类型的值构造X类型的iterable。事实上,虽然它通常适用于序列,但很少有其他例子是正确的
如果您想专门处理一些特殊类型,可以这样做:
def map2(f, seq):
it = imap(f, seq)
if isinstance(seq, (tuple, list)):
return type(seq)(it)
else:
return it
或者,如果你想假设所有的序列都可以以这种方式构建(对于大多数内置序列来说是正确的,但是考虑一下,例如,代码> xLange——它不是被设计成序列,而是符合协议,当然也不能保证超出所构建的):
您可以假设任何可以从iterable构造的iterable类型都是一个序列(如您在问题中所建议的)……但这可能会导致更多的误报,而不是好处,所以我不会这么做。同样,请记住,len
是序列定义的一部分,而“可从迭代器构造”不是序列定义的一部分,并且当给定迭代器时,有一些完全合理的iterable类型可以做完全不同的事情
无论你做什么都将是一次黑客攻击,因为你的意图就是一次黑客攻击,这与Python开发人员明确的设计愿望背道而驰。迭代器/iterable协议的全部要点是,您应该尽可能少地关心iterable的类型。这就是为什么Python3.x更进一步,用基于迭代器的函数取代了基于列表的函数,如map
和filter
那么,我们如何将这些转换之一转化为装饰器呢 首先,让我们跳过decorator位,只编写一个高阶函数,它接受一个类似于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
因此:
现在,我们如何把它变成一个装饰师?一个已经返回函数的函数是一个装饰器。您可能希望添加插件(尽管即使在非装饰器的情况下也可能需要),但这是唯一的更改。例如,我可以编写一个类似imap的生成器,或一个返回迭代器的函数,并自动转换为类似seqmap的函数:
@sequify
def map_and_discard_none(func, it):
for elem in imap(func, it):
if elem is not None:
yield elem
现在:
当然,这只适用于具有类似于
@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)
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
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):