如何在整个迭代中应用函数?

时间:2018-08-21 04:14:02

标签: python data-structures iterator

我希望函数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来处理字典,但是

  • 这很丑
  • 如果明天我能得到2树d.s.由不同人撰写

2 个答案:

答案 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)

这不需要任何中间临时数据结构(mapzip是基于延迟生成器的函数;它们仅在最后构造返回类型时才产生值)。假定所有非Mapping类型都可以通过迭代的值来构造,而所有Mapping类型都可以通过对的迭代来构造;对于通用内置集合(tuplelistsetfrozensetdict),就是这种情况。不太通用的容器不一定能工作(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

当新的数据结构类型到达时,您将需要为其注册新功能。