我有这样的代码:

good = [x for x in mylist if x in goodvals]
bad = [x for x in mylist if x not in goodvals]

目标是根据mylist的内容是否满足条件,将它们拆分为另外两个列表。

我怎样才能做得更优雅呢?我能避免在mylist上做两个单独的迭代吗?我可以通过这样做来提高性能吗?


当前回答

使用布尔逻辑将数据分配给两个数组

>>> images, anims = [[i for i in files if t ^ (i[2].lower() in IMAGE_TYPES) ] for t in (0, 1)]
>>> images
[('file1.jpg', 33, '.jpg')]
>>> anims
[('file2.avi', 999, '.avi')]

其他回答

def partition(pred, iterable):
    'Use a predicate to partition entries into false entries and true entries'
    # partition(is_odd, range(10)) --> 0 2 4 6 8   and  1 3 5 7 9
    t1, t2 = tee(iterable)
    return filterfalse(pred, t1), filter(pred, t2)

检查这个

我基本上喜欢安德斯的方法,因为它非常普遍。下面的版本将分类器放在前面(以匹配过滤器语法),并使用defaultdict(假定已导入)。

def categorize(func, seq):
    """Return mapping from categories to lists
    of categorized items.
    """
    d = defaultdict(list)
    for item in seq:
        d[func(item)].append(item)
    return d

所有提出的解决方案的问题是,它将扫描和应用过滤功能两次。我会做一个简单的小函数,像这样:

def split_into_two_lists(lst, f):
    a = []
    b = []
    for elem in lst:
        if f(elem):
            a.append(elem)
        else:
            b.append(elem)
    return a, b

这样你就不会重复处理任何东西,也不会重复代码。

清晰快速

这个列表理解是简单的阅读和快速。这正是上级要求的。

set_good_vals = set(good_vals)    # Speed boost.
good = [x for x in my_list if x in set_good_vals]
bad = [x for x in my_list if x not in set_good_vals]

我更喜欢一个列表理解而不是两个,但不像张贴的许多答案(其中一些相当巧妙),它是可读的和清晰的。这也是网页上最快的答案之一。

唯一(稍微)快一点的答案是:

set_good_vals = set(good_vals)
good, bad = [], []
for item in my_list:
    _ = good.append(item) if item in set_good_vals else bad.append(item)
    

...还有它的变体。(见我的另一个答案)。但我觉得第一种方法更优雅,而且几乎一样快。

如果你不想用两行代码来完成一个语义只需要一次的操作,你可以把上面的一些方法(甚至是你自己的方法)包装在一个函数中:

def part_with_predicate(l, pred):
    return [i for i in l if pred(i)], [i for i in l if not pred(i)]

这不是一种惰性计算方法,它确实对列表进行了两次迭代,但是它允许您在一行代码中对列表进行分区。