IT story

파이썬 : 조건에 따라 목록을 나누시겠습니까?

hot-time 2020. 4. 10. 08:23
반응형

파이썬 : 조건에 따라 목록을 나누시겠습니까?


미적 측면과 성능 측면에서 조건부에 따라 항목 목록을 여러 목록으로 분할하는 가장 좋은 방법은 무엇입니까? 다음과 같습니다.

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

더 우아한 방법이 있습니까?

업데이트 : 여기에 내가하려는 일을 더 잘 설명하기 위해 실제 사용 사례가 있습니다.

# files looks like: [ ('file1.jpg', 33L, '.jpg'), ('file2.avi', 999L, '.avi'), ... ]
IMAGE_TYPES = ('.jpg','.jpeg','.gif','.bmp','.png')
images = [f for f in files if f[2].lower() in IMAGE_TYPES]
anims  = [f for f in files if f[2].lower() not in IMAGE_TYPES]

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

더 우아한 방법이 있습니까?

이 코드는 완벽하게 읽을 수 있으며 매우 명확합니다!

# files looks like: [ ('file1.jpg', 33L, '.jpg'), ('file2.avi', 999L, '.avi'), ... ]
IMAGE_TYPES = ('.jpg','.jpeg','.gif','.bmp','.png')
images = [f for f in files if f[2].lower() in IMAGE_TYPES]
anims  = [f for f in files if f[2].lower() not in IMAGE_TYPES]

다시, 이것은 괜찮습니다!

세트를 사용하면 약간의 성능 향상이있을 수 있지만 사소한 차이가 있으므로 목록 이해가 훨씬 쉬워지며 주문이 엉망이되어 복제본이 제거되는 것에 대해 걱정할 필요가 없습니다.

사실, 나는 다른 단계 "뒤로"가서 간단한 for 루프를 사용할 수 있습니다.

images, anims = [], []

for f in files:
    if f.lower() in IMAGE_TYPES:
        images.append(f)
    else:
        anims.append(f)

목록 검사 또는 사용 set()은 다른 검사 또는 다른 비트의 논리를 추가해야 할 때까지 좋습니다 .0 바이트 jpeg를 모두 제거하려면 다음과 같이 추가하십시오.

if f[1] == 0:
    continue

good, bad = [], []
for x in mylist:
    (bad, good)[x in goodvals].append(x)

게으른 반복자 접근 방식은 다음과 같습니다.

from itertools import tee

def split_on_condition(seq, condition):
    l1, l2 = tee((condition(item), item) for item in seq)
    return (i for p, i in l1 if p), (i for p, i in l2 if not p)

항목 당 한 번 조건을 평가하고 두 개의 생성기를 리턴하며, 먼저 조건이 참인 순서에서 값을 산출하고 다른 조건이 참인 값을 산출합니다.

게으 르기 때문에 모든 반복자, 심지어 무한 반복자에서도 사용할 수 있습니다.

from itertools import count, islice

def is_prime(n):
    return n > 1 and all(n % i for i in xrange(2, n))

primes, not_primes = split_on_condition(count(), is_prime)
print("First 10 primes", list(islice(primes, 10)))
print("First 10 non-primes", list(islice(not_primes, 10)))

일반적으로 비 게으른 목록 반환 방법이 더 낫지 만

def split_on_condition(seq, condition):
    a, b = [], []
    for item in seq:
        (a if condition(item) else b).append(item)
    return a, b

편집 : 키를 사용하여 항목을 다른 목록으로 나누는 더 구체적인 유스 케이스의 경우 다음과 같은 일반적인 기능이 있습니다.

