Python의 functools.reduce는 Iterable 데이터에 2개의 인자를 갖는 함수를 누적하여 적용해서 결과를 하나로 만드는 함수이다.
reduce는 Java, Javascript, Elixir 와 같은 고수준 언어에서 제공되는 functional 함수/메서드이다.
이 글은 단지 Python의 reduce를 기준으로 설명하지만 전반적인 지식은 Python에만 국한되지 않는다.
간단한 예제로 두 수를 받아 그 합을 리턴하는 함수 add가 있다고 했을 때 int list에 대해 add 함수를 누적해서 적용하는 코드를 아래와 같이 reduce를 사용하여 만들 수 있다.
from functools import reduce
def add(a: int, b: int):
print(a, b)
return a + b
reduce(add, [1, 2, 3, 4])
# 1 2
# 3 3
# 6 4
콘솔에서 확인할 수 있듯이 reduce를 사용하여 int list에 있는 값들과 add 함수의 결과를 다시 입력으로 사용하면서 결과를 하나로 만드는 것을 볼 수 있다.
이를 시각화 해보면 아래와 같다.
reduce가 무엇인지 이해하고 사용하는 것은 전혀 어렵지 않다.
게다가 for 문이라는 가장 편한 대체제가 있기도하다.
하지만 이를 어떤 상황에서 활용하면 좋을지를 이해한다면 reduce의 사용을 고려할 수 있게 된다.
reduce를 활용할 수 있는 좋은 상황은 함수 인자를 (외부 라이브러리와 같이) 변경하기 힘들 때이다.
외부 라이브러리를 사용하면서 가끔씩 내가 원하는 형식의 인터페이스를 제공하지 않아 낭패스러운 경우가 있다.
예를 들어서 사용하고자하는 라이브러리 함수가 아래와 같이 인자를 2개 받는 함수라고 하자.
from typing import List
def bitwise_and(a: List[int], b: List[int]):
assert len(a) == len(b)
return [a_bit & b_bit for a_bit, b_bit in zip(a, b)]
이 라이브러리를 사용할 때 내가 갖고 있는 데이터도 2개 뿐이라면 큰 문제가 없다.
하지만 3개 이상의 데이터를 이 라이브러리 함수를 사용해서 결과를 누적 계산해야한다면 코드가 조금 애매해진다.
물론 for 문을 사용해서 누적 계산 처리를 할 수 있지만 코드가 조금은 복잡해져서 가독성 측면에서 조금 아쉬운 부분이 있다.
bytes = [
[1,0,0,1,0,1,1,1],
[1,0,1,0,1,1,0,1],
[1,0,0,1,0,1,1,1],
]
result = bytes[0]
for i in range(1, len(bytes)):
byte = bytes[i]
result = bitwise_and(result, byte)
assert result == [1,0,0,0,0,1,0,1]
이럴 때 reduce를 사용해주면 간결하게 누적 계산 처리를 할 수 있다.
from functools import reduce
bytes = [
[1,0,0,1,0,1,1,1],
[1,0,1,0,1,1,0,1],
[1,0,0,1,0,1,1,1],
]
assert reduce(bitwise_and, bytes) == [1,0,0,0,0,1,0,1]
이와 관련되어 Numpy의 논리 연산 함수(np.logical_xxx)는 데이터 마스킹에 쓰이는데 이 논리 연산 함수는 인자를 2개만 받는다.
만약 여러 데이터를 사용하여 마스킹을 해야한다면 reduce를 활용하여 간결하게 코드를 만들 수 있다.
import numpy as np
from functools import reduce
masks = [
np.array([1,0,0,1,0,1,1,1]),
np.array([1,0,1,0,1,1,0,1]),
np.array([1,0,0,1,0,1,1,1]),
]
reduce(np.logical_and, masks)
# array([ True, False, False, False, False, True, False, True])
물론 reduce는 함수의 입력과 출력 타입이 동일해야 사용할 수 있는 방법이기에 매우 한정적인 조건에서 사용할 수 있다.
하지만 앞서 예제와 같이 함수를 직접적으로 수정하기 힘든 상황에서 꽤나 유용하게 사용될 수 있다.
'Python > Python' 카테고리의 다른 글
2차원 List 에서 distinct 처리하기 (0) | 2024.02.12 |
---|---|
== 와 is 비교 (0) | 2020.12.28 |
function의 default parameter 사용 시 주의점 (0) | 2020.12.28 |
*args, **kwargs에 대해서 (0) | 2020.12.28 |
댓글