파이썬의 펑션을 주제로 벌써 네번째 글을 쓰고 있다. 그만큼 파이썬만이 가지고 있는 재밌고 유용한 기능들이 많다는 의미가 아닐까 생각하며, 이번 글을 끝으로 펑션에 관한 내용이 마무리 되길 기원하는 마음으로 글을 써보겠다.
Lambda Function
프로그래밍에 경험이 있다면, 람다 펑션에 대해 한번쯤은 들어본 경험이 있을 것이다. 왜냐면, 생각보다 많은 프로그래밍 언어에서 람다 펑션을 지원하기 때문이다. 일명, 익명 함수라고도 불리우는 람다 펑션은 콜스택 추적을 어렵게 만들고, 남발할 경우 코드의 가독성을 해칠 수 있다는 분명한 단점도 있지만, 그만큼 프로그램 코드를 간결하게 만들 수 있다는 장점 덕분에 많이 활용되고 있다.
람다 펑션에 관한 내용을 이해하기 위해 위 그림의 예를 한번 살펴보자. 4번 라인에서는 fcGetAdd라고 정의된 펑션을 호출했을 때 반환되는 결과를 print 함수를 통해 출력하고 있고, 5번 라인에서는 람다 펑션을 이용한 결과를 출력하고 있다. 이것으로, 람다 펑션은 def이라는 키워드 없이 펑션을 정의하는 방식임을 알 수 있다.
지금의 예로 람다 펑션이 어떤 것인지는 어렴풋이나마 알 수 있지만, 이것만으로 람다 펑션을 적용했을 때 얻을 수 있는 이점까지 알기는 어렵다.
클로저에 관한 내용을 다룬 지난 글에서, 펑션 자체를 반환하는 것이 가능하다는 것을 알게 되었다. 이것과 반대로, 펑션 자체를 매개변수로 전달하는 것도 가능하다.
위 그림에서 정의되어 있는 fcGetResult는 3개의 매개변수 중에서 가장 앞에 위치한 func를 통해 펑션을 전달 받고, 남은 두 매개변수를 이용해 내부에서 펑션을 호출하는 코드를 가지고 있다. 그리고, 4번과 5번 라인의 코드에서는 람다 펑션을 이용해서 각기 다른 펑션을 정의하여 fcGetResult 펑션에 전달하고 있다.
이렇게 람다 펑션을 이용하면, 단일 코드로 펑션을 바로 정의해서 바로 사용할 수 있다.
Generator
파이썬에서는 FOR 반복을 사용할 때, 다른 프로그래밍 언어와 달리 리스트나 튜플과 같은 순환 가능 개체를 사용하였다. 이러한 순환 가능 개체를 Iterator라고 한다.
위 그림과 같이 iter() 함수를 사용하면 이터레이터를 얻을 수 있다.
그리고, 이터레이터를 이용해서 FOR 반복문도 실행할 수 있다.
그렇다면, 이쯤에서 의문이 생길 수 밖에 없는 것이... 지금까지 FOR 반복의 개체로, 4번 라인의 이터레이터 객체 대신 9번 라인처럼 리스트 타입의 데이터를 직접 사용했지만, 파이썬 인터프리터는 아무런 에러 없이 동작했다는 점이다.
이 부분에 대해 좀 더 자세히 설명하자면, FOR 반복문의 in 키워드 뒤로 리스트나 튜플이 연결되면, 파이썬 인터프리터는 이터레이터 객체를 먼저 구한다. 그리고, 이터레이터의 내부 원소를 하나씩 가져와서 반복을 진행한다.
이터레이터 객체와 next 함수를 이용하면 FOR 반복문에서 원소를 하나씩 가져오는 것과 비슷한 작업을 만들어낼 수 있다. 단, 더 이상 반환할 원소가 없는 상황에서 next 함수를 사용하면, 그림과 같이 StopIteration 예외 처리된다.
정리하자면, FOR 반복에서 활용되는 리스트나 튜플도 결국 생략되었지만 이터레이터 객체로 전환되어 반복에 적용되었다고 할 수 있다.
그렇다면, 이쯤에서 리스트나 튜플과 같은 데이터에서 전환된 이터레이터 객체 대신, 여러 조건들이 버무러진 별도의 이터레이터 객체를 만들 수는 없을까 하는 장난끼 섞인 의문을 마주할 수 있다.
물론, 앞에서 잠깐 언급된 iter() 함수를 이용해서 생성할 수도 있지만, 제네레이터 펑션이 이 질문에 적절한 대답이 될 수 있다.
제네레이터 펑션도 다른 펑션과 마찬가지로, def 키워드와 함께 정의된다. 단, 호출한 위치로 데이터를 전달할 때 return 대신 yield를 사용한다. 펑션을 호출한 위치로 데이터를 반환할 때, '양보'라는 의미의 yield가 사용된다는 점을 주목하자. 그리고, 이터레이터에서와 마찬가지로, next 함수를 통해 내부의 원소를 하나씩 꺼내서 사용할 수 있다.
예제 프로그램의 실행 과정을 정리하면 위 그림처럼 간단하게 도식화 할 수 있다. 일반적인 펑션에서는 return에 의해 데이터를 반환함과 동시에 펑션 내부의 실행도 끝이 났었다. 그러나, 제네레이터에서 사용하는 yield는 next 함수를 만났을 때 데이터를 반환하고, 펑션 외부에서 다른 next 함수가 나타날 때까지 펑션 내부의 상태를 유지한채로 대기하는 특성을 가지고 있다.
이러한 결과에 대해서, 펑션을 호출하는 과정에서 이미 내부 원소들이 반환되었고, next 함수에 의해 원소들을 차례대로 꺼내오는 것이 아니냐고도 생각할 수 있는데, 파이썬의 인터프리터는 코드 라인을 한 줄씩 실행한다는 사실을 상기하면서 위 그림을 보면, 앞에서의 설명을 충분히 납득할 수 있을 것이다.
'Programming > Python' 카테고리의 다른 글
Python: Try & Exception (0) | 2021.01.22 |
---|---|
Python: Function (5) (0) | 2021.01.20 |
Python: Function (3) (0) | 2021.01.17 |
Python: Function (2) (0) | 2021.01.15 |
Python: Function (1) (0) | 2021.01.12 |