TIL. 28 람다 표현식(lambda) 사용하기
## lambda 표현식
## 람다 표현식은 식 형태로 되어 있어 람다 표현식이라 부르며,
## 함수를 간편하게 작성할 수 있어, "다른 함수의 인수"로 넣을때 자주 사용한다.
## 반화 값으로 매개변수 x + 10 형태의 함수를 람다 표현식으로 함수를 해보자
1. def
def ppp(x):
return x+10
print(ppp(1))
::
11
2. 람다 표현식
# 람다 표현식은 다음과 같이 lambda에 매개변수를 지정하고 :(콜론) 뒤에 반환값으로 사용할 식을 지정한다.
# lambda 매개변수들: 식
lambda x : x+10
# 람다 표현식을 위와 같이 작성하면 함수 객체가 생성되는데, 이 상태만으로는 함수를 호출 할 수 없다.
# 람다 표현식은 "이름없는"함수를 만들기 때문이다, 이때문에 람다 표현식을 익명 함수(anonymous function) 라고도 부른다.
## 람다로 만든 함수를 호출하기 위해선 임의의 변수에 할당하여 호출하면 된다.
a = lambda x : x+10
print(a(10))
::
20
## 람다 표현식을 살펴보면 lambda x: x + 10은 매개변수 x 하나를 받고, x에 10을 더해서 반환한다는 뜻이다.
# 즉, 매개변수, 연산자, 값 등을 조합한 식으로 반환값을 만드는 방식이다
# 다음그림과 같이 def로 만든 함수와 비교해보자.

## 람다 표현식 자체로 호출하기
## 임의의 변수를 할당하지않고 ()로 묶어 람다 표현식 자체만으로도 함수를 호출 할 수 있는 방법이있다.
## (lambda 매개변수들: 식)(인수들) IDLE에서 좀 더 쉽게 확인가능
(lambda x : x+10)(25) # (lambda 매개변수들: 식)(인수들)
print((lambda x : x+10)(25))
::
35
## 람다 표현식안에서는 변수를 만들 수 없다.
(lambda x: y=10; x + y) # error
## 이처럼 y라는 변수를 람다 표현식에 만들 수 없다
## 따라서 반환값 부분은 변수 없이 식 1줄로 작성되어야 하며, 추가 변수가 필요할 경우 def 이용해야한다.
## 람다 표현식은 바깥에 있는 함수를 사용할 수 있다.
y = 20
(lambda x: x+y)(1)
출력시
::
21
## 위와 같이 매개변수 x 와 바깥의 변수 y 를 이용해 값을 반환 할 수 있다.
## 람다 표현식을 사용하는 이유
## 람다 표현식은 "함수의 인수 부분에서 함수를 간단하게 만들기 위해 사용한다" 대표적으로 map이다.
def plus_ten(x):
return x + 10
b = list(map(plus_ten, [1, 2, 3])) ## map 객체로 저장되는 값을 사람이 보기 위해 list로 변환시켜주는 과정
print(b)
::
[11, 12, 13]
## map은 여러 개의 데이터를 한 번에 다른 형태로 변환하기 위해서 사용한다.
# 따라서, 여러 개의 데이터를 담고 있는 list나 tuple을 대상으로 주로 사용하는 함수이다.
### 지금까지 map을 사용할 때 map(str, [1, 2, 3])와 같이 자료형 int, float, str 등을 넣었다
### 사실 plus_ten처럼 함수를 직접 만들어서 넣어도 된다.
c = list(map(lambda x : x+10 ,[1, 2, 3]))
# 위 식의 b와 비교해보면 plus_ten 이라는 함수 자리를 람다 표현식으로 대체한 것
print(c)
::
[11, 12, 13]
## 위 식의 b와 비교해보면 plus_ten 이라는 함수 자리를 람다 표현식으로 대체한 것이며
## 대체할 경우 def 과정이 사라진 코드 단축효과를 볼 수 있다.
## 이처럼 "함수를 다른 함수의 인수로 넣을때" 편리하다
## 람다 표현식으로 매개변수가 없는 함수 만들기
f = (lambda: 1)()
print(f)
::
1
x = 10
g = (lambda : x)()
print(g)
::
10
## 람다 표현식으로 매개변수가 없는 함수를 만들 때는 lambda 뒤에 아무것도 지정하지 않고 :(콜론)을 붙혀준다.
## 단, 콜론 뒤에는 반드시 반환할 값이 있어야 한다. 왜냐하면 표현식(expression)은 반드시 값으로 평가되어야 하기 때문이다.
## 람다 표현식에서 map, filter, reduce 사용하기
## 람다 표현식에서 조건문 사용하기
# lambda 매개변수들: 식1 if 조건식 else 식2
## map을 이용해 a라는 리스트에서 3의 배수를 문자열로 출력
<IDLE> # not vscode
a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
list(map(lambda x : str(x) if x % 3 == 0 else x,a))
::
[1, 2, '3', 4, 5, '6', 7, 8, '9', 10]

# map은 리스트의 요소를 각각 처리하므로 lambda 의 반환값도 요소여야 한다.
## 식 1 == str(x) , 조건식 == x % 3 == , 식2 == x
## 람다 표현식에서 조건문 사용시 주의할 점
## 1. if else 조건문 사용시 :(콜론)을 붙히지 않음 (일반적 조건문 문법과 다름)
## 2. 식1 if 조건식 else 식2 // 조건문이 참이면 식1, 거짓일때는 식2를 사용한다.
## 3. if를 사용했다면 반드시 else도 사용해줘야한다. (if만 사용시 error)
## 4. elif 를 사용 할 수 없다
## 람다 표현식에 조건문 여러개 사용하기
# lambda 매개변수들: 식1 if 조건식1 else 식2 if 조건식2 else 식3
# 리스트에서 1은 문자열로 변환하고, 2는 실수로 변환, 3 이상은 10을 더하는 식은 다음과 같이 작성
<IDLE>
list(map(lambda x : str(x) if x== 1 else float(x) if x==2 else x+10,a))
::
['1', 2.0, 13, 14, 15, 16, 17, 18, 19, 20]
## lambda 매개변수들: 식1 if 조건식1 else // 식2 if 조건식2 else 식3 ==>
//를 기준으로 왼쪽 조건식 1개, 오른쪽 조건식 1개로 이해하면 편하다
## 위 식을 def로 바꿔 작성해보자
def f(x):
if x == 1:
return str(x)
elif x == 2:
return float(x)
else:
return x+10
a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
b = list(map(f,a))
print(b)
::
['1', 2.0, 13, 14, 15, 16, 17, 18, 19, 20]
## 조건이 복잡하여 이해하거나 알아보기 힘들경우 억지로 람다 표현식을 사용하지 말고, def로 사용하는것을 권장한다.
## 복잡하고 어려운 코드는 작성 후 시간이 지나면 알아보기 어려운 경우가 많으니,
코드는 길이가 조금 길어지더라도 알아보기 쉬워야 한다.(중요)
## mpa에 객채 여러개 넣기
<IDLE>
a = [1, 2, 3, 4, 5]
b = [2, 4, 6, 8, 10]
list(map(lambda x, y: x * y, a, b))
::
[2, 8, 18, 32, 50]
## map은 리스트 등의 반복 가능한 객체를 여러개 넣을 수 있다.
## 위 코드는 두 리스트의 요소를 서로 곱해 새로운 리스트를 만드는 방법이다.
## 리스트 2개를 처리해야하는 경우 lambda x, y : x*y 처럼 매개변수를 2개 지정해주면 된다.
## 그다음 리스트 2개를 ,(콤마)로 구분해 넣어주면 된다.
## 즉, 람다 표현식의 매개변수 개수에 맞춰 반복 가능한 객체를 콤마로 구분하여 넣어주면 된다.
## 여기서 반복 가능한 객체는 a, b 이며, a, b 의 요소의 개수가 다를 경우 요소의 개수가 더 작은 객체를 기준으로 출력해준다
## ex : a = [1, 2] ==> 해당 식의 반환 값은 [2, 8] 까지만 반환
## ex : a = [1, 2, 3, 4, 5, 6, 7, 8] ==> 해당 식의 반환 값은 [2, 4, 6, 8, 10] 까지만 반환 (b의 요소 개수 5개)
## filter 사용하기
# filter는 반복 가능한 객체에서 특정 조건에 맞는 요소만 가져오는데,
# filter에 지정한 함수의 반환값이 True일 때만 해당 요소를 가져온다.
# filter(함수, 반복가능한객체)
## def를 이용한 리스트에서 5보다 크면서 10보다 작은 숫자를 가져오기.
a = [8, 3, 2, 10, 15, 7, 1, 9, 0, 11]
def aaa(x):
return x > 5 and x <10
print(list(filter(aaa, a)))
::
[8, 7, 9]
## 람다 표현식을 이용한 리스트에서 5보다 크면서 10보다 작은 숫자를 가져오기.
a = [8, 3, 2, 10, 15, 7, 1, 9, 0, 11]
b = list(filter(lambda x : x > 5 and x < 10, a))
print(b)
::
[8, 7, 9]

## 즉 filter 는 x > 5 and x <10 조건 중 참인 것만을 가져오고, 거짓은 버림
## reduce 사용하기
# reduce는 반복 가능한 객체의 각 요소를 지정된 함수로 처리한 뒤 이전 결과와 누적해서 반환하는 함수
# (reduce는 파이썬 3부터 내장 함수가 아닙니다. 따라서 functools 모듈에서 reduce 함수를 가져와야 한다)
# from functools import reduce
# reduce(함수, 반복가능한객체)
## def로 reduce 사용하기
from functools import reduce
a = [1, 2, 3, 4, 5]
def bbb (x, y):
return x+y
print(reduce(bbb, a))
::
15
## 함수 bbb에서 x + y를 반환하도록 만들었으므로 reduce는 그림과 같이 요소 두 개를 계속 더하면서 결과를 누적한다
## 람다 표현식으로 reduce 사용하기
a = [1, 2, 3, 4, 5]
from functools import reduce
c = reduce(lambda x, y : x+y, a)
print(c)
::
15

# 참고 | map, filter, reduce와 리스트 표현식
## 리스트(딕셔너리, 세트) 표현식으로 처리할 수 있는 경우에는 map, filter와 람다 표현식 대신 리스트 표현식을 사용하는 것이 좋습니다. list(filter(lambda x: x > 5 and x < 10, a))는 다음과 같이 리스트 표현식으로도 만들 수 있습니다.
<IDLE>
a = [8, 3, 2, 10, 15, 7, 1, 9, 0, 11]
[i for i in a if i > 5 and i < 10]
::
[8, 7, 9]
### 리스트 표현식이 좀 더 알아보기 쉽고 속도도 더 빠릅니다.
## 또한, for, while 반복문으로 처리할 수 있는 경우에도 reduce 대신 for, while을 사용하는 것이 좋다.
왜냐하면 reduce는 코드가 조금만 복잡해져도 의미하는 바를 한 눈에 알아보기가 힘들다.
이러한 이유로 파이썬 3부터는 reduce가 내장 함수에서 제외되었다.
# reduce(lambda x, y: x + y, a)는 다음과 같이 for 반복문으로 표현할 수 있습니다.
<IDLE>
a = [1, 2, 3, 4, 5]
x = a[0]
for i in range(len(a) - 1):
... x = x + a[i + 1]
...
x
::
15
