[python] 목록 이해 vs. 람다 + 필터

기본 필터링이 필요한 것을 발견했습니다. 목록이 있고 항목의 속성으로 필터링해야합니다.

내 코드는 다음과 같습니다.

my_list = [x for x in my_list if x.attribute == value]

그러나 나는 이것을 이렇게 쓰는 것이 낫지 않을 것이라고 생각했다.

my_list = filter(lambda x: x.attribute == value, my_list)

더 읽기 쉽고 성능을 위해 필요한 경우 람다를 꺼내어 무언가를 얻을 수 있습니다.

질문은 : 두 번째 방법을 사용할 때주의 사항이 있습니까? 성능 차이가 있습니까? Pythonic Way ™가 완전히 누락되어 다른 방법으로 (예 : 람다 대신 itemgetter 사용)해야합니까?



답변

사람들마다 아름다움이 얼마나 달라지는지는 이상합니다. 목록 이해력이 filter+ 보다 훨씬 명확 lambda하지만 더 쉬운 것을 사용하십시오.

사용 속도가 느려질 수있는 두 가지가 있습니다 filter.

첫 번째는 함수 호출 오버 헤드입니다. 파이썬 함수 ( def또는에 의해 생성됨)를 사용하자마자 lambda필터가 목록 이해보다 느려질 수 있습니다. 거의 확실하게 충분하지 않으며 코드 시간을 정하고 병목 현상이 생길 때까지 성능에 대해 많이 생각해서는 안되지만 차이점이 있습니다.

적용될 수있는 다른 오버 헤드는 람다가 범위 변수 ( value) 에 액세스해야한다는 것 입니다. 그것은 로컬 변수에 액세스하는 것보다 느리고 Python 2.x에서는 목록 이해가 로컬 변수에만 액세스합니다. Python 3.x를 사용하는 경우 목록 이해는 별도의 함수에서 실행되므로 value클로저를 통해 액세스 하므로이 차이는 적용되지 않습니다.

고려해야 할 다른 옵션은 목록 이해 대신 생성기를 사용하는 것입니다.

def filterbyvalue(seq, value):
   for el in seq:
       if el.attribute==value: yield el

그런 다음 메인 코드 (가독성이 중요한 곳)에서 목록 이해와 필터를 희망있는 의미있는 함수 이름으로 대체했습니다.


답변

이것은 파이썬에서 다소 종교적인 문제입니다. 비록 귀도는 제거 간주 map, filter그리고 reduce파이썬 3에서 , 결국 것만 반발의 충분했습니다 reduce에 내장 된 기능에서 이동 된 functools.reduce .

개인적으로 나는 목록 이해가 더 읽기 쉽다는 것을 안다. [i for i in list if i.attribute == value]모든 동작이 필터 함수가 아닌 표면에 있기 때문에 식에서 발생하는 일이 더 분명 합니다.

두 접근 방식의 성능 차이가 미미하기 때문에 두 가지 접근 방식의 성능 차이에 대해 너무 걱정하지 않아도됩니다. 응용 프로그램의 병목 현상이 아닌 것으로 판명되면 실제로 이것을 최적화합니다.

또한 BDFLfilter언어에서 벗어나기를 원했기 때문에 목록 이해를 자동으로 파이썬으로 만듭니다. 😉


답변

속도 차이는 아주 작기 때문에 필터 사용 여부 또는 목록 이해 여부는 취향에 달려 있습니다. 일반적으로 이해력을 사용하는 경향이 있지만 (여기서는 대부분의 다른 답변과 동의하는 것 같습니다) 선호하는 경우가 filter있습니다.

매우 빈번한 유스 케이스는 술어 P (x)에 따라 반복 가능한 X 값을 가져 오는 것입니다.

[x for x in X if P(x)]

그러나 때로는 일부 기능을 먼저 값에 적용하려고합니다.

[f(x) for x in X if P(f(x))]

구체적인 예로서,

primes_cubed = [x*x*x for x in range(1000) if prime(x)]

나는 이것을 사용하는 것보다 약간 더 좋아 보인다고 생각합니다 filter. 그러나 지금 고려

prime_cubes = [x*x*x for x in range(1000) if prime(x*x*x)]

이 경우 우리 filter는 사후 계산 값에 반대하고 싶습니다 . 큐브를 두 번 계산하는 문제 (더 비싼 계산을 상상) 외에도 식을 두 번 작성하여 DRY 미학을 위반하는 문제가 있습니다. 이 경우 나는 사용하기 쉽다

prime_cubes = filter(prime, [x*x*x for x in range(1000)])


답변

filter“빠른 방법”일 수도 있지만 “Pythonic 방법”은 성능이 절대적으로 중요하지 않은 경우 (이 경우 Python을 사용하지 않을 것입니다!) 그런 것들에 신경 쓰지 않을 것입니다.


답변

필자는 파이썬 3에서 filter ()가 실제로 반복자 객체라고 추가한다고 생각했기 때문에 필터링 된 목록을 작성하려면 list ()에 필터 메서드 호출을 전달해야합니다. 파이썬 2에서 :

lst_a = range(25) #arbitrary list
lst_b = [num for num in lst_a if num % 2 == 0]
lst_c = filter(lambda num: num % 2 == 0, lst_a)

리스트 b와 c는 같은 값을 가지며, filter ()가 [z의 경우 x의 y에 대해 x]와 같은 시간과 거의 같은 시간에 완료되었습니다. 그러나 3에서 동일한 코드는 필터링 된 목록이 아닌 필터 객체를 포함하는 목록 c를 남깁니다. 3에서 동일한 값을 생성하려면

lst_a = range(25) #arbitrary list
lst_b = [num for num in lst_a if num % 2 == 0]
lst_c = list(filter(lambda num: num %2 == 0, lst_a))

문제는 list ()가 인수로 iterable을 가져 와서 해당 인수에서 새 목록을 작성한다는 것입니다. 결과적으로 파이썬 3에서 이런 식으로 필터를 사용하면 원래 목록뿐만 아니라 filter ()의 출력을 반복해야하기 때문에 [x for x in y if z] 방법보다 최대 두 배가 걸립니다.


답변

중요한 차이점은 목록 이해가 list필터를 반환 하는 동안 을 반환한다는 것입니다 . 필터는을 반환 filter할 수 없습니다 list(예 :의 호출 len로 작동하지 않는 호출 filter).

내 자신의 학습으로 비슷한 문제가 발생했습니다.

즉, .NET에서 할 때와 같은 결과 list를 얻을 수있는 방법이 있다면 알고 싶습니다.filterlst.Where(i => i.something()).ToList()

편집 : 이것은 2가 아닌 Python 3의 경우입니다 (주석의 토론 참조).


답변

두 번째 방법이 더 읽기 쉽습니다. 의도를 정확히 알려줍니다. 목록을 필터링하십시오.
추신 : 변수 이름으로 ‘list’를 사용하지 마십시오