14. 복잡한 기준을 사용해 정렬할 때는 key 파라미터를 사용하라.
list 내장 타입에는 리스트의 원소를 여러 기준에 따라 정렬할 수 있는 sort 메서드가 들어 있다.
기본적으로 오름차순으로 정렬한다.
numbers = [93, 86, 11, 68, 70]
numbers.sort()
print(numbers)
>>>
[11, 68, 70, 86, 93]
sort 메서드는 자연스럽게 순서를 정할 수 있는 거의 대부분의 내장 타입(문자열, 부동소수점)에 잘 작동한다.
__repr__ 메서드는 클래스 인스턴스를 출력
ex)
class Tool:
def __init__(self, name, weight):
self.name = name
self.weight = weight
def __repr__(self):
return f'Tool({self.name}, {self.weight})'
tools = [
Tool('수준계', 3.5),
Tool('해머', 1.25),
Tool('드라이버', 0.5),
Tool('끌',0.25)
]
tools.sort()
>>>
TypeError: '<' not supported between instances of 'Tool' and 'Tool'
정렬하고 싶은 애트리뷰트가 객체에 들어있는 경우 sort를 위해 key라는 파라미터가 있다.
key는 함수여야 한다.
key 함수에는 정렬 중인 리스트의 원소가 전달된다.
key 함수가 반환하는 값은 원소 대신 정렬 기준으로 사용할, 비교 가능한 값이여야한다.
- 이름을 기준으로 정렬 시
# sort
print("미정렬: ", repr(tools))
tools.sort(key=lambda x: x.name)
print("정렬: ",tools)
>>>
미정렬: [Tool(수준계, 3.5), Tool(해머, 1.25), Tool(드라이버, 0.5), Tool(끌, 0.25)]
정렬: [Tool(끌, 0.25), Tool(드라이버, 0.5), Tool(수준계, 3.5), Tool(해머, 1.25)]
key로 전달된 람다 함수 내부에서는 원소 애트리뷰트에 접근하거나 인덱스를 써서 값을 얻거나 (원소가 시퀀스, 튜플, 딕셔너리인 경우) 제대로 작동하는 모든 식을 사용할 수 있다.
심지어 문자열 같은 기본 타입의 경우에는 정렬하기 전에 key함수를 사용해 원소 값을 변경할 수 도 있다.
places = ['home', 'work', 'New York', 'Paris']
places.sort()
print("대소문자 구분: ", places)
places.sort(key=lambda x: x.lower())
print("대소문자 무시: ", places)
>>>
대소문자 구분: ['New York', 'Paris', 'home', 'work']
대소문자 무시: ['home', 'New York', 'Paris', 'work']
때로는 여러 기준을 사용해 정렬해야 할 수도 있다.
예를 들면 '어떤 도구'에서 weight로 먼저 정렬한 후 name으로 정렬하고 싶다면?
tuple로 해결을 한다.
튜플은 임의의 파이썬 값을 넣을 수 있는 불변 값이다.
어떤 도구가 다른 도구보다 무거울 때 어떤일이 벌어날까?
saw = (5, '원형톱')
hammer = (50, '해머')
assert not (hammer < saw)
class Tool:
def __init__(self, name, weight):
self.name = name
self.weight = weight
def __repr__(self):
return f'Tool({self.name}, {self.weight})'
ptools = [
Tool('드릴', 4),
Tool('원형 톱', 5),
Tool('착암기', 20),
Tool('연마기', 4)
]
ptools.sort(key=lambda x:(x.weight, x.name))
print(ptools)
>>>
[Tool(드릴, 4), Tool(연마기, 4), Tool(원형 톱, 5), Tool(착암기, 20)]
역순을 원한다면 reverse=True를 sort안에 넣어주자
그런데 만약 weight 내림차순으로 정렬하고 name은 오름차순으로 정렬하고 싶다면?
ptools.sort(key=lambda x:(-x.weight, x.name))
print(ptools)
>>>
[Tool(착암기, 20), Tool(원형 톱, 5), Tool(드릴, 4), Tool(연마기, 4)]
하지만 x.weight, -x.name은 에러가 날 것이다.
리스트 타입의 sort 메서드는 key 함수가 반환하는 값이 서로 같은 경우 리스트에 들어 있던 원래 순서를 그대로 유지해준다.
이는 같은 리스트에 대해 서로 다른 기준으로 sort를 여러 번 호출해도 된다는 뜻이다.
ptools.sort(key=lambda x:x.name)
print(ptools)
ptools.sort(key=lambda x:x.weight, reverse=True)
print(ptools)
>>>
[Tool(드릴, 4), Tool(연마기, 4), Tool(원형 톱, 5), Tool(착암기, 20)]
[Tool(착암기, 20), Tool(원형 톱, 5), Tool(드릴, 4), Tool(연마기, 4)]
- 리스트 타입에 들어 있는 sort 메서드를 사용하려면 원소 타입이 문자열, 정수, 튜플 등과 같은 내장 타입인 경우 자연스로운 순서로 리스트의 원소를 정렬할 수 있다.
- 원소 타입에 특별 메서드를 통해 자연스러운 순서가 정의되어 있지 않으면 sort 메서드를 쓸 수 없다.
- sort 메서드의 key 파라미터를 사용하면 리스트의 각 원소 대신 비교에 사용할 객체를 반환하는 도우미 함수를 제공할 수 있다.
- key 함수에서 튜플ㅇ르 반환하면 여러 정렬 기준을 하나로 엮을 수 있다.
- 부호를 바꿀 수 없는 타입의 경우 여러 기준으로 조합하려면 각 정렬 기준마다 reverser 값으로 정렬 순서를 지정하면서 sort 메서드를 여러 번 사용해야 한다.
본 블로그는 [파이썬 코딩의 기술] 서적을 참고하여 작성하였습니다.
'SW ENGINEERING > Python' 카테고리의 다른 글
가상환경 구축 (0) | 2021.08.26 |
---|---|
파이썬 코딩의 기술TIP 13일차 (0) | 2021.08.11 |
파이썬 코딩의 기술TIP 12일차 (0) | 2021.08.02 |
파이썬 코딩의 기술TIP 11일차 (0) | 2021.07.02 |
파이썬 코딩의 기술TIP 10일차 (0) | 2021.06.18 |
최근댓글