我希望函数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__()
给出任何可迭代的下一个元素,那么什么特殊的函数将赋予上行者将任何xyz添加到可迭代的d.s。为什么附加仅用于列表,而添加仅用于集合?为什么没有像__iter__
和__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来处理字典,但是
答案 0 :(得分: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
那样做,然后将其留给调用者以将结果重新打包为合适的数据结构(例如,{{1} } s,他们可以显式传递期望键/值dict
的映射函数并传递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))
?
答案 1 :(得分:1)
我认为functools.singledispatch()
可以为您提供所需的功能,使代码清晰。
from functools import singledispatch
@singledispatch
def f(i_ds: list, func):
holder = list()
for val in i_ds:
new_val = func(val)
holder.append(new_val)
return holder
@f.register(set)
def _(i_ds: set, func):
holder = set()
for val in i_ds:
new_val = func(val)
holder.add(new_val)
return holder
@f.register(dict)
def _(i_ds: dict, func):
holder = dict()
for k, v in i_ds.items():
new_key = func(k)
holder[new_key] = v
return holder
print(f([65, 97], chr))
print(f({65, 97}, chr))
print(f({65: 'upper_a', 97: 'lower_a'}, chr))
singledispatch仅适用于第一个参数,但您可以使用multidispatch第三方库(https://github.com/mrocklin/multipledispatch)
当新的数据结构类型到达时,您将需要为其注册新功能。