
Python 리스트가 원하는 순서로 바뀌지 않거나, 원본을 보존해야 하는데 값이 사라지는 것처럼 보인다면 먼저 sort와 sorted의 차이를 확인하세요. 정렬이 끝난 정상 상태는 숫자·문자열이 지정한 기준에 맞게 배열되고, 필요한 경우 원본 리스트도 그대로 남아 있는 것입니다.
정렬 전에는 값의 자료형과 정렬 기준을 확인합니다. 숫자와 문자열을 섞어 비교하면 오류가 날 수 있고, None이 포함된 리스트는 기본 정렬만으로 처리되지 않습니다. 객체 리스트라면 객체의 어떤 속성을 기준으로 삼을지도 먼저 정해야 합니다.
문제를 해결하는 순서
- 원본 리스트 자체를 바꿔도 되는지 결정합니다. 바꿔도 되면 list.sort(), 보존해야 하면 sorted(list)를 선택합니다.
- 오름차순인지 내림차순인지 정하고, 필요하면 reverse=True를 추가합니다.
- 문자열이나 객체처럼 기본 비교가 적절하지 않은 값은 key 함수로 정렬 기준을 지정합니다.
- None이나 서로 다른 자료형이 섞여 있다면 먼저 None을 앞이나 뒤로 보낼 규칙을 코드로 명시합니다.
- 정렬 결과와 원본 보존 여부를 출력해 확인합니다.
아래 코드는 기본적인 숫자 정렬입니다. sort()는 리스트를 직접 바꾸고 반환값은 None이므로, result = numbers.sort()처럼 작성하지 않아야 합니다. 반대로 sorted()는 정렬된 새 리스트를 반환합니다.
numbers = [4, 1, 3, 2]
numbers.sort()
print(numbers) # [1, 2, 3, 4]
numbers = [4, 1, 3, 2]
sorted_numbers = sorted(numbers)
print(sorted_numbers) # [1, 2, 3, 4]
print(numbers) # [4, 1, 3, 2]
numbers.sort(reverse=True)
print(numbers) # [4, 3, 2, 1]
sort()는 반환값을 사용하지 않습니다. 정렬된 결과를 변수에 담으려면 sorted()를 사용하거나, sort() 실행 후 원래 리스트를 사용하세요.
문자열은 기본적으로 사전식 순서로 정렬됩니다. 대소문자를 구분하지 않으려면 key=str.lower처럼 기준을 지정합니다. 문자열 길이, 절댓값처럼 별도의 기준이 필요할 때도 key를 사용합니다.
words = ["Banana", "apple", "cherry"]
print(sorted(words, key=str.lower))
# ['apple', 'Banana', 'cherry']
values = [-10, 3, -2, 1]
print(sorted(values, key=abs))
# [1, -2, 3, -10]
words.sort(key=len)
print(words)
객체 리스트는 key에 속성을 반환하는 함수를 넘기면 됩니다. 아래 예제에서는 학생의 score를 기준으로 정렬하고, reverse=True로 높은 점수부터 배치합니다. 여러 조건은 key가 튜플을 반환하도록 작성하면 됩니다.
students = [
{"name": "민수", "score": 90, "age": 20},
{"name": "지영", "score": 90, "age": 19},
{"name": "철수", "score": 80, "age": 21},
]
# 점수 내림차순
by_score = sorted(students, key=lambda student: student["score"], reverse=True)
print(by_score)
# 점수 내림차순, 같은 점수면 나이 오름차순
by_score_and_age = sorted(
students,
key=lambda student: (-student["score"], student["age"])
)
print(by_score_and_age)
문자열 객체나 클래스 인스턴스에서는 operator.itemgetter 또는 attrgetter를 사용하면 기준이 더 분명해질 수 있습니다.
from operator import itemgetter, attrgetter
records = [
{"name": "B", "score": 80},
{"name": "A", "score": 90},
]
print(sorted(records, key=itemgetter("score"), reverse=True))
class Product:
def __init__(self, name, price):
self.name = name
self.price = price
products = [Product("노트", 3000), Product("펜", 1000)]
products.sort(key=attrgetter("price"))
print([product.name for product in products])
None은 숫자나 문자열과 직접 비교할 수 없으므로, None을 항상 뒤로 보내는 기준을 따로 작성해야 합니다. 값이 None인지 여부를 첫 번째 기준으로 두면 실제 값은 None이 아닌 항목끼리만 비교됩니다.
values = [3, None, 1, 2, None]
none_last = sorted(values, key=lambda value: (value is None, value if value is not None else 0))
print(none_last) # [1, 2, 3, None, None]
# 문자열에서도 같은 방식으로 None을 뒤로 보냅니다
names = ["지영", None, "민수", "철수"]
names_sorted = sorted(names, key=lambda name: (name is None, name or ""))
print(names_sorted)
정상 작동 확인하기
정렬 후에는 결과만 보지 말고, 원본 변경 여부와 정렬 기준까지 함께 확인합니다. 특히 sort()를 사용한 경우 반환값이 아니라 리스트 자체를 출력해야 합니다.
original = [4, 1, 3, 2]
result = sorted(original)
assert result == [1, 2, 3, 4]
assert original == [4, 1, 3, 2] # 원본 보존
assert all(result[i] <= result[i + 1] for i in range(len(result) - 1))
mutable = [4, 1, 3, 2]
return_value = mutable.sort()
assert mutable == [1, 2, 3, 4]
assert return_value is None
print("정렬과 원본 보존 여부가 모두 예상대로입니다.")
자주 막히는 지점
TypeError: ‘<‘ not supported between instances of ‘NoneType’ and ‘int’가 발생하면 None이 포함된 상태에서 기본 정렬을 시도한 것입니다. 위의 None 처리용 key를 사용하세요. 객체 정렬에서 오류가 나면 key 함수가 실제 속성명이나 딕셔너리 키를 올바르게 가리키는지 확인합니다. 결과 순서가 반대라면 reverse=True 또는 튜플 key 안의 부호를 점검하세요.
정리하면 원본을 바꿀 때는 sort(), 새 결과가 필요할 때는 sorted()를 사용하고, 객체·문자열·None·다중 조건처럼 기준이 분명해야 하는 경우 key를 먼저 설계하면 됩니다. 마지막으로 작은 입력 예제와 assert를 실행해 원하는 순서와 원본 상태를 함께 확인하세요.