【问题标题】:elegantly splitting a list (or dict) into two via some arbitrary function in python通过python中的一些任意函数优雅地将一个列表(或字典)分成两个
【发布时间】:2011-10-16 18:09:52
【问题描述】:

是否有任何优雅的方法可以在 python 中将一个列表/字典拆分为两个列表/字典,并采用一些任意拆分器函数?

我可以轻松拥有两个列表推导或两个选择,但在我看来应该有更好的方法来避免对每个元素进行两次迭代。

我可以使用 for 循环和 if 语句轻松完成此操作,但这需要 7 行代码来完成本应非常简单的操作。

有什么想法吗?

编辑:

仅供参考,我的两个解决方案是,

# given dict cows, mapping cow names to weight
# fast solution
fatcows = {}
thincows = {}
for name, weight in cows:
    if weight < 100:
        thincows[name] = weight
    else:
        fatcows[name] = weight

# double-list-comprehension solution would be
fatcows = {name: weight for name, weight in cows.items() if weight > 100}
thincows = {name: weight for name, weight in cows.items() if weight < 100}

我在想一定有比这更优雅的东西,我从来没有想过,比如:

thincows, fatcows = ??? short expression involving cows ???

我知道可以通过编写高阶函数来为我做这件事,而且我知道如何手动做。我只是想知道是否有一些超级优雅的语言功能可以为我做这件事。

这就像你可以编写自己的子例程和诸如在列表上执行 SELECT 之类的东西,或者你可以直接说

thincows = select(cows, lambda c: c.weight < 100)

我希望有一些类似的优雅方式拆分列表,一次通过

【问题讨论】:

  • 你能举一个你提到的任意分割函数的例子吗?更好的是,展示一个你想加强的例子。
  • 你能添加你的七行代码吗?
  • 滚动浏览答案,很震惊没有看到 itertools.ifilter——我认为我误解了(另一个)itertools 函数,因为你的问题听起来像是另一个“有没有更好的方法来做(在此处插入由我重新制作的 itertools 函数)?”,这似乎出现了很多。但是看到底部超级用户对此的回答,我松了一口气。您应该接受 adam Rosenfeld 的回答,因为它是 python 标准库的方式来做您想做的事。 Gratz 为您的第一个问题提出了一个如此受欢迎的问题!

标签: python select dictionary list-comprehension


【解决方案1】:

它可以通过genex、排序和itertools.groupby() 来完成,但它可能不会比蛮力解决方案更有效。

暴力破解:

def bifurcate(pred, seq):
  if pred is None:
    pred = lambda x: x
  res1 = []
  res2 = []
  for i in seq:
    if pred(i):
      res1.append(i)
    else:
      res2.append(i)
  return (res2, res1)

优雅的解决方案:

import itertools
import operator

def bifurcate(pred, seq):
  if pred is None:
    pred = lambda x: x
  return tuple([z[1] for z in y[1]] for y in
    itertools.groupby(sorted((bool(pred(x)), x) for x in seq),
    operator.itemgetter(0)))

【讨论】:

  • 您的蛮力解决方案将比您的“优雅”解决方案更有效。 Brute 执行一次,优雅执行一个排序(以及更多)。
【解决方案2】:

非常简单,无需任何外部工具:

my_list = [1,2,3,4]
list_a = []
list_b = []

def my_function(num):
    return num % 2

generator = (list_a.append(item) if my_function(item) else list_b.append(item)\
        for item in my_list)
for _ in generator:
    pass

【讨论】:

    【解决方案3】:

    任何解决方案都将花费 O(N) 时间来计算,无论是通过两次遍历列表还是一次遍历每个项目做更多的工作。最简单的方法就是使用您可用的工具:itertools.ifilteritertools.ifilterfalse

    def bifurcate(predicate, iterable):
        """Returns a tuple of two lists, the first of which contains all of the
           elements x of `iterable' for which predicate(x) is True, and the second
           of which contains all of the elements x of `iterable` for which
           predicate(x) is False."""
        return (itertools.ifilter(predicate, iterable),
                itertools.ifilterfalse(predicate, iterable))
    

    【讨论】:

    • 我投票赞成这个答案。显然 ifilter 并不像说的那样出名,(对我来说)更令人困惑的 groupby。
    【解决方案4】:

    好的,关于奶牛:)

    cows = {'a': 123, 'b': 90, 'c': 123, 'd': 70}
    
    select = lambda cows, accept: {name: weight for name, weight
                                   in cows.items()
                                   if accept(weight)}
    
    thin = select(cows, lambda x: x < 100)
    fat  = select(cows, lambda x: x > 100)
    

    【讨论】:

    • Don't name your function select -- select 也是 select module 的名称,这是一个相当重要的模块,用于在多个文件上进行高效的非轮询 I/O描述符同时进行。
    【解决方案5】:

    更多的乐趣与奶牛:)

    import random; random.seed(42)
    cows = {n:random.randrange(50,150) for n in 'abcdefghijkl'}
    
    thin = {}
    for name, weight in cows.iteritems():
        thin.setdefault(weight < 100, {})[name] = weight
    
    >>> thin[True]
    {'c': 77, 'b': 52, 'd': 72, 'i': 92, 'h': 58, 'k': 71, 'j': 52}
    
    >>> thin[False]
    {'a': 113, 'e': 123, 'l': 100, 'g': 139, 'f': 117}
    

    【讨论】:

    • 很好,我真的很喜欢这个=)
    【解决方案6】:

    3行怎么样?

    fatcows, thincows = {}, {}
    for name, weight in cows.items():
        (fatcows if weight > 50 else thincows)[name] = weight
    

    测试:

    >>> cows = {'bessie':53, 'maud':22, 'annabel': 77, 'myrna':43 }
    >>> fatcows, thincows = {}, {}
    >>> for name, weight in cows.items():
    ...     (fatcows if weight > 50 else thincows)[name] = weight
    ... 
    >>> fatcows
    {'annabel': 77, 'bessie': 53}
    >>> thincows
    {'maud': 22, 'myrna': 43}
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-02-18
      • 2022-01-21
      • 1970-01-01
      • 2020-05-23
      • 2017-10-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多