본문 바로가기
Python/Python Advanced

10. Python - Lamdba의 활용

by Rosmary 2024. 2. 19.
728x90
반응형

 

 

 

 

 

되도 않는 망상에 빠져 Python으로  "이런 기능 한 번 만들어봐야지" 하면서 지낸 것이 벌써 5년이 되었다. 물론 필자 나름대로 아직까지 잘 쓰고 있는 프로그램도 있긴 하지만 99%가 업무로 바빠서(핑계가 아니다. 진짜...) 엎어진 것들이 많다.

그런데, 최근 어떠한 이유로 갑자기 옛 코드들을 들여다보다가, 공통적으로 개선이 필요한 사항이 필자의 눈에 많이 띄인다. 오늘 다루어 볼 내용은 지금까지 필자의 능력 부족으로 잘 사용하지 못했던 Lambda 함수다.

 

 

1. Lambda 식

 

아래와 같은 상황을 가정해보자. List형 데이터 변수가 있고, 여기에는 5명의 학생의 '국어', '영어', '수학', '프로그래밍' 점수가 Tuple 형태로 저장되어 있다고 가정해보자. 아래와 같은 형태로 나타날 것이다.

 

 

 

이 자료로부터 각 학생의 과목별 과락 여부를 확인한다고 가정해보자. 각 학생의 개별 과목의 합불 여부는 List에 저장된다. 필자는 조금 빡빡하게 70점을 과락 기준으로 정하려 하며, 70 미만은 불합격, 70 이상은 합격으로 정의하려한다. 이 코드를 python for 문으로 진행해보자.

 

 

 

위 코드의 실행 결과는 아래와 같다. 

 

 

 

결과는 잘 나왔는데... 작성한 코드를 보면 for 문이 중첩되어 있어서 가독성이 좋지는 않다. 지금이야 간단한 예시를 들어 코드를 작성했기에 크게 문제가 되지 않지만, 조금 더 복잡한 기능이 들어가는 경우, 추후에 저 코드를 어떻게 작성했는지 기억도 가물가물한 상태라면 코드의 유지보수가 매우 불편해진다. 

 

함수를 작성하면 그나마 괜찮을까?

 

아니... 안괜찮다.

 

여전히 보기 어렵다. 가독성의 문제는 for 문의 중첩이기 때문에 함수를 적용한다고 문제가 해결되지 않는다. 물론, 조금 복잡한 코드라면 코드의 동작 절차를 함수를 통해 조금 더 빠르게 타고 갈 수 있겠지만 어차피 for 문 지옥에서 머리를 싸매게 될 것이다. 

 

물론 이는 python 뿐만 아니라 대부분의 프로그래밍 언어가 가지고 있는 문제다. for나 while과 같은 Loop가 없는 언어는 드물고, 방대한 데이터에서 원하는 데이터만 추출하려면 Loop 문의 중첩은 필연적이다. 이 때문에 나타난 것이 Lambda다.

 

Lambda 식은 복잡하게 나타날 수 있는 코드를 단순화하는데 많이 사용한다. Python에서의 lambda 식은 보통 아래의 형태로 나타난다. 

 

variables_for_lambda = lambda parameter1, parameter2... : 반환할 연산 결과

 

각 학생의 과목별 합불 여부를 위의 lambda 구조식을 사용하여 다시 작성하면 아래와 같다.

 

 

list_score의 for 문도 같이 lambda로 추가할 수 있을 듯 한데, 이건 따로 진행해보려한다.

 

 

코드가 32줄에서 17줄로 절반 가량 줄었지만 결과는 동일하게 나타남을 확인할 수 있다. loop의 중첩이나 함수 작성이 굳이 필요하지 않는 경우라면 코드 가독성을 고려했을 때 Lambda는 괜찮은 선택이다. 

 

물론 lambda의 활용이 쉽지는 않은데, 람다 사용을  위해 삼항 연산자 등 Python으로 코드를 간결하게 작성할 수 있는 문법에 어느정도 익숙해져야하기 때문이다. 또한 람다식의 경우, 일반적인 함수를 호출하는 방식과 달리, 내부 함수에서 사용할 지역 변수를 지정할 수도 없다는 단점이 있다. 이 때문에 내부에서 별도의 변수를 활용해야하는 연산이 필요한 경우, def 문으로 함수를 만들어 사용하는 것이 훨씬 (머리가 덜 아프고) 좋은 방식이다.

 

 

 

