본문 바로가기

진리는어디에/Python

[Python] 함수 #7 람다(lambda)

이번 포스트에서는 익명의 함수를 만드는 람다 표현식에 대해 살펴 보도록 하겠다.

목차

  1. 함수 소개
  2. 디폴트 파라미터 주의 사항
  3. 파라미터 언패킹
  4. 파라미터 패킹
  5. 함수 객체
  6. 일급 객체(first class object)
  7. >> 람다(lambda)

람다 표현식이란?

람다란, 프로그래밍 언어에서 사용되는 개념으로 익명의 함수, 이름 없는 함수를 지칭하는 용어다.

익명함수란 말 그대로 함수의 이름이 없는 함수다. 보통 파이썬에서 def 키워드를 사용하여 함수를 선언하고 기능을 정의 하는 것과는 달리, 람다는 함수를 하나의 식으로 정의한다.

수학적으로 함수를 단순하게 표현하는 방법을 '람다'라고 하고, 함수를 하나의 식으로 표현 한다고 해서 '표현식'. 이 둘을 합하여 '람다 표현식'이라고 부른다.

아직 무슨 말인지 이해가 안되더라도 걱정하지 않아도 된다. 일단 이정도만 알아 두고 자세한 설명은 예제들과 함께 하도록 한다.

람다 표현식의 형식

lambda 인자 리스트 : 표현식(반환값)

람다 표현식은 lambda 키워드를 시작으로 위와 같은 형식을 가진다.

만일 람다 표현식에서 if를 사용한다면 콜론(:) 없이 표현 한다.

lambda x : exp1 if 조건 else exp2

조건을 만족하면 exp1 반환, 그렇지 않으면 exp2 반환한다.

람다 표현식의 활용

이전 장, [일급 객체]에서 파이썬에서 함수는 일급 객체로 취급 되며, 그러므로 다른 함수의 인자로 사용될 수 있다고 이야기 했었다. 람다 표현식 또한 함수의 일종으로 일급 객체이며, 역시 다른 함수의 인자로 넘겨질 수 있다(일급 객체에 대한 자세한 사항은 [여기]를 참고하도록 한다).

인자로써 함수가 필요한 경우, 매번 def 키워드를 이용해 함수를 정의하는 것이 아니라 간단한 람다 표현식을 이용하면 보다 간결하고 깔끔한 코드를 작성 할 수 있다.

아래 예제는 리스트를 정렬하는 sorted 함수를 사용하는 예다. 리스트의 정렬 기준을 변경하기 위해, 함수와 람다 표현식을 각각 사용하는 예를 비교 해보도록 하겠다.

s1 = ['banana', 'kiwi', 'apple']
print(s1)   # ['banana', 'kiwi', 'apple']

# 알파벳 오름차순 정렬
s2 = sorted(s1)
print(s2)   # ['apple', 'banana', 'kiwi']

# 문자열 길이에 따라 오름 차순 정렬
# sorted 함수의 'key'에 정렬키를 리턴하는 함수를 할당하여 정렬 순서를 변경 할 수 있다
def foo(x) :
    return len(x)

s3 = sorted(s1, key=foo)
print(s3)   # ['kiwi', 'apple', 'banana']

# 함수 대신 람다 표현식 사용
s4 = sorted(s1, key=lambda x : len(x))
print(s4)   # ['kiwi', 'apple', 'banana']

17라인에서 람다 표현식을 이용해 foo 함수를 대신하고 있다. 이 처럼, 코드가 간단하여 간단한 식으로 표현 할 수 있고, 코드의 다른 곳에서 사용하지 않는 함수라면, 매번 함수를 정의하는 것이 아닌 필요한 곳에서 표현식으로 함수를 대체 할 수 있다.

이번에는 람다 표현식에서 비교 구문을 사용한 예를 살펴 보자.

# lambda x : exp1 if 조건 else exp2
f = lambda x, y : x if x > y else y

print(f(10, 5))  # 10
print(f(6, 8))   # 8

람다 표현식에서 if는 콜론(:)이 필요 없으며, 조건이 참일 경우 exp1을 리턴하고 그렇지 않은 경우 exp2를 리턴한다고 이야기 했었다. 위 예제 4라인에서는 10이 5보다 크므로 앞에 있는 표현식. 즉, x를 리턴한다. 5라인에서는 6은 8보다 크지 않으므로 뒤에 있는 표현식, 8을 리턴한다.

람다 사용시 주의 사항

람다는 정의 시점이 아닌 실행 시 식을 평가 한다

종종 람다를 실행 할 때, 람다가 변수들을 조사한 시점을 헷깔려 의도치 않은 결과를 만드는 경우가 종종 있다.

아래 예제에서 f1의 결과물은 람다가 정의 되기 전의 x=10에 1을 더하여 11이 되는 것일까, 람다 표현식이 실행 되기 전에 20으로 변경된 값에 1을 더한 21일까?

x = 10

f1 = lambda y : x + y    # 람다는 실행 시 식을 평가한다. 아직 실행 안됨

x = 20
print(f1(1))

정답은 21이다. 람다는 정의 시점이 아니라 실행 시점에 식을 평가하므로 6라인에서 출력 되는 것은 5라인에서 이미 20으로 변경된 x를 참조하게 된다. 람다가 변수를 참조하는 시점은 정의 시점이 아닌 실행 시점이라는 것을 꼭 기억하도록 하자.

람다 표현식의 정체

람다의 활용법을 간략하게 살펴 보았으니, 이제 람다 자체를 좀더 자세히 알아 보도록 하자. 사실, 람다 표현식이라고 해서 뭔가 엄청난 비밀이 있을 것 같지만 정체는 일반 함수와 동일한 함수 객체다. 다만 람다는 함수의 이름이 없을 뿐이다.

def add1(x, y) :
    return x + y

add2 = lambda x, y : x + y

print(type(add1))         # <class 'function'>
print(type(add2))         # <class 'function'>

print(add1)               # <function add1 at 0x0000028D529D1430>
print(add2)               # <function <lambda> at 0x0000028D529D14C0>

print(add1(1,2))          # 3
print(add2(1,2))          # 3

add2.__dict__['x'] = 100  # 람다 표현식이지만 함수 객체의 어트리뷰트 __dict__가 있음
print(add2.x)

위의 예제를 보면 일반 함수와 람다 표현식의 모든 출력값이 동일하다는 것을 알 수 있다. 9, 10라인에서 결과가 다른 이유는 람다는 이름이 없기 때문에 함수 add1 처럼 이름을 출력 할 수 없기 때문이다. 그 외에 함수 객체의 어트리뷰트 중의 하나인 __dict__에 접근하여 값을 쓰거나 읽는 것도 볼 수 있다.

람다는 함수와 동일하다

마치며

이상 파이썬 람다 표현식에 대해 알아 보았다. 요약하자면 람다 표현식은 함수와 동일하다. 함수가 가지는 모든 어트리뷰트들을 가지고 있으며, 함수와 마찬 가지로 일급 객체로써 다른 함수의 인자로 사용 될 수도 있다. 람다와 일반 함수의 차이점은 람다는 이름이 없다는 것 뿐이다.

이렇게 파이썬 함수에 대한 강의가 완료 되었다. 다음 장에서는 함수 때문(?)에 미뤄 두었던 전역 변수와 지역 변수의 차이에 대해 살펴 보고록 하겠다.

부록 1. 같이 보면 좋은 글

유익한 글이었다면 공감(❤) 버튼 꾹!! 추가 문의 사항은 댓글로!!