DROP_VALUE = lambda _:_
def split_by_key(seq, resultmapping, keyfunc, default=DROP_VALUE):
    """Split a sequence into lists based on a key function.

        seq - input sequence
        resultmapping - a dictionary that maps from target lists to keys that go to that list
        keyfunc - function to calculate the key of an input value
        default - the target where items that don't have a corresponding key go, by default they are dropped
    """
    result_lists = dict((key, []) for key in resultmapping)
    appenders = dict((key, result_lists[target].append) for target, keys in resultmapping.items() for key in keys)

    if default is not DROP_VALUE:
        result_lists.setdefault(default, [])
        default_action = result_lists[default].append
    else:
        default_action = DROP_VALUE

    for item in seq:
        appenders.get(keyfunc(item), default_action)(item)

    return result_lists

용법:

def file_extension(f):
    return f[2].lower()

split_files = split_by_key(files, {'images': IMAGE_TYPES}, keyfunc=file_extension, default='anims')
print split_files['images']
print split_files['anims']

제안 된 모든 솔루션의 문제점은 필터링 기능을 두 번 스캔하고 적용한다는 것입니다. 다음과 같이 간단한 작은 기능을 만들었습니다.

def SplitIntoTwoLists(l, f):
  a = []
  b = []
  for i in l:
    if f(i):
      a.append(i)
    else:
      b.append(i)
 return (a,b)

그렇게하면 아무것도 처리하지 않고 코드를 반복하지 않습니다.


내가 맡아 partition출력 하위 시퀀스에서 상대 순서를 유지 하는 게으른 단일 패스 함수를 제안합니다 .

1. 요구 사항

요구 사항은 다음과 같다고 가정합니다.

  • 요소의 상대적 순서 유지 (따라서 세트와 사전 없음)
  • 모든 요소에 대해 조건을 한 번만 평가하십시오 (따라서 ( i) filter또는 groupby)
  • 두 시퀀스 중 하나의 지연 소비를 허용합니다 (사전 계산할 여유가 있다면 순진한 구현도 수용 가능할 것입니다)

2. split도서관

partition기능 (아래 소개) 및 기타 유사한 기능으로 작은 라이브러리로 만들었습니다.

PyPI를 통해 정상적으로 설치 가능합니다 :

pip install --user split

조건에 따라 목록 기반을 분할하려면 partitionfunction을 사용하십시오 .

>>> from split import partition
>>> files = [ ('file1.jpg', 33L, '.jpg'), ('file2.avi', 999L, '.avi') ]
>>> image_types = ('.jpg','.jpeg','.gif','.bmp','.png')
>>> images, other = partition(lambda f: f[-1] in image_types, files)
>>> list(images)
[('file1.jpg', 33L, '.jpg')]
>>> list(other)
[('file2.avi', 999L, '.avi')]

3. partition기능 설명

내부적으로 우리는 한 번에 두 개의 서브 시퀀스를 구축해야하므로 하나의 출력 시퀀스 만 소비하면 다른 하나의 시퀀스도 계산됩니다. 또한 사용자 요청 (상점 처리되었지만 아직 요청되지 않은 요소)간에 상태를 유지해야합니다. 상태를 유지하기 위해 두 개의 이중 끝 큐 ( deques)를 사용합니다.

from collections import deque

SplitSeq 수업은 하우스 키핑을 담당합니다.

class SplitSeq:
    def __init__(self, condition, sequence):
        self.cond = condition
        self.goods = deque([])
        self.bads = deque([])
        self.seq = iter(sequence)

마술은 그 .getNext()방법으로 일어난다 . 그것은 거의 .next()반복자와 비슷하지만 이번에는 원하는 요소를 지정할 수 있습니다. 배후에서 거부 된 요소를 버리지 않고 대신 두 개의 대기열 중 하나에 넣습니다.

    def getNext(self, getGood=True):
        if getGood:
            these, those, cond = self.goods, self.bads, self.cond
        else:
            these, those, cond = self.bads, self.goods, lambda x: not self.cond(x)
        if these:
            return these.popleft()
        else:
            while 1: # exit on StopIteration
                n = self.seq.next()
                if cond(n):
                    return n
                else:
                    those.append(n)

