2019.03.11 - python(4)
lambda 익명함수
람다(lambda) 함수는 이름이 없는 함수이다. 함수를 재사용하지 않고 몇 번 정도만 쓸 경우 사용한다. 변수에 할당해 놓지 않으면 다음 줄(line)으로 넘어가는 순간 메모리에서 사라진다.
파이썬의 함수는 “식”
식
은expression
이다.- 식은 반환값이 존재한다.
a = 10
b = 20
a+b
위의 세 줄 모두 식이다. 1, 2 번째 줄은 단항식이다.
def func(a, b):
a + b
위의 func함수는 None을 반환하는 식이다. 즉 return이 없으면 None을 반환하는 식이다.
lambda 문법
lambda는 return을 쓰지 않아도 무조건 return 값을 갖는다.
lambda a, b: a + b
예제)
li = [5, 2, 3, 1, 7, 10]
def pred(x):
return x % 2
li.sort(key=pred, reverse=True)
li
>>> [5, 3, 1, 7, 2, 10]
python은 stable sorting을 지원한다. 따라서 정렬후에 한번 더 정렬이 되지 않아 5, 3, 1, 7의 순서가 유지된다.
예제에 lambda적용)
li = [5, 2, 3, 1, 7, 10]
li.sort(key=lambda x: x % 2, reverse=True)
li
>>> [5, 3, 1, 7, 2, 10]
lazy evaluation
- lazy evaluation –> map, filter, reduce, generator 쓰는 이유
- 함수의 실행 시기는 내가 결정
- 내가 원할 때만 결과값을 가져온다
- 함수의 실행 시간을 결정
- 메모리 공간을 효율적으로 사용 가능
li = [-3, 5, 1, 2, -5, -4, 14]
def func(x):
print('func executed') # print가 실행되면 함수가 실행됐다는 것을 알 수 있음
return x > 0
f = filter(func, li)
next(f)
>>> func executed
func executed
5
next(f)
>>> func executed
1
next(f)
>>> func executed
2
next(f)
>>> func executed
func executed
func executed
14
filter, map, reduce
reduce함수는 귀도의 요청에 따라
from functools import reduce
에서 불러와 사용해야한다.
filter와 map객체는 generator객체이다. 그리고 이 generator객체는 iterator에 포함된다. 이 iterator는 순회(iterable)가 가능하다. 이러한 iterable한 객체들은 반복문에서 사용 가능하다.
filter
li = [-3, 5, 1, 2, -5, -4, 14]
# 음수 떼기, filter 사용
# li에는 iterater가 와야함
f = filter(lambda elm: elm>0, li) # 이때 f는 filter객체, lazy evalution을 하는 객체
f
>>> <filter at ~~~~>
next(f)
>>> 5
next(f)
>>> 1
next(f)
>>> 2
next(f)
>>> 14
next(f)
>>> Error Stopiteration
without_neg = list(filter(lambda elm: elm>0, li))
without_neg
>>> [5, 1, 2, 14]
map
li = [2, 3, 5, 7]
m = map(lambda x: x**2, li)
next(m)
>>> 4
next(m)
>>> 9
next(m)
>>> 25
next(m)
>>> 49
next(m)
>>> Error Stopiteration
filter와 map을 동시에 사용
# 양수를 골라내서 제곱한 값을 리스트로 만드세요
li = [2, 3, -5, 6, -2, 1, -10]
result = list(map(lambda b: b**2, filter(lambda a: a>0, li)))
result
>>> [4, 9, 36, 1]
a와 b가 같아도 괜찮다. 왜냐하면 지역변수이기 때문이다.
reduce
자료구조(list, tuple)를 연산을 통해서 단 하나의 값으로 만드는 함수이다. 사용하기 위해서는 from functools import reduce
통해 import해야한다.
from functools import reduce
li = [2, 3, -5, 6, -2, 1, -10]
result = reduce(lambda a, b: a + b, li, 100) # 초기값으로 100을 주면, 첫번째 a에 100이 들어간다.
result
>>> 95
- reduce함수를 사용하여 최대값 구하기
li = [3, 6, 8, -10, 2, 1, 100, 50, 46, -47]
max_result = reduce(lambda a, b: a if a > b else b, li) # reduce함수 안에 삼항연산자 사용
max_result
>>> 100
-
reduce함수를 사용하여 문자의 개수 세기
- hint
- 파이썬 함수는 식 -> 무조건 값을 반환 함
- 논리연산을 할 때 마지막으로 참조한 값을 반환한다.
li = ['a', 'b', 'a', 'b', 'b', 'a', 'c', 'a']
result = reduce(lambda dic, ch: dic.update({ch : dic.get(ch, 0) + 1}) or dic, li, {})
result
>>> {'a': 4, 'b': 3, 'c': 1}
python에서 dictionary update함수는 None을 반환한다. 따라서 or 연산자를 통해 update실행 후 뒤의 dic를 반환시켜야 update된 dictionary 결과를 재사용 할 수 있다.
decorator(데코레이터)
쉽게 기능을 추가할 수 있다.
예) fnacy한 decofunc을 내가 만든 함수에 기능 추가한다.(물론 decofunc은 없는 함수이다)
# 함수의 이름은 보통 함수 기능을 대변하는 이름으로 작성해야 한다.
def outer(org_func): # 내가 작성중인 함수
def inner(*args, **kwargs): # 함수가 정의 될때 받는 인자 (가변인자를 사용하겠다는 뜻)
# 추가하는 기능을 구현한다
print('added functionalty')
# 함수 실행 시 위의 args와 kwargs를 unpacking하여 인자로 받아 실행한다는 뜻이다.
return org_func(*args, **kwargs)
return inner
@outer
def func(a, b):
return a + b
func(10, 1)
>>> added functionalty
11
- 이를 이용해 함수실행에 걸린 시간을 체크하는 데코레이터를 만들어보자
import time
from functools import wraps
def benchmarker(org_func):
@wraps(org_func)
def inner(*args, **kwargs):
start = time.time()
result = org_func(*args, **kwargs)
elaspsed = time.time() - start
print(f'elaspsed time : {elaspsed:.2f}')
return result
return inner
@benchmarker
def something(a, b):
time.sleep(5)
return a + b
something(1, 2)
>>> elaspsed time : 5.01
3
위의 함수에서
wraps
로 inner 부분을 감싸면something.__name__
을 실행했을 때 함수의 이름(something
)이 나온다. wraps로 감싸기 전에는 (inner
)가 나온다.
- 함수의 호출 횟수를 체크하는 데코레이터
- 함수의 호출 횟수를 보여준다.
- func(a, b)
- ‘n’번 호출되었습니다.
g_call_num = 0
def callcounter(org_func):
@wraps(org_func)
def inner(*args, **kwargs):
global g_call_num
g_call_num += 1
print(f'{g_call_num}번 호출되었습니다.')
return org_func(*args, **kwargs)
return inner
@callcounter
def func(a, b):
return a + b
for _ in range(10):
func(1, 2)
>>> 11번 호출되었습니다.
12번 호출되었습니다.
13번 호출되었습니다.
14번 호출되었습니다.
15번 호출되었습니다.
16번 호출되었습니다.
17번 호출되었습니다.
18번 호출되었습니다.
19번 호출되었습니다.
20번 호출되었습니다.