我想要函数f
该工作风格是:
>>> f(chr, [65, 97])
['A', 'a']
>>> f(chr, {65, 97})
{'A', 'a'}
>>> f(chr, {65: 'upper_a', 97: 'lower_a'})
{'A': 'upper_a', 'a': 'lower_a'}
map
很懒,所以我必须这样做list(map(function, iterable_ds))
,但这种方式破坏了原始数据结构。
你怎么能做到这一点?
<小时/>有一天尝试写f
,我有这个问题吗?
每个带有 __iter__
的数据结构类是可迭代的,但像 __next__()
给出任何可迭代中的下一个元素,那么什么特殊函数将使 upower 将任何 xyz 添加到可迭代 d.s.?为什么append仅用于列表而add用于集合?为什么没有像__iter__
这样的通用接口(interface)和__next__
?
我试过了
def f(func, i_ds):
ctor = type(i_ds)
holder = list()
for _ in i_ds:
new_val = func(_)
# now what add? append? update? xyz? I hope ctor can consume list
holder.append(new_val)
return ctor(holder) # I know I know it fail for dict type easily
我可以写 f 来处理 dict 但是
- 很丑
- 如果明天我得到 2 棵树怎么办?由不同的人写的
最佳答案
映射(类似 dict
的东西)与其他集合根本不同,因此您无法完全避免对它们进行特殊的大小写,但您至少可以限制所需的专用代码的数量。虽然您说得对,没有通用的“将项目添加到集合”API,但大多数集合都接受构造函数的可迭代值。
使用基于构造函数的鸭子类型,对您所获得的内容进行一个小小的改进:
import collections.abc
def f(func, it):
res = map(func, it)
if isinstance(it, collections.abc.Mapping):
# Pair up mapped keys with original values
res = zip(res, it.values())
return type(it)(res)
这不需要任何中间临时数据结构(map
和zip
是基于惰性生成器的函数;它们仅在最后构造返回类型时才产生值)。它假设所有非 Mapping
类型都可以从可迭代的值构造,而所有 Mapping
类型都可以从可迭代的对构造;对于通用内置集合( tuple
、 list
、 set
、 frozenset
、 dict
),情况就是如此。不太通用的容器不一定能工作( bytes
将取决于 func
的结果,如果没有特殊的外壳来使用 str
, ''.join
将无法工作)。
也就是说,您尝试做的事情是不必要的。您的 f
函数正在复制 map
,情况很糟糕。由于不可能编写像您这样具有完美类型复制的函数,因此最好像 map
那样做,并将其留给调用者以任何合适的数据结构重新打包结果(例如,对于 dict
s,它们可以显式传递映射期望键/值 tuple
并传递 .items()
的函数,然后从结果构造 dict
;实际上他们可能需要 dict
理解)。
毕竟,您所要求的永远不会适用于每种输入类型,仅仅是因为构造函数并不总是遵循相同的规则。例如,此代码不适用于 collections.defaultdict
,因为它将 default_factory
作为第一个参数,第二个是可迭代初始值设定项(违反了我们对第一个参数是可迭代初始值设定项的期望)。尝试处理这个问题意味着更多的特殊情况,而且你只会让确定 f
实际作用变得越来越困难。调用者明确执行以下操作真的那么困难吗: {myfunc(k): v for k, v in mydict.items()}
而不是 f(myfunc, mydict)
,或者 [myfunc(x) for x in mylist]
/list(map(myfunc, mylist))
over f(myfunc, mylist)
?
关于python - 如何在整个迭代中应用函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51941464/