최종 사용자는 partition기능 을 사용해야 합니다. 조건 함수와 시퀀스 ( map또는 처럼 filter)를 취하고 두 개의 생성기를 반환합니다. 첫 번째 생성기는 조건이 유지되는 요소의 하위 시퀀스를 작성하고 두 번째 생성기는 상보 하위 시퀀스를 작성합니다. 반복자와 생성기는 길거나 무한한 시퀀스의 지연 분할을 허용합니다.

def partition(condition, sequence):
    cond = condition if condition else bool  # evaluate as bool if condition == None
    ss = SplitSeq(cond, sequence)
    def goods():
        while 1:
            yield ss.getNext(getGood=True)
    def bads():
        while 1:
            yield ss.getNext(getGood=False)
    return goods(), bads()

나는 미래에 부분 적용을 용이하게하기 위해 테스트 함수를 첫 번째 인수로 선택했다 (테스트 함수를 첫 번째 인수로 사용하는 방법 map유사 함 filter).


첫 번째 이동 (사전 편집) : 세트 사용 :

mylist = [1,2,3,4,5,6,7]
goodvals = [1,3,7,8,9]

myset = set(mylist)
goodset = set(goodvals)

print list(myset.intersection(goodset))  # [1, 3, 7]
print list(myset.difference(goodset))    # [2, 4, 5, 6]

가독성 (IMHO)과 성능 모두에 좋습니다.

두 번째 이동 (post-OP-edit) :

좋은 확장 프로그램 목록을 세트로 작성하십시오.

IMAGE_TYPES = set(['.jpg','.jpeg','.gif','.bmp','.png'])

그러면 성능이 향상됩니다. 그렇지 않으면, 당신이 나에게 잘 보이는 것.


나는 기본적으로 Anders의 접근 방식을 매우 좋아합니다. 다음은 분류기를 가장 먼저 (필터 구문과 일치시키기 위해) 배치하고 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

itertools.groupby 는 하나의 연속 범위를 갖도록 항목을 정렬해야한다는 점을 제외하고 원하는 것을 거의 수행하므로 키로 먼저 정렬해야합니다 (그렇지 않으면 각 유형에 대해 여러 개의 인터리브 그룹을 얻습니다). 예.

def is_good(f):
    return f[2].lower() in IMAGE_TYPES

files = [ ('file1.jpg', 33L, '.jpg'), ('file2.avi', 999L, '.avi'), ('file3.gif', 123L, '.gif')]

for key, group in itertools.groupby(sorted(files, key=is_good), key=is_good):
    print key, list(group)

제공합니다 :

False [('file2.avi', 999L, '.avi')]
True [('file1.jpg', 33L, '.jpg'), ('file3.gif', 123L, '.gif')]

다른 솔루션과 마찬가지로 주요 기능은 원하는 수의 그룹으로 나누도록 정의 할 수 있습니다.


개인적으로, 나는 당신이 이미 goodvals매달려 있는 목록을 가지고 있다고 가정하고 당신이 인용 한 버전을 좋아합니다 . 그렇지 않은 경우 :

good = filter(lambda x: is_good(x), mylist)
bad = filter(lambda x: not is_good(x), mylist)

물론 이것은 원래했던 것처럼 목록 이해를 사용하는 것과 매우 유사하지만 조회 대신 함수를 사용합니다.

good = [x for x in mylist if is_good(x)]
bad  = [x for x in mylist if not is_good(x)]

일반적으로 나는 목록 이해의 미학이 매우 기쁘게 생각합니다. 물론 실제로 순서를 유지할 필요가없고 복제본이 필요하지 않은 경우 세트 에서 intersectionand difference메소드를 사용하면 효과가 있습니다.


FP 스타일로 작성하려면 다음을 수행하십시오.

good, bad = [ sum(x, []) for x in zip(*(([y], []) if y in goodvals else ([], [y])
                                        for y in mylist)) ]

가장 읽기 쉬운 솔루션은 아니지만 최소한 mylist를 한 번만 반복합니다.


