이 글의 작성자는 C/C++ 프로그래밍을 하던 사람이다. 이 글은 Python을 복습하며 작성하는 글이니, 부족한 부분이 있으면 얼마든지 피드백을 주시기 바란다.

 

리스트 정렬하기

때로는 리스트 안에 있는 데이터들을 특정 규칙대로 정렬하고 싶을 때가 생긴다. 아래와 같은 리스트가 있다고 생각해보자.

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

a에 들어있는 숫자들은 딱히 규칙이 없어보인다. 이 숫자들을 오름차순(작은 수부터 큰 수대로, 즉 뒤로 갈 수록 커지도록) 또는 내림차순(큰 수부터 작은 수대로, 즉 뒤로 갈 수록 작아지도록)으로 정렬을 하려면 어떻게 해야 할까?

두 가지 방법이 있다. 각각의 방법에 대해 알아보자.

  • 리스트의 기능, sort() 사용하기

리스트의 기능 중에는 sort()라는 것이 있다. sort의 뜻은 정렬이다. 그러면 위의 a 리스트로 sort()를 사용해보자.

>>> a = [9,2,4,1,6,7,3,5,8]
>>> a.sort()
>>> a
[1, 2, 3, 4, 5, 6, 7, 8, 9]

리스트 a가 오름차순으로 정렬된 것을 확인할 수 있다. 그러면 내림차순으로 정렬을 하려면 어떻게 해야 할까? 아래의 코드를 보자.

>>> a = [9,2,4,1,6,7,3,5,8]
>>> a.sort(reverse=True)
>>> a
[9, 8, 7, 6, 5, 4, 3, 2, 1]

sort안에 reverse=True라는 것을 집어넣었다. 이게 들어가면 정렬은 내림차순으로 된다. reverse는 뒤집다 라는 뜻을 가지고 있다. 즉, 원래 정렬 순서(오름차순)의 반대로 정렬하라는 뜻으로 이해할 수 있을 것이다.

reverse=True 형태는 키워드 인자라고 부르는 것인데, 나중에 함수에 대해 배울 때 다루게 될 내용이다.

  • sorted() 함수 사용하기

리스트 자체의 기능은 아니지만, sorted()라는 함수가 있다. sort에서 ed가 뒤에 붙었다. 대충 해석하자면 ‘정렬된’ 이라고 읽을 수 있다. 그러면 sorted()는 어떻게 써야 하는 걸까?

sorted(list)

sorted 안에 정렬하고자 하는 리스트를 집어넣으면 된다. 그러면 한 번 써보자. 이번엔 내림차순으로 정렬하는 것도 같이 해 보자.

>>> a = [9,2,4,1,6,7,3,5,8]
>>> sorted(a)
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> sorted(a, reverse=True)
[9, 8, 7, 6, 5, 4, 3, 2, 1]

결과물 역시 정렬된 리스트를 돌려주고 있다. 그러면 ‘도대체 뭐가 다른거지?’ 라는 생각이 들 수 있지만, 이미 결과물에서 차이가 보인다.

a.sort()를 했을 때와 sorted(a)를 했을 때의 결과를 보자. a.sort()는 아무 것도 출력이 되지 않았지만, sorted(a)는 정렬된 리스트가 출력되었다. 동작 결과물이 나오느냐 안나오느냐의 차이가 이미 보이고 있다.

그리고 또 한가지 차이점이 있다. sorted(a)를 하고 난 후, a의 리스트는 어떤 상태일까? 한번 출력해보자.

>>> a
[9, 2, 4, 1, 6, 7, 3, 5, 8]

분명히 정렬을 한 것 같은데 정렬이 되어 있지 않다. 게다가 정렬된 리스트 결과물도 분명히 봤는데, 무슨 일이 일어난 걸까?

sorted()는 원본 리스트는 그대로 둔 채, 입력받은 리스트의 정렬된 결과를 돌려주는 역할을 수행하는 함수이기 때문이다. 원본은 그대로 둔 채 정렬된 결과만 얻고 싶을 때 사용하는 것이 바로 sorted()이다.

a = sorted(a)를 하면 a.sort()를 했을 때와 같은 형태의 리스트를 얻을 수 있을 것이다. 하지만 a = sorted(a)를 쓸 바엔 a.sort()를 사용하자. 굳이 자기자신을 정렬하는데 리스트를 하나 더 만들 필요는 없기 때문이다.

그리고 sort()와 sorted()에는 또 하나의 차이점이 보일 것이다. sort()를 쓰기 위해선 앞에 리스트 객체의 이름에 점을 붙여야 했는데, sorted()는 괄호 안에 리스트 객체의 이름을 집어넣어야 했다. 이건 무슨 차이일까?

결론부터 말하자면 함수메서드의 차이다. 하지만 이 내용에 대해 설명하기에는 아직 진도가 그 만큼 나가지 않았다. 메서드는 객체에 들어있는 함수라고 기억하고 넘어가자. Python의 객체에 대해 본격적으로 다룰 때 설명하게 될 내용이다.

정수만 정렬이 가능한 것이 아니다. 부동소수점수도 정렬이 가능하고, 문자열도 정렬이 가능하다! 문자열을 정렬하게 된다면, 사전순으로 정렬이 되는 것을 확인할 수 있을 것이다.

