programing

목록 이해력 대 람다 + 필터

nasanasas 2020. 9. 29. 07:53
반응형

목록 이해력 대 람다 + 필터


기본 필터링이 필요하다는 것을 알게되었습니다. 목록이 있고 항목의 속성으로 필터링해야합니다.

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

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.

첫 번째는 함수 호출 오버 헤드입니다. Python 함수를 사용하는 즉시 ( 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 이 언어에서 사라지 기를 원했기 때문에 목록 이해를 자동으로 더 Pythonic하게 만듭니다 ;-)


속도 차이는 미미하기 때문에 필터를 사용할 것인지 목록 이해를 사용할 것인지는 취향에 달려 있습니다. 일반적으로 나는 이해력을 사용하는 경향이 있지만 (여기에서 대부분의 다른 답변과 일치하는 것처럼 보입니다), 내가 선호하는 경우가 하나 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"빠른 방법" 이긴하지만 성능이 절대적으로 중요하지 않은 경우 (이 경우 Python을 사용하지 않을 것입니다!) "Pythonic 방법"은 그런 것들을 신경 쓰지 않습니다.


파이썬 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)

lists b and c have the same values, and were completed in about the same time as filter() was equivalent [x for x in y if z]. However, in 3, this same code would leave list c containing a filter object, not a filtered list. To produce the same values in 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))

The problem is that list() takes an iterable as it's argument, and creates a new list from that argument. The result is that using filter in this way in python 3 takes up to twice as long as the [x for x in y if z] method because you have to iterate over the output from filter() as well as the original list.


An important difference is that list comprehension will return a list while the filter returns a filter, which you cannot manipulate like a list (ie: call len on it, which does not work with the return of filter).

My own self-learning brought me to some similar issue.

That being said, if there is a way to have the resulting list from a filter, a bit like you would do in .NET when you do lst.Where(i => i.something()).ToList(), I am curious to know it.

EDIT: This is the case for Python 3, not 2 (see discussion in comments).


I find the second way more readable. It tells you exactly what the intention is: filter the list.
PS: do not use 'list' as a variable name


Filter is just that. It filters out the elements of a list. You can see the definition mentions the same(in the official docs link I mentioned before). Whereas, list comprehension is something that produces a new list after acting upon something on the previous list.(Both filter and list comprehension creates new list and not perform operation in place of the older list. A new list here is something like a list with, say, an entirely new data type. Like converting integers to string ,etc)

In your example, it is better to use filter than list comprehension, as per the definition. However, if you want, say other_attribute from the list elements, in your example is to be retrieved as a new list, then you can use list comprehension.

return [item.other_attribute for item in my_list if item.attribute==value]

This is how I actually remember about filter and list comprehension. Remove a few things within a list and keep the other elements intact, use filter. Use some logic on your own at the elements and create a watered down list suitable for some purpose, use list comprehension.


generally filter is slightly faster if using a builtin function.

I would expect the list comprehension to be slightly faster in your case


In addition to the accepted answer, there is a corner case when you should use filter instead of a list comprehension. If the list is unhashable you cannot directly process it with a list comprehension. A real world example is if you use pyodbc to read results from a database. The fetchAll() results from cursor is an unhashable list. In this situation, to directly manipulating on the returned results, filter should be used:

cursor.execute("SELECT * FROM TABLE1;")
data_from_db = cursor.fetchall()
processed_data = filter(lambda s: 'abc' in s.field1 or s.StartTime >= start_date_time, data_from_db) 

If you use list comprehension here you will get the error:

TypeError: unhashable type: 'list'


Here's a short piece I use when I need to filter on something after the list comprehension. Just a combination of filter, lambda, and lists (otherwise known as the loyalty of a cat and the cleanliness of a dog).

In this case I'm reading a file, stripping out blank lines, commented out lines, and anything after a comment on a line:

# Throw out blank lines and comments
with open('file.txt', 'r') as lines:        
    # From the inside out:
    #    [s.partition('#')[0].strip() for s in lines]... Throws out comments
    #   filter(lambda x: x!= '', [s.part... Filters out blank lines
    #  y for y in filter... Converts filter object to list
    file_contents = [y for y in filter(lambda x: x != '', [s.partition('#')[0].strip() for s in lines])]

It took me some time to get familiarized with the higher order functions filter and map. So i got used to them and i actually liked filter as it was explicit that it filters by keeping whatever is truthy and I've felt cool that I knew some functional programming terms.

Then I read this passage (Fluent Python Book):

The map and filter functions are still builtins in Python 3, but since the introduction of list comprehensions and generator ex‐ pressions, they are not as important. A listcomp or a genexp does the job of map and filter combined, but is more readable.

And now I think, why bother with the concept of filter / map if you can achieve it with already widely spread idioms like list comprehensions. Furthermore maps and filters are kind of functions. In this case I prefer using Anonymous functions lambdas.

Finally, just for the sake of having it tested, I've timed both methods (map and listComp) and I didn't see any relevant speed difference that would justify making arguments about it.

from timeit import Timer

timeMap = Timer(lambda: list(map(lambda x: x*x, range(10**7))))
print(timeMap.timeit(number=100))

timeListComp = Timer(lambda:[(lambda x: x*x) for x in range(10**7)])
print(timeListComp.timeit(number=100))

#Map:                 166.95695265199174
#List Comprehension   177.97208347299602

Curiously on Python 3, I see filter performing faster than list comprehensions.

I always thought that the list comprehensions would be more performant. Something like: [name for name in brand_names_db if name is not None] The bytecode generated is a bit better.

>>> def f1(seq):
...     return list(filter(None, seq))
>>> def f2(seq):
...     return [i for i in seq if i is not None]
>>> disassemble(f1.__code__)
2         0 LOAD_GLOBAL              0 (list)
          2 LOAD_GLOBAL              1 (filter)
          4 LOAD_CONST               0 (None)
          6 LOAD_FAST                0 (seq)
          8 CALL_FUNCTION            2
         10 CALL_FUNCTION            1
         12 RETURN_VALUE
>>> disassemble(f2.__code__)
2           0 LOAD_CONST               1 (<code object <listcomp> at 0x10cfcaa50, file "<stdin>", line 2>)
          2 LOAD_CONST               2 ('f2.<locals>.<listcomp>')
          4 MAKE_FUNCTION            0
          6 LOAD_FAST                0 (seq)
          8 GET_ITER
         10 CALL_FUNCTION            1
         12 RETURN_VALUE

But they are actually slower:

   >>> timeit(stmt="f1(range(1000))", setup="from __main__ import f1,f2")
   21.177661532000116
   >>> timeit(stmt="f2(range(1000))", setup="from __main__ import f1,f2")
   42.233950221000214

My take

def filter_list(list, key, value, limit=None):
    return [i for i in list if i[key] == value][:limit]

참고URL : https://stackoverflow.com/questions/3013449/list-comprehension-vs-lambda-filter

반응형