N 조건에 따라 iterable을 분할하는 일반화가 편리하다고 생각합니다.

from collections import OrderedDict
def partition(iterable,*conditions):
    '''Returns a list with the elements that satisfy each of condition.
       Conditions are assumed to be exclusive'''
    d= OrderedDict((i,list())for i in range(len(conditions)))        
    for e in iterable:
        for i,condition in enumerate(conditions):
            if condition(e):
                d[i].append(e)
                break                    
    return d.values()

예를 들어 :

ints,floats,other = partition([2, 3.14, 1, 1.69, [], None],
                              lambda x: isinstance(x, int), 
                              lambda x: isinstance(x, float),
                              lambda x: True)

print " ints: {}\n floats:{}\n other:{}".format(ints,floats,other)

 ints: [2, 1]
 floats:[3.14, 1.69]
 other:[[], None]

요소가 여러 조건을 만족할 수있는 경우 중단을 제거하십시오.


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)

이것을 확인 하십시오


때로는 목록 이해가 최선의 방법이 아닌 것처럼 보입니다!

사람들 이이 주제에 대한 답변을 기반으로 작은 테스트를 수행하여 무작위로 생성 된 목록에서 테스트했습니다. 다음은 목록 생성입니다 (아마도 더 좋은 방법이 있지만 요점이 아닙니다).

good_list = ('.jpg','.jpeg','.gif','.bmp','.png')

import random
import string
my_origin_list = []
for i in xrange(10000):
    fname = ''.join(random.choice(string.lowercase) for i in range(random.randrange(10)))
    if random.getrandbits(1):
        fext = random.choice(good_list)
    else:
        fext = "." + ''.join(random.choice(string.lowercase) for i in range(3))

    my_origin_list.append((fname + fext, random.randrange(1000), fext))

우리가 간다

# Parand
def f1():
    return [e for e in my_origin_list if e[2] in good_list], [e for e in my_origin_list if not e[2] in good_list]

# dbr
def f2():
    a, b = list(), list()
    for e in my_origin_list:
        if e[2] in good_list:
            a.append(e)
        else:
            b.append(e)
    return a, b

# John La Rooy
def f3():
    a, b = list(), list()
    for e in my_origin_list:
        (b, a)[e[2] in good_list].append(e)
    return a, b

# Ants Aasma
def f4():
    l1, l2 = tee((e[2] in good_list, e) for e in my_origin_list)
    return [i for p, i in l1 if p], [i for p, i in l2 if not p]

# My personal way to do
def f5():
    a, b = zip(*[(e, None) if e[2] in good_list else (None, e) for e in my_origin_list])
    return list(filter(None, a)), list(filter(None, b))

# BJ Homer
def f6():
    return filter(lambda e: e[2] in good_list, my_origin_list), filter(lambda e: not e[2] in good_list, my_origin_list)

cmpthese 함수를 사용하면 최상의 결과는 dbr answer입니다.

f1     204/s  --    -5%   -14%   -15%   -20%   -26%
f6     215/s     6%  --    -9%   -11%   -16%   -22%
f3     237/s    16%    10%  --    -2%    -7%   -14%
f4     240/s    18%    12%     2%  --    -6%   -13%
f5     255/s    25%    18%     8%     6%  --    -8%
f2     277/s    36%    29%    17%    15%     9%  --

이 문제에 대한 또 다른 해결책. 가능한 빠른 솔루션이 필요했습니다. 즉, 목록을 한 번만 반복하고 결과 목록 중 하나에 데이터를 추가하기위한 O (1)이 바람직합니다. 이것은 훨씬 짧은 것을 제외하고 sastanin이 제공하는 솔루션과 매우 유사합니다 .

from collections import deque

def split(iterable, function):
    dq_true = deque()
    dq_false = deque()

    # deque - the fastest way to consume an iterator and append items
    deque((
      (dq_true if function(item) else dq_false).append(item) for item in iterable
    ), maxlen=0)

    return dq_true, dq_false

