PYTHON

[Python] 일급 함수(First Class Function)

Kine00 2020. 1. 27. 16:57

1. 변수 등에 할당 가능

 

2. 함수 인수 전달 가능

 

3. 함수 결과로 반환 가능

 

 

위의 세가지 조건이 충족된다면 일급함수(First Class Function)이다.

 

 

 


1. 변수 등에 할당 가능

 

def factorial(n):
    if n == 1:
        return 1
    return n*factorial(n-1)

 

 

# 변수 할당
var_func = factorial

print('1 -', var_func)
print('2 -', var_func(5))

 

 

결과값

1 - <function factorial at 0x000001E2674F99D0>
2 - 120

 

위의 코드와 같이 함수를 정의한 후 변수(var_func)에 함수를 할당해서 여러가지 방식으로 사용 할 수 있습니다.

 

 

 


2. 함수 인수 전달 및 함수로 결과 반환(Higher-order Function = 고위함수)

 

print('1 -', map(var_func, range(1,10)))
print('2 -', list(map(var_func, range(1,10))))
print('3 -', list(map(var_func, filter(lambda x: x%2==0 , range(1,6)))))

 

결과값

1 - <map object at 0x0000025D61F764C0>
2 - [1, 2, 6, 24, 120, 720, 5040, 40320, 362880]
3 - [2, 24]

 

코드에서 사용된 map()함수는 두 번째 파라미터에(iterable가능한 객체여야 한다.) 첫 번째 파라미터(함수)를 적용한 결과를 원소로 갖는 iterable 객체를 반환한다.

 

 

이와같이 일급 함수가 있으면 함수형 프로그래밍이 가능하다.

그렇다면 함수형 프로그래밍의 특징 중 하나인 고위함수에 대해서 좀 더 알아보자.

 

 

고위함수는 함수를 인수로 받거나, 함수를 결과로 반환하는 함수다.

위에서 사용한 map() 함수도 고위함수이다. 그 밖에도 sorted() 함수가 있는데 key 파라미터로 함수를 전달받아 정렬할 각 원소에 적용한다.

 

아래의 코드를 보자.

 

 

fruits = ['apple', 'banana', 'strawberry', 'blueberry', 'melon']

print(sorted(fruits, key=len))

 

 

결과값

['apple', 'melon', 'banana', 'blueberry', 'strawberry']

위와 같이 단어의 길이순서로 정렬하기 위해 len()함수를 key파라미터에 전달하면 된다.

Parameter를 1개 받는 함수는 모두 key에 전달할 수 있다.

 

 

 

그 외에도 위해서 한번 사용했던 filter()와 같은 고위함수가 있다.

print(list(map(var_func, filter(lambda x: x%2 , range(1,6)))))

 

결과값

[1, 6, 120]

 

 

 

map(), filter()는 파이썬3의 빌트인 함수이지만, 지능형 리스트(Comprehending List)와 제너레이터 표현식이 소개된 후에 이 함수들의 중요성은 다소 떨어졌다. map()과 filter()는 제너레이터를 리턴하므로 제너레이터 표현식이 이 함수들을 대체할 수 있기 때문이다.

 

# Comprehending List 사용

print([var_func(i) for i in range(1,6) if i%2])

 

결과값

[1, 6, 120]

 

 

iterable한 객체의 원소들의 합계를 내릴 때 사용하던 reduce()는 sum() 함수로 대체할 수 있다.

 

from functools import reduce
from operator import add

print(reduce(add, range(1,11)))
print(sum(range(1,11)))

 

결과값

55
55

 


익명함수(lambda)

 

고위함수를 사용할 때 일회용 함수를 생성하는 게 편리할 때도 있다. lambda키워드는 파이썬 표현식 내에서 익명함수를 제공한다. 그러나 람다 함수의 본체는 순수한 표현식으로만 구성하도록 제한되어있다.

 

익명함수를 사용할 땐 가급적 주석을 사용하고, 익명함수를 사용하는 것 보단 함수를 사용하는 것들 PEP(Python Enhance Proposal)에서도 권장한다.

(이름이 없는 함수를 많이 사용하면 코드의 가독성이 떨어진다는 인식이 있다.)

 

print(reduce(lambda x, t: x+t, range(1,11)))

 

결과값

55

reduce()add()함수 대신 익명함수(lambda) 함수를 사용해도 똑같은 결과값이 출력된다.

 

 

 


Collable : 호출 연산자 -> 메소드 형태로 호출 가능한지 확인

 

 

import random

# 로또 추첨 클래스 선언
class LottoGame:
    def __init__(self):
        self._balls = [n for n in range(1,46)]
    
    def pick(self):
        random.shuffle(self._balls)
        return sorted([random.choice(self._balls) for n in range(6)])

    def __call__(self):
        return self.pick()

 

__call__() 메서드를 오버로딩을 통해 구현하면 모든 객체를 콜러블 타입으로 만들 수 있다.

즉, 함수처럼 동작을 할 수 있게 해준다.

 

game = LottoGame()

# 호출 가능 확인
print(callable(str), callable(list), callable(factorial), callable(54))
print(game.pick())
print(game())
print(callable(game))

 

결과값

True True True False
[4, 20, 21, 34, 34, 38]
[6, 15, 16, 16, 19, 32]
True

 

__call__() 메소드가 구현되어 있으므로 LottoGame타입의 객체는 호출가능하며, return 값은 _balls에서 꺼낸 원소가 된다.