【问题标题】:Combining multiple lambda expressions into one line将多个 lambda 表达式组合成一行
【发布时间】:2021-12-30 21:31:05
【问题描述】:

在一行中实现,使用 lambda 表达式(map/filter/reduce), 获取不同类型列表并返回具有以下键的字典的函数: {‘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

【问题讨论】:

  • 并不是所有的东西都应该简化为 Python 中的功能单行。使用无聊的for 循环可以更好地完成此操作,该循环一次更新dict 一个值。如果我必须在一行中执行此操作,我将使用 itertools.groupby 和一个函数,该函数接受一个值并返回该值类型的单字母“类别”。

标签: python list dictionary filter lambda


【解决方案1】:

你可以使用下面丑陋的单线

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 与辅助函数一起使用(不完全是一个衬里,但不需要多个filters,这样可以节省时间):

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': []}

【讨论】:

    【解决方案2】:

    任务似乎要求您对此采取功能性方法并创建一个操作。

    一个选项是单个 reduce 来操作 dict。这在 python 中不像在其他语言中那样自然,因为大多数 dict 操作返回 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,该dict 使用dict.fromkeys('cifo', []) 创建的空列表进行初始化。

    【讨论】:

    • 您的解决方案背后的想法与我的相似,但实现要好得多! +1
    【解决方案3】:

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

    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))
       }
    

    【讨论】:

    • 列表理解怎么样?
    • OP 要求明确使用 map/reduce/filter。
    【解决方案4】:

    如果我用 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", []),因为这样每个键默认为对相同空列表的引用,而不是每个单独的空列表。

    【讨论】:

    • 使用dict 甚至defaultdict 构建类型代码映射怎么样
    【解决方案5】:

    您可以使用列表推导并创建字典文字:

    {
        '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}]
    }
    

    【讨论】:

      【解决方案6】:

      您可以使用双重间接将类型转换为字母并设置默认值来更新结果字典:

      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)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-08-06
        • 1970-01-01
        • 1970-01-01
        • 2013-03-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多