그런 다음 다음과 같은 방식으로 기능을 사용할 수 있습니다.

lower, higher = split([0,1,2,3,4,5,6,7,8,9], lambda x: x < 5)

selected, other = split([0,1,2,3,4,5,6,7,8,9], lambda x: x in {0,4,9})

당신이 결과하지 좋은 경우 deque개체를 쉽게로 변환 할 수 있습니다 list, set무엇이든 당신처럼 (예를 들어 list(lower)). 변환은 목록의 구성보다 훨씬 빠릅니다.

이 방법은 항목의 순서와 복제본을 유지합니다.


성능을 위해을 시도하십시오 itertools.

itertools의 모듈은 빠른 일련의 핵심, 그 자체로 또는 조합에 유용한 메모리 효율적인 도구를 표준화. 이들은 함께 "반복 대수"를 형성하여 순수 Python에서 간결하고 효율적으로 특수 도구를 구성 할 수 있습니다.

itertools.ifilter 또는 imap을 참조하십시오 .

itertools.ifilter (조건 자, 반복 가능)

술어가 True 인 요소 만 리턴하여 반복 가능한 요소를 필터링하는 반복자를 작성하십시오.


때로는 목록의 다른 절반이 필요하지 않을 수도 있습니다. 예를 들면 다음과 같습니다.

import sys
from itertools import ifilter

trustedPeople = sys.argv[1].split(',')
newName = sys.argv[2]

myFriends = ifilter(lambda x: x.startswith('Shi'), trustedPeople)

print '%s is %smy friend.' % (newName, newName not in myFriends 'not ' or '')

예를 들어, 짝수와 홀수로 목록 나누기

arr = range(20)
even, odd = reduce(lambda res, next: res[next % 2].append(next) or res, arr, ([], []))

또는 일반적으로 :

def split(predicate, iterable):
    return reduce(lambda res, e: res[predicate(e)].append(e) or res, iterable, ([], []))

장점 :

  • 가능한 가장 짧은 방법
  • 술어는 각 요소에 대해 한 번만 적용됩니다.

단점

  • 기능적 프로그래밍 패러다임에 대한 지식 필요

@gnibbler의 위대한 (그러나 간결한!) 답변 에서 영감을 얻은 우리는 여러 파티션에 매핑하는 방법을 적용 할 수 있습니다.

from collections import defaultdict

def splitter(l, mapper):
    """Split an iterable into multiple partitions generated by a callable mapper."""

    results = defaultdict(list)

    for x in l:
        results[mapper(x)] += [x]

    return results

그런 splitter다음 다음과 같이 사용할 수 있습니다.

>>> l = [1, 2, 3, 4, 2, 3, 4, 5, 6, 4, 3, 2, 3]
>>> split = splitter(l, lambda x: x % 2 == 0)  # partition l into odds and evens
>>> split.items()
>>> [(False, [1, 3, 3, 5, 3, 3]), (True, [2, 4, 2, 4, 6, 4, 2])]

이것은 더 복잡한 매핑 (및 반복자)이있는 두 개 이상의 파티션에서 작동합니다.

>>> import math
>>> l = xrange(1, 23)
>>> split = splitter(l, lambda x: int(math.log10(x) * 5))
>>> split.items()
[(0, [1]),
 (1, [2]),
 (2, [3]),
 (3, [4, 5, 6]),
 (4, [7, 8, 9]),
 (5, [10, 11, 12, 13, 14, 15]),
 (6, [16, 17, 18, 19, 20, 21, 22])]

또는 사전을 사용하여 매핑 :

>>> map = {'A': 1, 'X': 2, 'B': 3, 'Y': 1, 'C': 2, 'Z': 3}
>>> l = ['A', 'B', 'C', 'C', 'X', 'Y', 'Z', 'A', 'Z']
>>> split = splitter(l, map.get)
>>> split.items()
(1, ['A', 'Y', 'A']), (2, ['C', 'C', 'X']), (3, ['B', 'Z', 'Z'])]

