[python] Python이 목록에있는 여러 값의 멤버 자격을 테스트 할 수 있습니까?

둘 이상의 값이 목록에 포함되어 있는지 테스트하고 싶지만 예상치 못한 결과가 나타납니다.

>>> 'a','b' in ['b', 'a', 'foo', 'bar']
('a', True)

그렇다면 Python은 목록에서 한 번에 여러 값의 멤버 자격을 테스트 할 수 있습니까? 그 결과는 무엇을 의미합니까?



답변

이것은 원하는 것을 수행하며 거의 모든 경우에서 작동합니다.

>>> all(x in ['b', 'a', 'foo', 'bar'] for x in ['a', 'b'])
True

'a','b' in ['b', 'a', 'foo', 'bar']파이썬이 튜플로 해석하기 때문에 표현식 이 예상대로 작동하지 않습니다.

>>> 'a', 'b'
('a', 'b')
>>> 'a', 5 + 2
('a', 7)
>>> 'a', 'x' in 'xerxes'
('a', True)

다른 옵션

이 테스트를 실행하는 다른 방법이 있지만 여러 종류의 입력에 대해서는 작동하지 않습니다. 으로 Kabie는 지적, 당신은 세트를 사용하여이 문제를 해결할 수 …

>>> set(['a', 'b']).issubset(set(['a', 'b', 'foo', 'bar']))
True
>>> {'a', 'b'} <= {'a', 'b', 'foo', 'bar'}
True

…때때로:

>>> {'a', ['b']} <= {'a', ['b'], 'foo', 'bar'}
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

집합은 해시 가능한 요소로만 만들 수 있습니다. 그러나 생성기 표현식 all(x in container for x in items)은 거의 모든 컨테이너 유형을 처리 할 수 ​​있습니다. 유일한 요구 사항은 container다시 반복 할 수 있어야 한다는 것입니다 (즉, 생성기가 아님). items반복 가능할 수 있습니다.

>>> container = [['b'], 'a', 'foo', 'bar']
>>> items = (i for i in ('a', ['b']))
>>> all(x in [['b'], 'a', 'foo', 'bar'] for x in items)
True

속도 테스트

대부분의 경우 하위 집합 테스트는보다 빠르지 all만 집합이 옵션이 아니기 때문에 질문이 관련이없는 경우를 제외하고는 차이가 충격적이지 않습니다. 이와 같은 테스트 목적으로 목록을 집합으로 변환하는 것이 항상 문제의 가치가있는 것은 아닙니다. 그리고 발전기를 세트로 변환하는 것은 때로는 엄청나게 낭비가 될 수 있으며 프로그램 속도가 몇 배나 느려질 수 있습니다.

다음은 예시를위한 몇 가지 벤치 마크입니다. 둘 때 가장 큰 차이점은 제공 container하고 items상대적으로 작다. 이 경우 하위 집합 접근 방식은 약 10 배 더 빠릅니다.

>>> smallset = set(range(10))
>>> smallsubset = set(range(5))
>>> %timeit smallset >= smallsubset
110 ns ± 0.702 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
>>> %timeit all(x in smallset for x in smallsubset)
951 ns ± 11.5 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

이것은 큰 차이처럼 보입니다. 그러나 container세트 all가있는 한 훨씬 더 큰 규모로 완벽하게 사용할 수 있습니다.

>>> bigset = set(range(100000))
>>> bigsubset = set(range(50000))
>>> %timeit bigset >= bigsubset
1.14 ms ± 13.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
>>> %timeit all(x in bigset for x in bigsubset)
5.96 ms ± 37 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

하위 집합 테스트를 사용하는 것이 더 빠르지 만이 규모에서는 약 5 배 정도 밖에되지 않습니다. 속도 향상은 Python의 빠른 c지원 구현 으로 인한 set것이지만 기본 알고리즘은 두 경우 모두 동일합니다.

items다른 이유로 이미 목록에 저장된 경우 하위 집합 테스트 접근 방식을 사용하기 전에 집합으로 변환해야합니다. 그러면 속도가 약 2.5 배로 떨어집니다.

>>> %timeit bigset >= set(bigsubseq)
2.1 ms ± 49.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

당신이 만약 container시퀀스를하고, 먼저 변환해야 다음, 속도 향상은 더 작은입니다 :

>>> %timeit set(bigseq) >= set(bigsubseq)
4.36 ms ± 31.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

비참하게 느린 결과를 얻는 유일한 경우는 container시퀀스로 떠날 때입니다 .