2. Lambda 식의 활용: map()과 functools.reduce() 함수

 

얼핏 보면, Lambda 식은 활용도가 크지는 않아보인다. 일회성의 아주 간단한 함수를 만들어서 동작시키는 경우가 아니라면 굳이 코드를 한 줄로 짜기 위해 머리를 싸매면서 Lambda를 쓰기보다 바로 일반 함수를 만드는 것이 속 편하기 때문이다. 그럼, Lambda는 언제 사용하면 좋을까? 

 

Lambda는 간단한 함수를 만들 수 있다. 따라서 함수의 인자(parameter)가 함수형(func)의 자료를 받을 수 있는 경우라면 활용도가 조금 더 높아진다. 그 예로 map() 함수와 functools.reduce() 함수가 있다.

 

map() 함수는 두 개의 인자를 받는다. 첫 번 째로는 함수를, 두 번째로는 List 등 Iterator 타입의 자료를 받는다. 받은 인자들로 수행하는 일은 단순한데, Iterator 타입 내 원소들을 하나씩 호출하여 함수를 실행한 뒤, map 타입으로 저장하는 것이다. 예를 들어, 열 명의 사람의 나이가 List에 저장되어 있고, 징병을 위해 20세 이상 30세 미만의 사람을 선별해야한다고 가정해보자. 일반 함수를 사용한다면 아래와 같은 형태가 된다.

 

 

우선 저 for문이 영 눈에 거슬리니 저것부터 map() 함수로 없애보자. map() 을 적용하면 아래와 같이 코드를 변경할 수 있게된다.

 

 

map은 인자로 들어온 Iterator의 원소를 하나씩 꺼내어 지정된 함수의 인자로 사용한다고 언급했다. 따라서 map의 인자로 사용할 check() 함수 역시 for 문을 제외하고 단일 원소에 대한 연산을 진행할 수 있도록 만들어 줄 수 있다. 이제 변경한 함수를 map() 함수에 적용하고 돌려보면 코드 변경 전과 동일하게 결과가 나타남을 확인할 수 있다 (동일한 화면이니 굳이 스크린 샷 안올릴거다..)

 

마지막으로 여기에 lambda를 적용해보자. 필자가 수정한 check() 함수는 현재 인자를 하나만 받고 있고, 별 다른 지역 변수를 사용하지 않기 때문에 lambda 식을 적용하기가 매우 수월하다. 

 

 

동일한 결과임에도 코드 라인이 확연히 줄어든데다가, 어떤 의도로 코드를 작성했는지도 알아보기가 훨씬 쉬워졌다. 이제 다른 예시를 들어보자. 

 

이번에 사용할 예시는 functools라는 python 내장 모듈에 존재하는 reduce()다. reduce() 함수는 map() 함수처럼 Iterator 자료형을 받으나, map() 함수와 달리 두 개의 인자를 받는 함수를 인자로 삼는다. 이 말인 즉, Iterator의 원소들을 하나로 합치거나, 합산을 해야하는 경우 많이 사용한다.

 

필자가 출퇴근하면서 사용한 비용을 dictation 형태로 정리한 자료로부터, 하루에 사용한 금액을 계산하는 코드를 작성해보려한다. reduce()나 lambda를 사용하지 않는다면 아래와 같이 코드를 작성하게 될 것이다.

 

한 때 출퇴근거리가 너무 멀어, 출퇴근 비용이 어마무시하게 나왔었다.

 

이제 reduce() 함수를 적용해보자.

 

 

reduce() 함수 역시 Iterator의 원소를 하나씩 추출하여 함수 연산에 참여시키는 것은 map()과 동일하지만, 그 결과를 별도로 저장하고 다음 원소와의 연산에 사용한다는 것이 map()과 다른 점이다. 이제 여기에 lambda를 적용해보자.

 

26줄짜리 코드가 16줄로 줄어들었다.

 

이번에는 조금 다른 연산을 reduce로 진행해보려한다. 방금 전에는 금액의 합산을 위해 reduce() 함수를 사용했지만 이번에는 필자가 돈을 사용한 내역에 대해 List 형태로 담아보려 한다. 바로 lambda 식을 적용하면 아래와 같이 나타낼 수 있다.

 

 

 


 

 

END.

 

반응형

댓글