bad = []
good = [x for x in mylist if x in goodvals or bad.append(x)]

추가는 없음을 반환하므로 작동합니다.


영리한 것을 고집한다면 Winden의 솔루션과 약간의 가짜 영리함을 취할 수 있습니다.

def splay(l, f, d=None):
  d = d or {}
  for x in l: d.setdefault(f(x), []).append(x)
  return d

여기에 이미 몇 가지 해결책이 있지만 또 다른 방법은 다음과 같습니다.

anims = []
images = [f for f in files if (lambda t: True if f[2].lower() in IMAGE_TYPES else anims.append(t) and False)(f)]

목록을 한 번만 반복하면 좀 더 파이썬처럼 보이므로 읽을 수 있습니다.

>>> files = [ ('file1.jpg', 33L, '.jpg'), ('file2.avi', 999L, '.avi'), ('file1.bmp', 33L, '.bmp')]
>>> IMAGE_TYPES = ('.jpg','.jpeg','.gif','.bmp','.png')
>>> anims = []
>>> images = [f for f in files if (lambda t: True if f[2].lower() in IMAGE_TYPES else anims.append(t) and False)(f)]
>>> print '\n'.join([str(anims), str(images)])
[('file2.avi', 999L, '.avi')]
[('file1.jpg', 33L, '.jpg'), ('file1.bmp', 33L, '.bmp')]
>>>

목록을 필터링하는 것과 술어의 평가를 분리하는 2 패스 접근법을 사용합니다.

def partition(pred, iterable):
    xs = list(zip(map(pred, iterable), iterable))
    return [x[1] for x in xs if x[0]], [x[1] for x in xs if not x[0]]

성능면에서 (의 pred각 멤버에 대해 한 번만 평가하는 것 외에도) 이것에 대해 좋은 iterable점은 많은 논리를 인터프리터에서 고도로 최적화 된 반복 및 매핑 코드로 이동한다는 것입니다. 답변에 설명 것처럼 긴 반복 가능 항목에 대한 반복 속도를 높일 수 있습니다 .

표현 측면에서는 이해력 및 매핑과 같은 표현 관용구를 활용합니다.


해결책

from itertools import tee

def unpack_args(fn):
    return lambda t: fn(*t)

def separate(fn, lx):
    return map(
        unpack_args(
            lambda i, ly: filter(
                lambda el: bool(i) == fn(el),
                ly)),
        enumerate(tee(lx, 2)))

테스트

[even, odd] = separate(
    lambda x: bool(x % 2),
    [1, 2, 3, 4, 5])
print(list(even) == [2, 4])
print(list(odd) == [1, 3, 5])

외부 라이브러리를 사용하는 것이 마음에 들지 않으면이 작업을 적극적으로 구현한다는 것을 알고 있습니다.

>>> files = [ ('file1.jpg', 33, '.jpg'), ('file2.avi', 999, '.avi')]
>>> IMAGE_TYPES = ('.jpg','.jpeg','.gif','.bmp','.png')
  • iteration_utilities.partition:

    >>> from iteration_utilities import partition
    >>> notimages, images = partition(files, lambda x: x[2].lower() in IMAGE_TYPES)
    >>> notimages
    [('file2.avi', 999, '.avi')]
    >>> images
    [('file1.jpg', 33, '.jpg')]
    
  • more_itertools.partition

    >>> from more_itertools import partition
    >>> notimages, images = partition(lambda x: x[2].lower() in IMAGE_TYPES, files)
    >>> list(notimages)  # returns a generator so you need to explicitly convert to list.
    [('file2.avi', 999, '.avi')]
    >>> list(images)
    [('file1.jpg', 33, '.jpg')]
    

이것이 좋은 접근 방법인지 확실하지 않지만이 방법으로도 수행 할 수 있습니다

