[Python] 정렬 메서드 sort()에 관한 모든 것
파이썬에는 sort()라는 list 자료형 메서드가 존재한다. (sorted 함수는 다른 포스팅으로 다루겠다)
자주 사용하지만, 제대로 정리할 필요는 있을 거 같아 이 포스팅으로 정리한다.
1. sort()
sort() 메서드는 list 자료형의 메서드중 하나로, list를 제자리에서 정렬을 하는 역할을 한다. 원래의 list를 수정하므로 파괴적 메서드이다.
다른 말로, sorting이 가능한 자료형이 list안에 담겨있고, 각 자료형 간 비교가 가능하다면 (쉽게 말해, '<' 을 적용할 수 있으면) sort 메서드를 사용할 수 있다.
반대로 말하면, 비교 연산이 가능하지 않거나 비교에 실패하였다면, sort 연산은 실패하게 된다. 이때 실패 지점 이전까지 한 정렬은 수정된 상태로 남는다.
1 2 3 4 5 6 7 8 9 10 | nums = [1, 3, 5, 2, 4] nums.sort() # nums = [1, 2, 3, 4, 5] nums = [1, 2, 5, 4, (1, 2)] # 오류! tuple과 int형은 비교할 수 없다. # 다만, tuple형을 비교하기 전까지의 수행된 정렬은 nums에 남아있다. nums.sort() | cs |
sort 메서드의 반환값은 None이다. sort 메서드 자체가 list를 수정하기에, 원래 list가 필요하다면 sorted를 사용해야 한다.
1 2 3 4 5 6 | nums = [3, 2, 1] print(nums.sort()) # None을 반환 print(nums) # 정렬이 수행되었음 # [1, 2, 3] | cs |
sort 메서드의 기본적으로 두 개의 키워드로 전달할 수 있는 인자가 있다. 두 인자 모두 키워드-전용(keyword-only) 인자이다.
sort(*, key = None, reverse = False)
1) key = None
key 인자에는 비교 기준을 추가할 수 있다. key에 인자를 넣어준다면 sort가 적용될 list에서 key를 기준으로 정렬을 수행하게 된다. 이때 key는 한 번만 계산하게 된다.
기본값은 None으로, python이 기본적으로 제공하는 비교 연산자('<') 순서대로 정렬을 수행한다.
다음은 key에 str.lower를 인자로 넘겨준 예시이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 | words = ['G', 'a', 'C', 'D', 'b', 'E', 'f'] words.sort() print(words) # ASCII 코드 순서대로 정렬을 수행한다. # ['C', 'D', 'E', 'G', 'a', 'b', 'f'] words.sort(key = str.lower) print(words) # 모든 문자를 소문자로 바꾸고 정렬을 수행한다. # ['a', 'b', 'C', 'D', 'E', 'f', 'G'] | cs |
또는 key에 직접 함수를 생성하여 넣거나, 람다식을 활용하여 정렬을 수행할 수 있다. 만약, 여러 기준으로 정렬을 수행하고 싶다면, 람다식을 튜플로 묶거나 별도의 함수를 작성하여 여러 번 정렬을 수행할 수 있다.
다음은 key에 람다식을 넣어 활용한 예시이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | tuples = [ (1, 100, 49), (2, 97, 65), (3, 85, 30), (4, 62, 30) ] tuples.sort(key = lambda x : x[1]) print(tuples) # tuple의 두 번째 정수를 기준으로 정렬을 수행한다. # [(4, 62, 30), (3, 85, 30), (2, 97, 65), (1, 100, 49)] tuples.sort(key = lambda x : (x[2], x[1])) print(tuples) # tuple의 세 번째 정수를 기준으로 정렬을 수행하고, # 만약 우선순위가 같은 항목이 있다면 두 번쨰 정수를 기준으로 정렬을 수행한다. # [(4, 62, 30), (3, 85, 30), (1, 100, 49), (2, 97, 65)] | cs |
혹은, class에서의 attribute를 기준으로 정렬을 수행할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | class Student: def __init__(self, name, grade, age): self.name = name self.grade = grade self.age = age def __repr__(self): return repr((self.name, self.grade, self.age)) students = [ Student("alice", 1, 16), Student("bob", 3, 20), Student("cameron", 2, 25) ] students.sort(key = lambda x: x.age) print(students) # Student 의 age에 따라 정렬 수행 # [('alice', 1, 16), ('bob', 3, 20), ('cameron', 2, 25)] | cs |
혹은, operator 모듈을 활용하면 더욱 쉽고 빠르게 정렬을 수행할 수 있다. 이때 함수 itemgetter(), attrgetter(), methodcaller()를 활용할 수 있다.
위에서 사용한 람다식 대신 key에 itemgetter, attrgetter를 활용하면 다음과 같이 작성할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | import operator class Student: def __init__(self, name, grade, age): self.name = name self.grade = grade self.age = age def __repr__(self): return repr((self.name, self.grade, self.age)) tuples = [ (1, 100, 49), (2, 97, 65), (3, 85, 30), (4, 62, 30) ] students = [ Student("alice", 1, 16), Student("bob", 3, 20), Student("cameron", 2, 25) ] # 위 함수와 아래 함수 모두 같은 역할을 수행하는 함수이다. #tuples.sort(key = lambda x : x[1]) tuples.sort(key = operator.itemgetter(1)) print(tuples) #tuples.sort(key = lambda x : (x[2], x[1])) tuples.sort(key = operator.itemgetter(2, 1)) print(tuples) #students.sort(key = lambda x: x.age) students.sort(key = operator.attrgetter('age')) print(students) | cs |
2) reverse = False
reverse에는 역순 정렬 여부에 관한 인자이다. 만약 False일 경우, 정렬이 오름차순으로 진행된다. 만약 True일 경우, 정렬이 내림차순으로 진행된다.
1 2 3 4 5 6 | words = ['G', 'a', 'C', 'D', 'b', 'E', 'f'] words.sort(key = str.lower, reverse = True) print(words) # 역순 정렬이 수행횐다. # ['G', 'f', 'E', 'D', 'C', 'b', 'a'] | cs |
2. sort()의 여러 특징
1.
python의 sort는 기본적으로 stable함을 보장한다. 이는, sort가 수행될 때 sort의 기준이 되는 항목 이외의 순서는 기존의 순서가 유지된다는 의미이다.
다음 예시에서, list안의 원소에서 정렬되는 기준 이외에는 본래 순서를 유지하는지 확인하여보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 | arr = [ ('a', 1), ('b', 1), ('a', 2), ('b', 2), ('c', 3) ] arr.sort(key = lambda x: x[0]) print(arr) # 기준이 되는 첫번째 원소를 제외하고는 본래 순서 유지 # [('a', 1), ('a', 2), ('b', 1), ('b', 2), ('c', 3)] | cs |
이때, 두 원소 ('b', 1)과 ('b', 2)의 순서는 유지되고 있음을 확인할 수 있다.
2.
sort 메서드를 사용할 때, list 자체 내부에서의 객체에서 사용할 필요는 없다.
만약, 다음과 같이 정렬 기준이 dictionary 자료형에 저장되어 있다면, 이를 활용하여 기존 list를 정렬할 수 있다.
1 2 3 4 5 6 7 8 9 10 | students = ['Alice', 'Bob', 'Cameron'] grades = { 'Alice' : 'F', "Bob" : "A", "Cameron": "C" } students.sort(key = lambda x : grades[x]) # students.sort(key =grades.__getitem__) 도 가능하다. print(students) | cs |
3.
class 내에서 연산자 오버라이딩을 활용하면 정렬 수행 시 '<'의 우선순위를 정해줄 수 있다.
이 경우 별도의 key나 reverse에 인자를 전달할 필요가 없다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | class Student: def __init__(self, name, grade, age): self.name = name self.grade = grade self.age = age def __repr__(self): return repr((self.name, self.grade, self.age)) def __lt__(self, other): return self.age < other.age students = [ Student("alice", 1, 16), Student("bob", 3, 20), Student("cameron", 2, 25) ] students.sort() print(students) # 정렬 순서를 __lt__ ('<')를 오버라이딩하여 정해줌 # [('alice', 1, 16), ('bob', 3, 20), ('cameron', 2, 25)] | cs |
4.
python 내부에서는 sort의 방법으로 Timsort 알고리즘을 사용한다. (Tim Peters라는 사람이 알고리즘을 개발하였다.)
도움이 되었다면 지나가는 길에 하트 하나 눌러주세요, 양질의 글을 쓰는데 하나의 동기부여가 됩니다😍
지적이나 오타 수정 댓글 환영합니다!!