使用map函数应用多个函数

13

我有一些二维数据,希望能够对其应用多个函数。实际代码使用 xlrd 和一个 .xlsx 文件,但为了方便重现输出,我将提供以下样板代码。

class Data:
    def __init__(self, value):
        self.value = value

class Sheet:
    def __init__(self, data):
        self.data = [[Data(value) for value in row.split(',')] for row in data.split('\n')]
        self.ncols = max(len(row) for row in self.data)

    def col(self, index):
        return [row[index] for row in self.data]

创建一个工作表:

fake_data = '''a, b, c,
               1, 2, 3, 4
               e, f, g, 
               5, 6, i, 
                , 6,  , 
                ,  ,  ,  '''

sheet = Sheet(fake_data)

在这个对象中,data 包含一个二维字符串数组(根据输入格式),我想对这个对象的列执行操作。到目前为止,我无法控制任何东西。

我想对这个结构做三件事情:将行转置为列,从每个 Data 对象中提取 value 并尝试将其转换为 float。如果该值不是 float,则应将其转换为带有剥离空白的 str

from operators import attrgetter

# helper function
def parse_value(value):
    try:
        return float(value)
    except ValueError:
        return str(value).strip()

# transpose
raw_cols = map(sheet.col, range(sheet.ncols))

# extract values
value_cols = (map(attrgetter('value'), col) for col in raw_cols)

# convert values
typed_cols = (map(parse_value, col) for col in value_cols)

# ['a', 1.0, 'e', 5.0, '',  '']
# ['b', 2.0, 'f', 6.0, 6.0, '']
# ['c', 3.0, 'g', 'i', '',  '']
# ['',  4.0, '',  '',  '',  '']

可以看到,每个列都应用了两次map。在其他情况下,我想对每个列应用多于两次的函数。

有没有更好的方法将多个函数映射到可迭代对象的条目上?此外,是否有办法避免生成器推导式并直接将映射应用于每个内部可迭代对象?或者,是否有更好且可扩展的方法来处理所有这些问题?

请注意,这个问题不仅适用于xlrd,它只是当前的用例。


提醒:map(f, map(g, xs)) 的输出与 map(compose(f,g), xs) 相同;前者将遍历集合两次,而后者仅遍历一次。 - Mulan
@naomik 对的,我想我只是希望有一个内置的 compose - Jared Goguen
2
Jared,你可以很容易地自己制作。 compose仅仅是lambda f,g: lambda x: f(g(x))。或者只需使用map(lambda x: f(g(x)), xs) - Mulan
没错,又是一个加入到我的“库”文件夹中的东西。 - Jared Goguen
3个回答

16
似乎最简单的解决方案是编写自己的函数,将多个函数应用于同一可迭代对象。
def map_many(iterable, function, *other):
    if other:
        return map_many(map(function, iterable), *other)
    return map(function, iterable)

这里的缺点是使用方式与map(function, iterable)相反,而且在Python 3.X中扩展map接受参数会很麻烦。

用法:

map_many([0, 1, 2, 3, 4], str, lambda s: s + '0', int)
# [0, 10, 20, 30, 40]

6
你可以使用Lambda函数轻松地将最后两个map调用组合在一起,
typed_cols = (map(lambda element:parse_value(element['value']), col)
              for col in value_cols)

虽然你可以在 Sheet.col 中使用类似的方法进行解析和提取,但我认为这会影响代码的可读性。


2
正如Jared Goguen所注意到的那样,他的“map_many”的缺点是顺序相反。 这是一个常规顺序:
def map_many(function, *other, iterable=None):
    if iterable is None:
        *other, iterable = other
    if other:
        return map_many(*other, map(function, iterable))
    return map(function, iterable)

另一种更加函数化的方法:
def map_many(*funcs_with_iterable):
    *functions, iterable = funcs_with_iterable
    return reduce(lambda x, y: map(y, x), functions, iterable)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接