将多个 lambda 表达式组合成一行

Combining multiple lambda expressions into one line

一行实现,使用 lambda 表达式(map/filter/reduce), 获取不同类型列表的函数和 returns 具有这些键的字典: {‘c’:,‘i’:,‘f’:,‘o’:}

'c' 将显示字符列表 'i' 整数列表 'f' 花车列表 'o' 任何其他类型的列表

例如列表: myList = ['a', 2, 3, 's', 2.23]

输出将是: {'c': ['a', 's'], 'i': [2, 3], 'f': [2.23], 'o': [ ]}

到目前为止,我制作了一种可行的方法,但我需要以某种方式更改它的一行代码:

def q1a(myList):
   myDict = dict.fromkeys(('c', 'i', 'f', 'o'))
   myDict['c'] = list(filter(lambda x: type(x) is str, myList))
   myDict['i'] = list(filter(lambda x: type(x) is int, myList))
   myDict['f'] = list(filter(lambda x: type(x) is float, myList))
   myDict['o'] = list(filter(lambda x: type(x) is not float and type(x) is not int and type(x) is not str, myList))
   return myDict

这解决了一次分配一个键的需要:

def q1a(myList):
   return {
       'c': list(filter(lambda x: type(x) is str, myList)),
       'i': list(filter(lambda x: type(x) is int, myList)),
       'f': list(filter(lambda x: type(x) is float, myList)),
       'o': list(filter(lambda x: type(x) is not float and type(x) is not int and type(x) is not str, myList))
   }

您可以使用列表理解并创建字典文字:

{
    'c': [e for e in myList if type(e) is str],
    'i': [e for e in myList if type(e) is int],
    'f': [e for e in myList if type(e) is float],
    'o': [e for e in myList if type(e) not in {float, int, str}]
}

作业似乎要求您对此采取函数式方法并创建单个操作。

一个选项是一个操作 dict 的 reduce。这在 python 中不像在其他语言中那样自然,因为大多数字典操作 return None。但是如果你尝试的话,你仍然可以以一种功能性的方式来做到这一点(它是为了(小)提高可读性而分解的单行代码):

from functools import reduce

l = ['a', 2, 3, 's', 2.23]

res = reduce(
    lambda d, t: dict(d, **{t[1]: d[t[1]] + [t[0]]}),
    map(lambda el: (el, {str: 'c', int: 'i', float: 'f'}.get(type(el), 'o')) , l),
    dict.fromkeys('cifo', [])
)

print(res)
# {'c': ['a', 's'], 'i': [2, 3], 'f': [2.23], 'o': []}

这通过使用 map():

创建元组列表来实现
list(map(lambda el: (el, {str: 'c', int: 'i', float: 'f'}.get(type(el), 'o')) , l),)
# [('a', 'c'), (2, 'i'), (3, 'i'), ('s', 'c'), (2.23, 'f')]

然后更新 reduce 中的字典,该字典使用 dict.fromkeys('cifo', []) 创建的空列表进行初始化。

你可以使用下面丑陋的一行

out = dict(zip(['c','i','f','o'], map(list, (filter(lambda x:isinstance(x,str), lst), filter(lambda x:isinstance(x,int), lst), filter(lambda x:isinstance(x,float), lst), filter(lambda x: not isinstance(x,(str,float,int)), lst)))))

您还可以将 functools.reduce 与辅助函数一起使用(不完全是一个衬垫,但不需要多个 filter,因此可以节省时间):

def add(d, x):
    d[x[0]].append(x[1])
    return d
from functools import reduce
out = reduce(add, 
             map(lambda x: (('c',x) if isinstance(x,str) 
                            else (('i',x) if isinstance(x,int) 
                                  else (('f',x) if isinstance(x,float) 
                                        else ('o',x)))), lst), 
             {'c':[],'i':[],'f':[],'o':[]})

输出:

{'c': ['a', 's'], 'i': [2, 3], 'f': [2.23], 'o': []}

如果我在 Python 中写这个,我的第一选择是跳过函数式方法并使用无聊的 for 循环和 default 字典。

from collections import defaultdict


def q1a(myList):
    d = defaultdict(list)
    for v in myList:
        if isinstance(v, str):
            type_code = 'c'
        elif isinstance(v, int):
            type_code = 'i'
        elif isinstnace(v, float):
            type_code = 'f'
        else:
            type_code = 'o'
        d[tc].append(v)
    return d

但是,这表明使用 itertools.groupby 的解决方案。大 if 语句构成了一个像

这样的简单函数
def type_code(v):
    if isinstance(v, str):
        type_code = 'c'
    elif isinstance(v, int):
        type_code = 'i'
    elif isinstnace(v, float):
        type_code = 'f'
    else:
        type_code = 'o'
    return type_code

可以写成适合在 lambda 表达式中使用的单个条件表达式:

 lambda v: ('c' if isinstance(v, str) else 
            'i' if isinstance(v, int) else
            'f' if isinstance(v, float) else
            'o')

在下文中,为了便于阅读,我会将上述 lambda 表达式缩写为 tc

使用 itertools.groupby,您可以使用指定每个元素属于哪个分区的函数对(排序的)列表进行分区。这样产生的分区适合在字典理解中使用。

from itertools import groupby

def q1a(myList):
    return {k: list(vs) for k, vs in groupby(sorted(myList, key=tc), tc)}

一个小故障:

>>> q1a(['a', 2, 3, 's', 2.23])
{'c': ['a', 's'], 'f': [2.23], 'i': [2, 3]}

结果不包括不是由列表中的值生成的键。您必须将结果与包含所有键的预初始化 dict 合并。

def q1a(myList):
    return ({'c': [], 'i': [], 'f': [], 'o': []}
            | {k: list(vs) for k, vs in groupby(sorted(myList, key=tc), tc)})

我不使用 dict.fromKeys("cifo", []),因为这样每个键都默认引用 相同的 空列表,而不是每个键都有一个不同的空列表。

您可以使用双重间接将类型转换为字母并使用 setdefault 更新生成的字典:

def q1a(myList):
    types  = {str:'c', int:'i', float:'f'}
    myDict = dict()
    for x in myList:
        myDict.setdefault(types.get(type(x),'o'),[]).append(x)
    return myDict

myList = ['a', 2, 3, 's', 2.23]
print(q1a(myList))
{'c': ['a', 's'], 'i': [2, 3], 'f': [2.23]}

使用 defaultdict 可以稍微简化它:

from collections import defaultdict

def q1a(myList):
    types  = defaultdict(lambda:'o',{str:'c', int:'i', float:'f'})
    myDict = defaultdict(list)
    for x in myList:
        myDict[types[type(x)]].append(x)
    return dict(myDict)