>>> a = ['hello', 'apple', 'zebra', 'bread']
>>> a.sort()
>>> a
['apple', 'bread', 'hello', 'zebra']

나중에 알고리즘을 배우게 된다면 직접 정렬하는 코드를 작성하게 될 것이다.

 

리스트 복사/할당하기

아래 코드를 확인해보자.

>>> a = [1,2,3]
>>> b = a
>>> a[-1] = 4

변수 a에 [1,2,3]이 담긴 리스트를 대입했다.
그리고 변수 b에 변수 a를 대입했다.
마지막으로 리스트 a의 마지막 데이터를 4로 바꿨다.
여기서 리스트 b는 어떻게 생겼을까?

>>> b
[1, 2, 4]

b에 대해서는 아무것도 건드린게 없는데, b가 가리키는 리스트도 값이 바뀌었다. 왜 그럴까?

b = a가 의미하는 것은 a가 보고 있는 것을 나도 보겠다 라는 것을 의미한다. a가 보고 있는 리스트의 값을 바꿨으니, b가 보고 있는 리스트에도 영향이 가는 것이 당연하다. 똑같은 리스트를 보고 있으니까.

list

물론 위와 같이 한 쪽이 바뀌면 다른 쪽도 바뀌는 동작을 원하는 경우가 있을 수도 있지만, ab는 별개의 리스트로 관리하고 싶을 때도 있을 것이다. 이럴 때 쓰는 것이 바로 복사(copy)다. 똑같은 리스트를 만들지만, 둘은 서로 다른 리스트가 되는 것이다.

리스트를 복사하는 방법도 여러 가지가 존재한다.]

  • copy() 사용하기

list의 기능(즉, 메서드)으로 copy()가 있다. 자기 자신의 리스트와 똑같이 생긴 리스트를 돌려주는 역할을 수행한다.

>>> a = [1,2,3]
>>> b = a.copy()
>>> b
[1, 2, 3]
  • list() 사용하기

튜플이나 리스트를 변환할 때 list()를 사용한다고 했었다. 그리고 list() 안에는 똑같이 리스트를 집어넣을 수 있다. 주어진 리스트를 토대로 리스트를 만든다는 것은, 사실상 복사하는 동작이 된다.

>>> a = [1,2,3]
>>> b = list(a)
>>> b
[1, 2, 3]
  • 리스트의 일부를 가져오는 기능을 사용하기

a[:3]을 이용하여 리스트 a의 0번째 인덱스부터 2번째 인덱스까지를 포함한 리스트가 나온다는 것을 익힌 바가 있다. 그러면 a[:]를 사용하면? 리스트 a의 처음부터 마지막까지를 포함한 리스트가 나올 것이다. 이는 사실상 복사하는 동작이 된다.

>>> a = [1,2,3]
>>> b = a[:]
>>> b
[1, 2, 3]

 

그러면 실제로 리스트 a와 리스트 b가 다른 리스트인지 어떻게 확인할 수 있을까? id() 함수를 통해 객체의 고유 값을 확인하거나, is 연산자로 두 변수가 같은 객체를 가리키고 있는지 확인하면 된다.

>>> a = [1,2,3]
>>> b = a
>>> c = a.copy()
>>> id(a)
56869960
>>> id(b)
56869960
>>> id(c)
52174888
>>> a is b
True
>>> a is c
False

id 값은 실행할 때 마다 달라지므로, id 함수를 썼는데 왜 같은 번호가 나오지 않냐고 당황하지 말자. 중요한건 a,b,c의 id 값이 같은가 다른가이다.

is 연산자에 대해선 나중에 다시 다루도록 한다. 혼동할 수도 있는 연산자가 있기 때문이다.

 

언팩(unpack)

마지막으로 리스트/튜플의 언팩에 대해 알아보자. 언팩은 리스트/튜플의 데이터들을 각각 변수에 대입해주는 기능이다. 다음 코드를 보면 이해가 될 것이다. 이 코드는 리스트가 아니라 튜플로 해도 동작한다.

>>> a = [1,2,3]
>>> q,w,e = a
>>> q
1
>>> w
2
>>> e
3

q, w, e 3개의 변수에 리스트를 대입하였더니, 각각의 변수가 a 리스트의 데이터를 하나씩 가리키고 있는 것을 확인할 수 있다. 변수의 이름을 선언한 순서대로 0번째 인덱스, 1번째 인덱스, 그리고 2번째 인덱스의 데이터를 가리키고 있다는 사실도 알 수 있다.

이 동작은 리스트의 데이터 개수와 변수의 개수가 정확히 일치해야만 한다. 서로 개수가 맞지 않는다면 에러가 발생한다.

>>> a = [1,2,3]
>>> q,w = a
Traceback (most recent call last):
  File "<pyshell#124>", line 1, in <module>
    q,w = a
ValueError: too many values to unpack (expected 2)

>>> q,w,e,r = a
Traceback (most recent call last):
  File "<pyshell#125>", line 1, in <module>
    q,w,e,r = a
ValueError: not enough values to unpack (expected 4, got 3)

 

Categories:

Updated:

Comments