프로젝트 중에 Node 코드를 Python으로 짜야하는 프로젝트 진행 중에 같은 이름으로 같은 기능을 하는 함수지만 사용 방법이 다른 두 개의 함수를 공부하였다.
우선 JS, 즉 Node에서 사용되는 filter와 reduce를 정리해본다.
filter 함수와 reduce 함수는 고차 함수로서 다른 함수를 소비 또는 생성하는 함수다.
reduce 함수
array.reduce(callbackFunction(previousValue, currentValue, currentIndex, array1), initValue);
reduce 함수의 매개변수는 callbackFunction과 initVaule이다.
callback 함수는 4개의 매개변수를 선택적으로 받을 수 있고, 차례대로 배열의
첫번째 값, 혹은 initValue / 배열 내 처리되고 있는 값 / 배열 내 처리되고 있는 값의 인덱스 / 호출된 배열 이다.
initValue는 callback을 호출할 때 초기값으로 쓰이며 선택적 사용이 가능하다.
<script type="text/javascript">
(function test(){
var array = [0,1,2,3,4];
array.reduce(function(previousValue, currentValue, currentIndex, array1){
console.log(previousValue);
console.log(currentValue);
console.log(currentIndex);
console.log(array1);
});
})();
</script>
reduce 함수는 배열의 길이의 -1만큼 반복된다. 이것은 두번째 인자인 currentValue 때문이다.
조금 더 알아보기 쉽게 표현하자면, 아래의 표로 알아볼 수 있다.
회차 | previousValue | currentValue | currentIndex | array1 |
1 | 0 | 1 | 1 | [0,1,2,3,4] |
2 | 1 | 2 | 2 | [0,1,2,3,4] |
3 | 3 | 3 | 3 | [0,1,2,3,4] |
4 | 6 | 4 | 4 | [0,1,2,3,4] |
결론적으로 reduce의 함수의 역할은 배열을 순회하면서 결과값을 내는데, 위의 코드에는 reduce 함수의 return형이 없기에 출력 결과 undfiend로 나오는 것을 확인할 수 있다.
<script type="text/javascript">
(function test(){
var array = [0,1,2,3,4];
var sum = array.reduce(function(previousValue, currentValue, currentIndex, array1){
return previousValue+currentValue;
});
console.log("sum : "+sum);
})();
</script>
reduce의 return형을 작성하여 결과값을 찍어보면 sum=10이 찍히는 것을 확인할 수 있다.
만약 여기서 initValue를 1로 주게 된다면 첫 값은 1로 시작하게 되고 sum=11이 되는 것을 확인할 수 있다.
filter 함수
filter 함수는 명칭과 같이 callbackFunction의 조건에 해당하는 모든 요소가 있는 배열을 새로 생성하는 기능을 한다.
var newArray = arr.filter(callbackFunction(element, index, array), thisArg);
callback 함수에서 차례대로 요소값, 요소의 인덱스, 사용되는 배열의 매개변수를 사용할 수 있다.
thisArg는 filter에서 사용하는 this 값으로 선택적으로 사용할 수 있다.
<script type="text/javascript">
(function test(){
var testArray = [1,2,3,4,5];
var newArray = testArray.filter(function(element, index, array){
console.log(element);
console.log(index);
console.log(array);
});
})();
</script>
위의 설명과 같이 요소는 현재 순회하는 배열의 값이고, 인덱스, 배열이 로그에 찍히게 된다.
이 필터를 사용하여 원하는 조건의 값만 뽑아낼 수 있다. 아래의 코드는 배열의 3 이하인 값만 추출해 새 배열을 만드는 코드이다.
<script type="text/javascript">
(function test(){
var testArray = [1,2,3,4,5];
var newArray = testArray.filter(function(element){
return element<=3;
});
console.log(newArray);
})();
</script>
이렇게 하면 결과값은 [1,2,3]이 출력이 되게 된다.
이제 파이썬에서의 reduce 함수와 filter 함수를 확인해본다.
reduce 함수
파이썬의 functools 내장 모듈의 reduce 함수는 여러 개의 데이터를 대상으로 주로 누적 집계를 내기 위해서 사용을 한다.
reduce(집계 함수, 순회 가능한 데이터[, 초기값])
여기서 집계 함수는 js 와 같이 콜백 함수이기도 하며, 두 개의 인자를 받아야 하는데, 첫번째 인자는 누적자(accumulator)이고, 두번 째 인자는 현재값(currentValue)를 사용해야 합니다.
누적자는 함수 실행의 시작부터 끝까지 재사용되는 값이고, 현재값은 루프를 돌며 계속 바뀌는 값을 의미합니다.
from functools import reduce
users = [{'mail': 'gregorythomas@gmail.com', 'name': 'Brett Holland', 'sex': 'M', 'age': 73},
{'mail': 'hintoncynthia@hotmail.com', 'name': 'Madison Martinez', 'sex': 'F', 'age': 29},
{'mail': 'wwagner@gmail.com', 'name': 'Michael Jenkins', 'sex': 'M', 'age': 51},
{'mail': 'daniel79@gmail.com', 'name': 'Karen Rodriguez', 'sex': 'F', 'age': 32},
{'mail': 'ujackson@gmail.com', 'name': 'Amber Rhodes', 'sex': 'F', 'age': 42}]
reduce(lambda acc, cur: acc + cur["age"], users, 0)
예제 코드를 갖고서 유저들의 나이의 합을 구하는 코드이다. 위에서 말했듯이 acc는 누적자이며, cur는 순회하는 값이다.
집계 함수를 따로 콜백 함수를 만들지 않고 재사용하지 않을 함수이기에 바로 lambda 함수를 사용하여서 바로 리턴하는 함수를 만들었다.
함수의 실행 과정을 보자면 아래와 같은 형태로 누적 집계를 하고 배열을 순회하여서 결과를 도출해낼 수 있다.
>>> 0
0
>>> 0 + 73 -> (acc+cur)
73
>>> 73 + 29
102
>>> 102 + 51
153
>>> 153 + 32
185
>>> 185 + 42
227
집계 함수 뿐만이 아니라 원하는 목록을 뽑아내기도 가능하다.
>>> reduce(lambda acc, cur: acc + [cur["mail"]], users, [])
['gregorythomas@gmail.com', 'hintoncynthia@hotmail.com', 'wwagner@gmail.com', 'daniel79@gmail.com', 'ujackson@gmail.com']
filter 함수
filter는 파이썬의 내장 함수로 여러 개의 데이터로부터 일부의 데이터만 추려낼 때 사용 가능하다. 따라서, 여러 개의 데이터를 담고있는 list나 tuple을 대상으로 주로 사용한다.
filter(조건 함수, 순회 가능한 데이터)
filter 함수는 두번째 인자로 넘어온 데이터 중에서 첫번째 인자로 넘어온 조건을 만족하는 데이터만 반환한다.
위의 실습 코드를 재활용하여 코드를 짤 수 있다.
from functools import reduce
users = [{'mail': 'gregorythomas@gmail.com', 'name': 'Brett Holland', 'sex': 'M', 'age': 73},
{'mail': 'hintoncynthia@hotmail.com', 'name': 'Madison Martinez', 'sex': 'F', 'age': 29},
{'mail': 'wwagner@gmail.com', 'name': 'Michael Jenkins', 'sex': 'M', 'age': 51},
{'mail': 'daniel79@gmail.com', 'name': 'Karen Rodriguez', 'sex': 'F', 'age': 32},
{'mail': 'ujackson@gmail.com', 'name': 'Amber Rhodes', 'sex': 'F', 'age': 42}]
def is_man(user):
return user["sex"] == "M"
for man in filter(is_man, users):
print(man)
조건을 지정할 함수를 만들고 함수를 filter의 첫번째 인자로 전달, list를 두번째 인자에 전달해서 filter를 호출할 수 있다.
reduce 처럼 간단한 조건문이라면 lambda 함수를 사용하여 작성할 수도 있다.
기본 동작을 확인하고서 프로젝트의 코드를 파이썬의 코드로 바꾸는 작업을 진행하였다.
아래는 node에서 사용된 코드이다. arr, key가 인자로 전달되는 함수 안의 내용이고 arr를 순환하면서 key에 해당하는 값이 서로 중복인지 확인 후 중복이면 return하지 않고 제일 처음 값만 새로운 배열에 넣어서 만들어진 최종 배열을 리턴하는 코드이다.
node의 장점인 화살표 함수의 내용이 이해가 안되고 어떻게 동작하는 건지 확인이 불가하여 개념 공부를 하고 테스트를 거쳐 해당 코드를 이해할 수 있게 되었다.
arr.reduce((p,c) => {
if(p.filter(i => i === c[key]).length === 0) p.push(c[key])
return p
}, [])
이 코드는 결론적으로 중복을 제거하는 코드이기 때문에 위에서 알아본 filter 함수는 사용하지 않고 파이썬의 set 함수를 사용하게 되었다.
def test(arr, key):
a = reduce(lambda acc, cur: acc + [cur[key]], arr, [])
print(list(set(a)))
몇줄 안 되는 코드를 이해하는데 로직을 공부하면서 애를 많이 썼지만 로직을 이해하고 나니 조금 더 간결한 코드를 짤 수 있게 되었다.
'study > Python 🌼' 카테고리의 다른 글
[Python] 월 단위의 날짜 차이 계산 (0) | 2021.07.05 |
---|---|
[Python] Mac SSLCertVerificationError (0) | 2021.06.30 |
[Python] dataclass 모듈 사용법 (1) | 2021.05.28 |
[Python] SQLAlcehmy (0) | 2021.05.28 |
[Python] 암호화를 하는 bcrypt (0) | 2021.05.28 |