2019.03.11 - python(4)

3 분 소요

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
    1. 파이썬 함수는 식 -> 무조건 값을 반환 함
    2. 논리연산을 할 때 마지막으로 참조한 값을 반환한다.
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 호출되었습니다.

태그:

카테고리:

업데이트: