본문 바로가기
  • hazard_dev@__
  • hazard_dev@__
Python

[Python 자료구조] list_map, filter 등 리스트 응용!

by Hazard3_o00sung 2021. 1. 28.
728x90

강력한 스크립트 언어 파이썬 입니다!

Linear Structure on Python

  파이썬에서 가장 많이 사용되는 자료구조는 당연히 리스트일 겁니다. 그렇다면 왜 사용이 많이 되는지, 다른 언어와 비교할 때 어떤 장점이 있는지 알아보도록 하겠습니다. 우선 C언어의 배열을 살펴보면 아래와 같습니다. 

 

int arr[10] = {1,2,3,4,5};

 

이렇게 초기화해서 사용할 수 있습니다. 물론 포인터 변수로 받아서 값을 초기화하는 것도 가능하지만 현재는 C언어의 배열에 대해서 알아보는 것이 아니기 때문에 위 코드라인만 보여드렸습니다. 그렇다면 파이썬에서의 리스트는 어떻게 선언되고 혹은 초기화될까요!

 

arr = []
arr = [1,2,3,4,5]
..

 

위와 같은 방법으로 선언하거나 초기화가 가능합니다. C언어 같은 정적 언어와 비교해볼 때 큰 차이점은 정적 언어에서는 컴파일되기 전 배열의 크기를 알아야 하기 때문에 동적으로 파이썬이나 다른 언어처럼 자유롭게 선언 혹은 초기화해서 사용할 수 없습니다. 물론 크기를 알려주지 않아도 동적으로 할당되기는 하나, 메모리 관리를 위해서는 메모리에 적당량을 차지하는 것이 프로그램에 많은 도움이 되니까 크기를 명시합니다. 그러니까 파이썬에서의 리스트는 동적으로 메모리가 할당되며, 크기가 늘어나든, 줄어들든 사용자 레벨에서 고민할 필요가 전혀 없다는 거죠!

 

이 리스트에도 다양한 내장 함수들이 있습니다. 너무 많지만, 그 점을 모두 설명할 순 없기에! 강력한 내장 함수에 대해서만 설명을 드리려 합니다!! 우선 map에 대해서 알아보도록 하겠습니다.

 

MAP 

맵은 상당히 추상적인 형태의 함수입니다. 우선 사전적 의미를 먼저 알아야 합니다. 검색해보면 아래와 같이 나옵니다!

 

맵의 사전적 의미_ search on google

 

아래 4번째 단락을 보시면 "수학"이라고 돼있는 게 보입니다. 의미는 "세트의 각 요소를 다른 세트의 요소와 연관시킵니다"인데요, 이 사전적 의미만 잘 곱씹어보아도 이해는 끝납니다. 여기서 map에 대해서 알아간다면, 다른 언어에서도 map에 관한 전반적인 콘셉트는 잡고 갈 수 있습니다. 

 

즉, 두 그룹이 있고, 그룹 간 요소를 연관, 관계 맺는다 이런 의미인데요, 만약 이 점을 리스트에 응용하면 어떻게 될까요?

 

foo = [1,2,3,4,5]
bar = [6,7,8,9,10]

foo + bar --> ?

 

설마 위와 같이 더하기를 해보신 분 없으시겠죠? 물론 리스트 간 연결은 위 코드처럼 이항 연산을 통해서 연결시키는 것은 맞습니다. 하지만 리스트 요소 간 더하기는 저렇게 더하는 게 아니겠죠? 그렇다면 아래 코드를 보도록 하겠습니다.

 

foo = [1,2,3,4,5]
bar = [6,7,8,9,10]

newfoo = []
for x in range(0, len(foo)):
    newfoo.append(foo[x] + bar[x])

 

이렇게 더하면 안 될까 하시는 분들 계시지만, 안될 거 없죠. 다만 코드의 양이 늘어나고 굉장히 더럽다 이 말입니다. 그래서 map이란 내장 함수가 존재하는 거죠. 기초 문법은 아래와 같습니다.

 

map(<*procedure>, <*arg1>,<*arg2>,..)

 

첫 번째 인자로는 두 그룹 간 연산을 시킬 프로시저가 오고, 그다음으로는 그룹의 인자들을 받습니다. 들어온 인자들은 프로시저에 의해 데이터 간 사상이 이루어집니다. 이터레이션은 순서대로 진행되면, 리스트가 들어온 경우에는 어떻게 진행될까요? 위의 for구문처럼 순서대로 반복되며 연산이 이루어집니다. 아래 예제 코드를 보도록 하겠습니다.

 

import operator as op

foo = [1,2,3,4,5]
bar = [6,7,8,9,10]

newFoo = map(op.add, foo,bar)
print(list(newFoo))

 

내장 라이브러리 중 하나인 operator를 임포트 해서 사용한 예입니다. 만약 그냥 newFoo를 출력한다면 반복가능 객체로만 출력됩니다. 그렇기 때문에 리스트 형으로 명시적 형 변환을 이루어 준 후 사용을 하는 것이 맞습니다! 그렇다면 이런 이항 연산만 가능하냐 그런 것은 아닙니다. 개발자가 원하는 매핑이 있다면 함수를 만들어서 넘겨주는 것 또한 가능합니다.

 

foo = [1,2,3,4,5]
bar = [6,7,8,9,10]

def add(x, y):
    return x + y

newFoo = map(add, foo,bar)

newFoo2 = map(lambda x,y : x * y, foo,bar)
print(list(newFoo))
print(list(newFoo2))

 

람다 수식을 사용하는 것 또한 가능하며, 함수의 식별자를 넣어서 연산을 하는 것도 가능합니다. 인자의 개수만 맞추어 준다면, 여러 개도 이론적으론 들어갈 수 있습니다!! 

 

Filter

필터라는 단어는 뭐 많이들 사용해보셨죠? 하지만 이 단어 또한 사전적 의미를 찾아서 이해해보도록 하겠습니다.

 

필터의 사전적 의미 _ search on google

 

아시긴 하지만, 리마인드 해서 보도록 하겠습니다. 그러니까 여과기라는 건데요 프로그램에서의 여과는 어떤 의미일까요? 네, 데이터를 여과한다는 의미입니다. 그러니까 데이터를 어떤 여과기능을 통해서 데이터를 식별하는 겁니다. 위의 map함수처럼 추상적인 의미가 짙긴 하나, 예제 코드를 보면서 이해해보도록 하겠습니다. 

 

foo = [1,2,3,4,5,6,7,8,9,10]

bar = []
for x in foo:
    if x % 2 == 0:
        bar.append(x)

 

2의 배수를 찾기 위해서 이렇게 코드를 작성하는 것 또한 가능합니다. 그렇다면, 이렇게 사용하면 위에서 제가 말씀드렸듯이 코드가 쓸데없이 길어지며, 한 지역에서만 이렇게 선언하게 되면 재사용하는 것은 당연히 포기해야 하는 거죠. filter를 통해서 해결해보죠!

 

foo = [1,2,3,4,5,6,7,8,9,10]

def twoNum(x):
    return True if x % 2 == 0 else False 

bar = filter(twoNum, foo)
print(list(bar))

 

코드 또한 간결해지며, 재사용하기에 유리한 형태로 코드가 바뀐 것을 확인할 수 있습니다. 물론 위의 코드와 아래 코드가 무슨 차이냐 하실 수 있지만, 프로그램의 사이즈가 커질수록 코드를 작성한 지점을 잊게 되며, 최악의 상황엔 비슷한 함수를 재선 언하는 경우도 있다는 말이죠, 그렇기 때문에 확실한 함수 이름만 지어주면 재사용할 때 유리해지게 됩니다!!

 

이상 설명은 어느 정도 끝마쳤으나 좀 더 궁금한 점이 있으신 분은 댓글로 남겨주시기 바랍니다!!

 

이후에 리스트 제너레이션 등 좀 더 많은 내장 함수들을 작성해보도록 하겠습니다!!

 

감사합니다!!

 

댓글로 피드백, 질문 모두 환영합니다!!

728x90

댓글