1. 함수를 매개변수로 하는 함수
def call_10_times(func):
for i in range(10):
func()
def func():
print("안녕")
call_10_times(func)
윗 def func()는 print_hello()로 변경하는게 네이밍 규칙에 따르면 올바른 방법이다.
def call_10_times(func):
for i in range(10):
func()
def print_hello():
print("안녕")
call_10_times(print_hello)
윗 코드를 보면 funcion print_hello()에서 괄호를 빼고 call_10_times(print_hello)라고 호출했다. 만일 괄호를 넣어서 호출하면 어떨까? 당연히 에러가 난다.
안녕
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[25], line 8
5 def print_hello():
6 print("안녕")
----> 8 call_10_times(print_hello())
Cell In[25], line 3, in call_10_times(func)
1 def call_10_times(func):
2 for i in range(10):
----> 3 func()
TypeError: 'NoneType' object is not callable
또한 ‘안녕’이라는 글자 호출 후 에러를 낸다. 이유는 def print_hello()가 먼저 한 번 호출되어서 그 결과를 찍었기 때문이다.
이러한 개념은 콜백함수와 연결된다. 콜백은 내가 함수를 연결해 놓을 테니, 나중에 호출하면 실행해줘라는 뜻이다. 그래서 cv2.setMouseCallback(window, on_mouse)와 같이 콜백함수를 연결한 모습을 종종 보았던 것이다.
2. map, filter 함수
콜백함수는 나중에 한 번 더 다루기로 하고, 이제는 파이썬 내장함수인 map, filter 함수에 대해서 알아보겠다. 일단, 아래의 포스팅을 한 번 읽어보자.
윗 포스팅에서 제곱 구하기 함수를 정의한 바 있다. 이건 어떤 함수를 매핑(mapping)한 것과 같은 의미이다. 그래서 이런 것을 구할 때 명시적으로 매핑한다는 것을 강조하면서 표준화된 방법으로 찾아보자라고 해서 나온것이 map 내장 함수이다.
일단 개념만 가지고 짜면 아래처럼 된다.
def my_power(list_x):
return [i*i for i in range list_x]
list_a=[1,2,3,4,5,6]
map(my_power, list_a)
--------------------------------
<map object at 0x7f85106c9780>
얼추 맞는것 같다. list()가 필요한것 같다.
def my_power(list_x):
return [i*i for i in list_x]
list_a=[1,2,3,4,5,6]
print(list(map(my_power, list_a)))
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[10], line 5
2 return [i*i for i in list_x]
4 list_a=[1,2,3,4,5,6]
----> 5 print(list(map(my_power, list_a)))
Cell In[10], line 2, in my_power(list_x)
1 def my_power(list_x):
----> 2 return [i*i for i in list_x]
TypeError: 'int' object is not iterable
음… map에 대해서 공부를 해야 겠다.
class map(
func: (_T1@__new__) -> _S@map, #func를 인자로 받고
iterable: Iterable[_T1@__new__], #이터러블이 여기 들어와야 된다.
/
)
map(func, *iterables) --> map object #반환은 map object
Make an iterator that computes the function using arguments from
each of the iterables. Stops when the shortest iterable is exhausted.
#"각 반복 가능 객체(iterables)로부터 인자들을 가져와 함수를 계산하는 이터레이터를 만듭니다. 인자로 전달된 객체 중 가장 짧은 객체가 소진되면 동작을 멈춥니다."
map 함수는 이터러블을 직접 하나씩 풀어서 계산하므로 리스트 컴프리핸션이 필요 없었다. 그럼 코드를 다시 짜 보자.
def my_power(x): # 리스트가 아니라 숫자 하나(x)를 받음
return x * x
list_a = [1, 2, 3, 4, 5, 6]
# list_a의 각 요소를 my_power에 하나씩 적용하고 리스트로 변환
result = list(map(my_power, list_a))
print(result) # [1, 4, 9, 16, 25, 36]
함수의 정의가 C/C++ 처럼 명료하다. 또한 함수의 사상(mapping)이라는 의미를 가진, map 함수의 기능도 이해가 되었다.단, 마지막에 list()로 한 번 더 처리해 주어야 map 반환값이 리스트로 변경되는 것에 주의한다.
map 함수를 이해했다면 filter 함수도 쉽게 이해할 수 있다.
result에서 10 이하의 값만 뽑아보자.
def my_power(x): # 리스트가 아니라 숫자 하나(x)를 받음
return x * x
def under_10(x):
return([x if x < 10])
list_a = [1, 2, 3, 4, 5, 6]
# list_a의 각 요소를 my_power에 하나씩 적용하고 리스트로 변환
result = list(map(my_power, list_a))
filtered_result = list(filter(under_10, result))
print(result)
print(filtered_result)
---------------------------------------------------------
Cell In[13], line 5
return([x if x < 10])
^
SyntaxError: expected 'else' after 'if' expression
쉽지 않다. 리스트 컴프리핸션까지 했는데… else는 왜 넣으란 거지?
filter 함수 사용법을 읽어 보니 이넘도 각각의 요소에 대해서 조건을 테스트 후, true면 그 요소를 filtered object로 저장하는 함수다. 요렇게 짜면 된다.
def my_power(x): # 리스트가 아니라 숫자 하나(x)를 받음
return x * x
def under_10(x):
return(x < 10)
list_a = [1, 2, 3, 4, 5, 6]
# list_a의 각 요소를 my_power에 하나씩 적용, 리스트로 변환
result = list(map(my_power, list_a))
# result의 각 요소를 테스트해서 true인 것 만 리스트로 변환
filtered_result = list(filter(under_10, result))
print(result)
print(filtered_result)
여기까지 했으면, map과 filter 함수에 대해서는 감이 왔을 것 같다.
3. 람다함수(lambda)
이제는 람다함수에 대해서 알아보자. 위에서 def로 2 개의 함수를 정의했다. 이것을 이렇게 나타내 보겠다.
my_power = lambda x: x*x under_10 = lambda x: x<10 list_a = [1, 2, 3, 4, 5, 6] # list_a의 각 요소를 my_power에 하나씩 적용, 리스트로 변환 result = list(map(my_power, list_a)) # result의 각 요소를 테스트해서 true인 것 만 리스트로 변환 filtered_result = list(filter(under_10, result)) print(result) print(filtered_result)
윗 코드는 동작하는 코드다. 그리고 위에서 함수 정의부를 각 라인으로 이동시키면 아랫처럼 쓸 수 있다.
list_a = [1, 2, 3, 4, 5, 6] # list_a의 각 요소를 my_power에 하나씩 적용, 리스트로 변환 result = list(map(lambda x: x*x, list_a)) # result의 각 요소를 테스트해서 true인 것 만 리스트로 변환 filtered_result = list(filter(lambda x: x<10, result)) print(result) print(filtered_result)
이렇게 하면 함수의 원형을 찾으로 위로 올라가서 확인할 필요가 없으며 함수명에 대해서 고심할 필요가 없게 된다. 이제 lambda라는 코드를 보면 함수의 원형이 머릿속에 그려지는가?
lambda x : # 함수의 매개변수 x : x*x # 연산를 return : x<10 # 연산을 return
참조: 물론, map과 filter를 안쓰고도 훌륭한 코드를 만들 수 있다.
list_a = [1, 2, 3, 4, 5, 6] #펑션에서 호출하는게 아니므로 리스트 컴프리핸션 위에 위치 filtered_result = [i*i for i in list_a if i*i < 10] print(filtered_result)