IMAGE_TYPES = ('.jpg','.jpeg','.gif','.bmp','.png')
files = [ ('file1.jpg', 33L, '.jpg'), ('file2.avi', 999L, '.avi')]
images, anims = reduce(lambda (i, a), f: (i + [f], a) if f[2] in IMAGE_TYPES else (i, a + [f]), files, ([], []))

목록이 그룹 및 간헐적 구분자로 구성된 경우 다음을 사용할 수 있습니다.

def split(items, p):
    groups = [[]]
    for i in items:
        if p(i):
            groups.append([])
        groups[-1].append(i)
    return groups

용법:

split(range(1,11), lambda x: x % 3 == 0)
# gives [[1, 2], [3, 4, 5], [6, 7, 8], [9, 10]]

이것이 가장 빠른 방법입니다.

if elsedbr의 답변과 같은을 사용 하지만 먼저 세트를 만듭니다. 세트는 O (m * n)에서 O (log m) + O (n)까지 작업 수를 줄여서 속도가 45 % 이상 증가합니다.

good_list_set = set(good_list)  # 45% faster than a tuple.

good, bad = [], []
for item in my_origin_list:
    if item in good_list_set:
        good.append(item)
    else:
        bad.append(item)

조금 더 짧습니다 :

good_list_set = set(good_list)  # 45% faster than a tuple.

good, bad = [], []
for item in my_origin_list:
    out = good if item in good_list_set else bad
    out.append(item)

벤치 마크 결과 :

filter_BJHomer                  80/s       --   -3265%   -5312%   -5900%   -6262%   -7273%   -7363%   -8051%   -8162%   -8244%
zip_Funky                       118/s    4848%       --   -3040%   -3913%   -4450%   -5951%   -6085%   -7106%   -7271%   -7393%
two_lst_tuple_JohnLaRoy         170/s   11332%    4367%       --   -1254%   -2026%   -4182%   -4375%   -5842%   -6079%   -6254%
if_else_DBR                     195/s   14392%    6428%    1434%       --    -882%   -3348%   -3568%   -5246%   -5516%   -5717%
two_lst_compr_Parand            213/s   16750%    8016%    2540%     967%       --   -2705%   -2946%   -4786%   -5083%   -5303%
if_else_1_line_DanSalmo         292/s   26668%   14696%    7189%    5033%    3707%       --    -331%   -2853%   -3260%   -3562%
tuple_if_else                   302/s   27923%   15542%    7778%    5548%    4177%     343%       --   -2609%   -3029%   -3341%
set_1_line                      409/s   41308%   24556%   14053%   11035%    9181%    3993%    3529%       --    -569%    -991%
set_shorter                     434/s   44401%   26640%   15503%   12303%   10337%    4836%    4345%     603%       --    -448%
set_if_else                     454/s   46952%   28358%   16699%   13349%   11290%    5532%    5018%    1100%     469%       --

Python 3.7의 전체 벤치 마크 코드 (FunkySayu에서 수정) :

good_list = ['.jpg','.jpeg','.gif','.bmp','.png']

import random
import string
my_origin_list = []
for i in range(10000):
    fname = ''.join(random.choice(string.ascii_lowercase) for i in range(random.randrange(10)))
    if random.getrandbits(1):
        fext = random.choice(list(good_list))
    else:
        fext = "." + ''.join(random.choice(string.ascii_lowercase) for i in range(3))

    my_origin_list.append((fname + fext, random.randrange(1000), fext))

# Parand
def two_lst_compr_Parand(*_):
    return [e for e in my_origin_list if e[2] in good_list], [e for e in my_origin_list if not e[2] in good_list]

# dbr
def if_else_DBR(*_):
    a, b = list(), list()
    for e in my_origin_list:
        if e[2] in good_list:
            a.append(e)
        else:
            b.append(e)
    return a, b

# John La Rooy
def two_lst_tuple_JohnLaRoy(*_):
    a, b = list(), list()
    for e in my_origin_list:
        (b, a)[e[2] in good_list].append(e)
    return a, b

# # Ants Aasma
# def f4():
#     l1, l2 = tee((e[2] in good_list, e) for e in my_origin_list)
#     return [i for p, i in l1 if p], [i for p, i in l2 if not p]

# My personal way to do
def zip_Funky(*_):
    a, b = zip(*[(e, None) if e[2] in good_list else (None, e) for e in my_origin_list])
    return list(filter(None, a)), list(filter(None, b))

# BJ Homer
def filter_BJHomer(*_):
    return list(filter(lambda e: e[2] in good_list, my_origin_list)), list(filter(lambda e: not e[2] in good_list,                                                                             my_origin_list))

# ChaimG's answer; as a list.
def if_else_1_line_DanSalmo(*_):
    good, bad = [], []
    for e in my_origin_list:
        _ = good.append(e) if e[2] in good_list else bad.append(e)
    return good, bad

# ChaimG's answer; as a set.
def set_1_line(*_):
    good_list_set = set(good_list)
    good, bad = [], []
    for e in my_origin_list:
        _ = good.append(e) if e[2] in good_list_set else bad.append(e)
    return good, bad

# ChaimG set and if else list.
def set_shorter(*_):
    good_list_set = set(good_list)
    good, bad = [], []
    for e in my_origin_list:
        out = good if e[2] in good_list_set else bad
        out.append(e)
    return good, bad

# ChaimG's best answer; if else as a set.
def set_if_else(*_):
    good_list_set = set(good_list)
    good, bad = [], []
    for e in my_origin_list:
        if e[2] in good_list_set:
            good.append(e)
        else:
            bad.append(e)
    return good, bad

# ChaimG's best answer; if else as a set.
def tuple_if_else(*_):
    good_list_tuple = tuple(good_list)
    good, bad = [], []
    for e in my_origin_list:
        if e[2] in good_list_tuple:
            good.append(e)
        else:
            bad.append(e)
    return good, bad

def cmpthese(n=0, functions=None):
    results = {}
    for func_name in functions:
        args = ['%s(range(256))' % func_name, 'from __main__ import %s' % func_name]
        t = Timer(*args)
        results[func_name] = 1 / (t.timeit(number=n) / n) # passes/sec

    functions_sorted = sorted(functions, key=results.__getitem__)
    for f in functions_sorted:
        diff = []
        for func in functions_sorted:
            if func == f:
                diff.append("--")
            else:
                diff.append(f"{results[f]/results[func]*100 - 100:5.0%}")
        diffs = " ".join(f'{x:>8s}' for x in diff)

        print(f"{f:27s} \t{results[f]:,.0f}/s {diffs}")


if __name__=='__main__':
    from timeit import Timer
cmpthese(1000, 'two_lst_compr_Parand if_else_DBR two_lst_tuple_JohnLaRoy zip_Funky filter_BJHomer if_else_1_line_DanSalmo set_1_line set_if_else tuple_if_else set_shorter'.split(" "))

images = [f for f in files if f[2].lower() in IMAGE_TYPES]
anims  = [f for f in files if f not in images]

예를 들어 조건이 더 길면 좋습니다. 독자는 부정적인 조건과 다른 모든 사례를 포착하는지 여부를 파악할 필요가 없습니다.


또 다른 대답은 짧지 만 "악"입니다 (목록 이해 부작용).

digits = list(range(10))
odd = [x.pop(i) for i, x in enumerate(digits) if x % 2]

>>> odd
[1, 3, 5, 7, 9]

>>> digits
[0, 2, 4, 6, 8]

good.append(x) if x in goodvals else bad.append(x)

@dansalmo의 우아하고 간결한 답변은 댓글에 묻혀 있으므로 여기에 답변으로 다시 게시하여 특히 새로운 독자에게 탁월한 가치를 얻을 수 있습니다.

완전한 예 :

good, bad = [], []
for x in my_list:
    good.append(x) if x in goodvals else bad.append(x)

참고 URL : https://stackoverflow.com/questions/949098/python-split-a-list-based-on-a-condition

반응형