Story07 map & filter

유동헌·2021년 9월 29일
0

열혈파이썬_중급

목록 보기
6/8

map

이번에 소개하는 기본 함수인 map은 예저를 통해서 이해하는게 빠르다.

>>> def pow(n):
...     return n ** 2
... 
>>> st1 = [1,2,3]
>>> st2 = [pow(st1[0]), pow(st1[1]), pow(st1[2])]
>>> st2
[1, 4, 9]

map 함수 사용

>>> def pow(n):
...     return n ** 2
... 
>>> st1 = [1,2,3]
>>> st1 = list(map(pow, st1))
>>> st1
[1, 4, 9]

map 함수는 st1에 저장된 값들을 하나씩 전달하면서 pow 함수를 호출해준다. 즉 st1에 저장된 값의 수만큼 pow 함수가 호출되며 그것이 map 함수가 되는 것이다. 단 이때 map의 두 번째 전달 대상은 iterable 객체여야 한다.

그럼 이때 pow 함수가 반환하는 값들을 어떻게 얻을 수 있을까? map은 객체를 하나 반환하는데 그 객체는 iterable 객체이다. 따라서 다음과 같은 방식으로 그 값들을 얻을 수 있다.

>>> def pow(n):
...     return n ** 2
... 
>>> st = [1,2,3]
>>> ir = map(pow, st)
>>> for i in ir:
...     print(i, end = ' ')
... 
1 4 9 >>>

그런데 이번 스토리에서 처음 언급한 목표는 반환되는 값으로 이뤄진 리스트를 만드는 것이었으므로 앞서 보인 예제에서는 다음과 같이 문장을 작성하였다.

st2 = list(map(pow, st1)) # 리스트 함수에 iterator 객체 전달!

그리고 지금 보인 예에서는 map에 리스트를 전달했지만 다음에서 보이듯이 튜플도 전달 가능하고 문자열도 전달이 가능하다. 즉 iterable 객체이면 무엇이든 전달 가능하다.

>>> def test(e):
...     return e * 2
... 
>>> list(map(test, (1, 3, 4)))
[2, 6, 8]
>>> list(map(test, 'hello'))
['hh', 'ee', 'll', 'll', 'oo']

map 심화

>>> def sum(n1, n2):
...     return n1 + n2
... 
>>> st1 = [1,2,3]
>>> st2 = [3,2,1]

목적은 이렇다. st1, st2에 있는 값들을 각각 더해서 다음 형태의 리스트를 만들고자 한다.

>>> st3 = list(map(sum, st1, st2))
>>> st3
[4, 4, 4]

위에서는 map에 sum을 전달했다. 그런데 sum은 매개변수가 둘이므로 map은 두 개의 iterable 객체를 요구한다. 그래서 다음과 같이 총 세 개의 객체가 map에 전달되었다.

map과 람다

먼저 보편적인 그리고 슬라이싱 연산의 예

>>> st3
[4, 4, 4]
>>> st = [1,2,3,4,5,6,7,8]
>>> st[:]
[1, 2, 3, 4, 5, 6, 7, 8]
>>> st[: :1] # 처음부터 끝까지 한 칸씩 뛰면서 값을 꺼내 리스트 생성
[1, 2, 3, 4, 5, 6, 7, 8]
>>> st[: :2]
[1, 3, 5, 7]
>>> st[: :3]
[1, 4, 7]

>>> st[: :-1]
[8, 7, 6, 5, 4, 3, 2, 1]

>>> s[: :-1]
'olleh'

>>> st = ['one', 'two', 'three']
>>> rst = list(map(rev, st))
>>> rst
['on', 'tw', 'thre']
>>> def rev1(s):
...     return s[: :-1]
... 
>>> res1 = list(map(rev1, st))
>>> res1
['eno', 'owt', 'eerht']

람다식 코드

>>> st = ['one', 'two', 'three']
>>> rst = list(map(lambda s : s[: :-1], st))
>>> rst
['eno', 'owt', 'eerht']

filter

이번에 소개하는 filter 함수도 map과 마찬가지로 함수를 인자로 전달받는다. 그리고 이 함수는 값을 걸러내는 기준이 된다. 즉 filter 함수는 값을 걸러내는 기능을 제공하는 함수이다. 그럼 먼저 간단한 예를 보이겠다.

>>> def is_odd(n):
...     return n % 2
... 
>>> st = [1,2,3,4,5,6]
>>> ost = list(filter(is_odd, st))
>>> ost
[1, 3, 5]

위 예제의 다음 문장에서 filter 함수의 호출 부분을 보자.

ost = list(filter(is_odd, st))
# is_odd를 기준으로 st에 저장된 값들 중 일부 걸러 냄

위의 문장에서 보이듯이 filter 함수의 첫 번째 인자는 다음과 같아야 한다.

  • True, False를 반환하는 함수
  • 리스트나 튜플과 같이 값을 저장하고 있는 iterable 객체

즉 위 예제에서 filter는 st에 저장된 값들을 하나씩 꺼내서 is_odd에 전달한다. 그리고 is_odd가 True 함수를 반환하는 값들만 따로 모은다. 그리고 이 값들을 얻을 수 있는 iterable 객체를 반환한다. 그럼 이번에는 위 예제를 람다식을 기반으로 다시 작성해보겠다.

>>> st = [1,2,3,4,5,6,7,8]
>>> ost = list(filter(lambda n : n % 2, st))
>>> ost
[1, 3, 5, 7]

이번에는 10 이하의 자연수 중에서 3의 배수만 리스트에 담는 예를 filter와 람다식을 기반으로 작성해보겠다.

>>> st = list(range(1, 11))
>>> ost = list(filter(lambda n : n % 3, st))
>>> ost
[1, 2, 4, 5, 7, 8, 10]
>>> ost = list(filter(lambda n : not(n % 3), st))
>>> ost
[3, 6, 9]

사실 위의 예에서 st는 다음과 같이 선언해도 결과는 같다. range 객체도 iterable 객체이므로 filter의 두 번째 인자가 될 수 있기 때문이다.

이번에는 map과 filter를 다 사용하는 예를 만들어 보겠다. 다음과 1 ~ 10의 제곱을 리스트에 담되 3의 배수만 담은 예이다. 1 ~ 10 의 제곱을 만들기 위해서 map이 사용되고, 이 중에서 3의 배수만을 담기 위해 filter가 사용되었다.

>>> st = range(1, 11)
>>> fst = list(filter(lambda n : not(n%3), map(lambda n : n ** 2, st)))
>>> fst
[9, 36, 81]
map(lambda n : n ** 2, st) # 이 부분이 가장 먼저 호출된다. 

이를 통해서 리스트 st에 저장된 값들의 제곱을 해당하는 다음 값들이 만들어진다.

그리고 위의 값들을 하나씩 얻을 수 있는 iterator 객체가 반환되어 다음의 filter 함수 호출이 진행된다. (map이 반환한 iterator 객체를 아래에서 ir1이라 하였다)

fst = list(filter(lambda n : not(n%2), ir1))

그리고 위의 filter 함수를 통해서 3의 배수만 통과되어 다음 값들만 남게 된다.

9, 36, 81

마지막으로 위의 값들을 하나씩 얻을 수 있는 iterator 객체가 filter 함수 호출의 결과로 반환되어 다음의 상태가 된다. (filter가 반환한 iterator 객체를 아래에서 ir2라 하였다)

fst = list(ir2)
profile
지뢰찾기 개발자

0개의 댓글