HNo*_*oob 5 python functional-programming python-3.x
如何使用以下属性编写函数"fmap":
>>> l = [1, 2]; fmap(lambda x: 2*x, l)
[2, 4]
>>> l = (1, 2); fmap(lambda x: 2*x, l)
(2, 4)
>>> l = {1, 2}; fmap(lambda x: 2*x, l)
{2, 4}
Run Code Online (Sandbox Code Playgroud)
(我在haskell中搜索了一种"fmap",但是在python3中).
我有一个非常难看的解决方案,但肯定有更多pythonic和泛型的解决方案?:
def fmap(f, container):
t = container.__class__.__name__
g = map(f, container)
return eval(f"{t}(g)")
Run Code Online (Sandbox Code Playgroud)
eval__class__ 也可用于实例化新实例:
def mymap(f, contener):
t = contener.__class__
return t(map(f, contener))
Run Code Online (Sandbox Code Playgroud)
这样就不再需要eval使用它,这被认为是不好的做法.根据@ EliKorvigo的评论,您可能更喜欢内置于type魔术方法:
def mymap(f, contener):
t = type(contener)
return t(map(f, contener))
Run Code Online (Sandbox Code Playgroud)
返回值是一个类型对象,通常与返回的对象相同
object.__class__.
在新式课程中,"通常相同"应视为"等同".
您可以通过几种方式检查/测试迭代.使用try/ except捕获TypeError:
def mymap(f, contener):
try:
mapper = map(f, contener)
except TypeError:
return 'Input object is not iterable'
return type(contener)(mapper)
Run Code Online (Sandbox Code Playgroud)
from collections import Iterable
def mymap(f, contener):
if isinstance(contener, Iterable):
return type(contener)(map(f, contener))
return 'Input object is not iterable'
Run Code Online (Sandbox Code Playgroud)
因为这特别适用内置类常用的容器,如list,set,tuple,collections.deque,等,可用于通过一个懒惰的迭代实例的实例.存在异常:例如,str(map(str.upper, 'hello'))即使str实例是可迭代的,也不会像您预期的那样工作.
使用输入类型作为转换器并不一定适用于所有场合。 map只是使用其输入的“可迭代性”来产生输出。在 Python3 中,这就是为什么map返回一个生成器而不是一个列表(这更合适)。
因此,一个更干净、更健壮的版本将明确期望它可以处理各种可能的输入,并在所有其他情况下引发错误:
def class_retaining_map(fun, iterable):
if type(iterable) is list: # not using isinstance(), see below for reasoning
return [ fun(x) for x in iterable ]
elif type(iterable) is set:
return { fun(x) for x in iterable }
elif type(iterable) is dict:
return { k: fun(v) for k, v in iterable.items() }
# ^^^ use .iteritems() in python2!
# and depending on your usecase this might be more fitting:
# return { fun(k): v for k, v in iterable.items() }
else:
raise TypeError("type %r not supported" % type(iterable))
Run Code Online (Sandbox Code Playgroud)
else您可以在Cause 子句中为所有其他可迭代值添加一个案例:
else:
return (fun(x) for x in iterable)
Run Code Online (Sandbox Code Playgroud)
但这会返回一个可迭代的子类,set而该子类可能不是您想要的。
请注意,我故意不使用isinstance,因为这会从 的子类中生成一个列表list,例如。我认为在这种情况下这是明确不想要的。
有人可能会争辩说,任何 a list(即 a 的子类list)都需要遵守一个构造函数,该构造函数为元素的迭代返回此类型的事物。同样,对于set,的子类dict(必须适用于对的迭代)等。然后代码可能如下所示:
def class_retaining_map(fun, iterable):
if isinstance(iterable, (list, set)):
return type(iterable)(fun(x) for x in iterable)
elif isinstance(iterable, dict):
return type(iterable)((k, fun(v)) for k, v in iterable.items())
# ^^^ use .iteritems() in python2!
# and depending on your usecase this might be more fitting:
# return type(iterable)((fun(k), v) for k, v in iterable.items())
else:
raise TypeError("type %r not supported" % type(iterable))
Run Code Online (Sandbox Code Playgroud)