정의
Higher-order function(고차 함수)란, 다음 조건 중 하나 이상을 충족하는 function를 가리킴:
- 다른 함수를 argument로 받을 수 있는 function
- 다른 함수를 반환할 수 있는 function
즉, Higher-order function이란
function을 다루는 function이라고 생각하면 됨.
Python에서는 function이 1급 객체(First-class object)이기 때문에,
- function이 argument(인수)로 다른 function에 넘겨지거나,
- 다른 function에서 return value(반환값)으로 사용되는 것이 가능함.
2023.07.15 - [Python] - [Python] first-class object (일급객체)
Higher-order Function(고차함수)의 특징
- function을 variable처럼 취급
- function이 variable에 할당되거나
- 다른 function에 argument로 전달될 수 있음.
- function의 조합 및 추상화 지원
- 복잡한 연산을 간결하게 표현.
- 주로 function을 return value로 반환.
High-order Function(고차 함수)의 예시
1. 다른 함수를 argument(인수)로 받는 Higher-order function
Python의 map
, filter
, reduce
등등.
map
: 각 요소에 대해 지정된 함수를 적용하여 새로운 sequence를 얻음 .
nums = [1, 2, 3, 4]
squared = map(lambda x: x ** 2, nums)
print(list(squared)) # [1, 4, 9, 16]
filter
: 조건을 만족하는 요소만을 포함한 sequence 반환.
nums = [1, 2, 3, 4, 5]
evens = filter(lambda x: x % 2 == 0, nums)
print(list(evens)) # [2, 4]
reduce
: 누적 계산 수행.
from functools import reduce
nums = [1, 2, 3, 4]
product = reduce(lambda x, y: x * y, nums)
print(product) # 24
lambda expression(= annonymous function)과 같이 사용되는 경우가 많음.
2023.07.07 - [Python] - [Python] lambda expression and map, filter, reduce.
2. 다른 함수를 반환하는 Higher-order Function
Higher-order function은 function를 반환하여
새로운 기능을 가진 함수 를 만들 수 있음.
2-1. Closure 패턴
2023.07.15 - [Python] - [Python] Closure
다음은 exponent를 다르게 설정한 제곱함수를 반환해주는 함수의 구현을 보여줌.
def power(exponent):
def inner(base):
return base ** exponent
return inner
square = power(2) # 제곱 함수 생성
cube = power(3) # 세제곱 함수 생성
print(square(4)) # 16
print(cube(2)) # 8
2-2. Decorator
2023.08.18 - [Python] - [Python] Decorator
Decorator는 Higher-order function의 대표적인 사례임:
def my_decorator(func):
def wrapper(*args, **kwargs):
print("Before function call")
result = func(*args, **kwargs)
print("After function call")
return result
return wrapper
@my_decorator
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
이 외에 Python의 functools.partial
, functools.singledispatch
등이 대표적인 사례임.
functools 모듈은 Python의 표준 라이브러리에 포함된 모듈로,
주로 고차 함수(higher-order function) 또는 함수형 프로그래밍 스타일을 구현하는 데
유용한 function들을 제공함.
즉, 이 모듈의 주요 목적은 함수와 관련된 작업을 쉽게 수행할 수 있도록 돕는 유틸함수를 제공하는 것임.
2-3. functools.partial
- 특정 function의 특정 arguments를 미리 고정하여 새 함수를 생성하는 function.
- 반복적으로 동일한 arguments를 전달해야 하는 경우 주로 이용됨.
from functools import partial
def multiply(x, y):
return x * y
double = partial(multiply, y=2) # y 값을 2로 고정
print(double(5)) # 10
2-4. functools.singledispatch
singledispatch는 첫번째 단일 인자 (=single) 타입을 기준으로 디스패치하는 overloading을 구현.
- 일종의 generic function을 구현하는데 사용됨.
- overloading을 python에서 가능하게 해줌.
- 첫번째 argument의 type에 따라 다른 function호출이 가능해짐.
- arguments의 조합에 따른 dispatch를 수행하는 multipledispatch가 아님을 주의
- class의 메서드로도 사용가능하나,
__init__
에는 동작안함: 생성자를 위해선singledispatchmethod
이용할 것.
참고로 "dispatch" 는 "전달하다, 분배하다, 할당하다" 등의 의미를 가지며,
프로그래밍에서는 특정 조건에 따라 실행할 함수를 선택 또는 호출하는 과정을 가리킴.
즉, type, value, 또는 다른 조건에 따라 function이나 method를 호출시키는 것을 가리킴.
다음은 argument가 int 인지 str인지에 따라 다르게 dispatch되는 function 을 구현한 예제임.
from functools import singledispatch
@singledispatch
def process(data):
print("Default:", data)
@process.register(int)
def _(data):
print("Processing int:", data)
@process.register(str)
def _(data):
print("Processing str:", data)
process(10) # Processing int: 10
process("hello") # Processing str: hello
process([1, 2]) # Default: [1, 2]
register
로 사용된 함수의 이름이_
로 쓰는 이유는 대부분 이후로 사용되지 않기 때문임.- 관례적으로 많이 사용되는 방식이나 강제 사항의 아니며 다음과 같이 구현해도 문제 없음.
from functools import singledispatch
@singledispatch
def process(data):
print("Default:", data)
@process.register(int)
def process_int(data):
print("Processing int:", data)
@process.register(str)
def process_string(data):
print("Processing str:", data)
process(10) # Processing int: 10
process("hello") # Processing str: hello
process([1, 2, 3]) # Default: [1, 2, 3]
class에서의 method에 적용할 때는 singledispatchmethod
를 사용하는 게 좋음
(생성자도 커버하는 장점을 가지나, Python 3.8이후부터만 지원됨.)
from functools import singledispatchmethod
class MyClass:
@singledispatchmethod
def __init__(self, arg):
raise TypeError(f"Unsupported type: {type(arg)}")
@__init__.register(str)
def _(self, arg):
print(f"Initialized with a string: {arg}")
self.data = arg.upper()
@__init__.register(int)
def _(self, arg):
print(f"Initialized with an integer: {arg}")
self.data = arg * 10
# 사용 예시
obj1 = MyClass("hello") # Initialized with a string: hello
print(obj1.data) # HELLO
obj2 = MyClass(5) # Initialized with an integer: 5
print(obj2.data) # 50
obj3 = MyClass(3.14) # TypeError: Unsupported type: <class 'float'>
3. Higher-order Function의 유용성
- 코드 재사용성: 공통 패턴을 함수로 추출하여 재사용 가능.
- 가독성 향상: 복잡한 연산을 간결하게 표현.
- 유연성: 함수를 동적으로 생성하거나 조합 가능.
같이 보면 좋은 자료들
2023.10.06 - [Pages] - [Python] function 과 Scope, Name Space 정리
https://medium.com/analytics-vidhya/higher-order-functions-python-716f508a8f41
'Python' 카테고리의 다른 글
[Py] Serialization of Python: pickle (1) | 2024.11.27 |
---|---|
[Py] Context Manager: with statement! (0) | 2024.11.27 |
[Py] 숫자 야구 게임: structured programming, type annotation, and OOP (0) | 2024.11.20 |
[DIP] Block Truncation Coding (BTC) (0) | 2024.11.18 |
[Py] Queue 와 Stack 구현하기: 상속과 오버라이딩 이용. (0) | 2024.11.13 |