>>> %timeit all(x in bigseq for x in bigsubseq)
184 ms ± 994 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

물론 우리는 꼭 필요한 경우에만 그렇게 할 것입니다. 의 모든 항목 bigseq이 해시 가능한 경우 대신 다음을 수행합니다.

>>> %timeit bigset = set(bigseq); all(x in bigset for x in bigsubseq)
7.24 ms ± 78 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

이는 대안 ( set(bigseq) >= set(bigsubseq), 4.36에서 시간 초과) 보다 1.66 배 더 빠릅니다 .

따라서 하위 집합 테스트는 일반적으로 더 빠르지 만 놀라운 차이는 아닙니다. 반면에 언제 all더 빠른지 살펴 보겠습니다 . 어떤 경우 items긴 십 만 값이며, 가능성에없는 값을 가질 수 있나요 container?

>>> %timeit hugeiter = (x * 10 for bss in [bigsubseq] * 2000 for x in bss); set(bigset) >= set(hugeiter)
13.1 s ± 167 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
>>> %timeit hugeiter = (x * 10 for bss in [bigsubseq] * 2000 for x in bss); all(x in bigset for x in hugeiter)
2.33 ms ± 65.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

이 경우 발전기를 세트로 변환하는 것은 엄청나게 낭비입니다. set생성자는 전체 발전기를 소비한다. 그러나의 단락 동작은 all생성기의 작은 부분 만 소비하면되므로 하위 집합 테스트보다 4 배 더 빠릅니다 .

이것은 확실히 극단적 인 예입니다. 그러나 이것이 보여 주듯이 모든 경우에 한 가지 접근 방식이 더 빠를 것이라고 가정 할 수는 없습니다.

업샷

대부분의 container경우 적어도 모든 요소가 해시 가능한 경우 세트 로 변환 하는 것이 가치가 있습니다. infor sets는 O (1)이고 infor sequence는 O (n) 이기 때문 입니다 .

반면에 하위 집합 테스트를 사용하는 것은 때때로 그만한 가치가 있습니다. 테스트 항목이 이미 세트에 저장되어 있으면 확실히 수행하십시오. 그렇지 않으면 all약간 느리며 추가 스토리지가 필요하지 않습니다. 또한 대규모 항목 생성기와 함께 사용할 수 있으며 때로는이 경우 엄청난 속도 향상을 제공합니다.


답변

이를 수행하는 또 다른 방법 :

>>> set(['a','b']).issubset( ['b','a','foo','bar'] )
True


답변

나는 확신 in보다 우선 순위 데 ,로 문이 해석되고, 그래서를 'a', ('b' in ['b' ...])다음으로 평가하는, 'a', True이후'b' 배열입니다.

원하는 작업을 수행하는 방법은 이전 답변을 참조하십시오.


답변

검사 할 경우 귀하의 의견 일치를 모두 ,

>>> all(x in ['b', 'a', 'foo', 'bar'] for x in ['a', 'b'])

검사 할 경우 적어도 하나의 일치에서 ,

>>> any(x in ['b', 'a', 'foo', 'bar'] for x in ['a', 'b'])


답변

Python 구문 분석기는 해당 문을 튜플로 평가했습니다. 여기서 첫 번째 값은 'a'이고 두 번째 값은 식 'b' in ['b', 'a', 'foo', 'bar'](로 평가됨 True)입니다.

하지만 간단한 함수를 작성하여 원하는 작업을 수행 할 수 있습니다.

def all_in(candidates, sequence):
    for element in candidates:
        if element not in sequence:
            return False
    return True

그리고 다음과 같이 부릅니다.

>>> all_in(('a', 'b'), ['b', 'a', 'foo', 'bar'])
True


답변

[x for x in ['a','b'] if x in ['b', 'a', 'foo', 'bar']]

이것이 선택한 대답보다 낫다고 생각하는 이유는 실제로 ‘all ()’함수를 호출 할 필요가 없기 때문입니다. 빈 목록은 IF 문에서 False로 평가되고 비어 있지 않은 목록은 True로 평가됩니다.

if [x for x in ['a','b'] if x in ['b', 'a', 'foo', 'bar']]:
    ...Do something...

예:

>>> [x for x in ['a','b'] if x in ['b', 'a', 'foo', 'bar']]
['a', 'b']
>>> [x for x in ['G','F'] if x in ['b', 'a', 'foo', 'bar']]
[]


답변

대괄호는 빼도 괜찮습니다.

array = ['b', 'a', 'foo', 'bar']
all([i in array for i in 'a', 'b'])