betterway14sort함수

김승환·2021년 7월 11일

코딩의 기술

목록 보기
5/36

복잡한 기준을 사용해 정렬할 때는 key 파라미터를 사용하라

# list 에는 sort함수를 이용하여 오름차순 내림차순을 만들 수 있음
numbers = [93, 86, 11, 68, 70]
numbers.sort()
print(numbers)

[11, 68, 70, 86, 93]

# sort의 기준이 여러개 인경우 

class Tool:
    def __init__(self, name, weight):
        self.name = name
        self.weight = weight

    def __repr__(self):
        return f'Tool({self.name!r}, {self.weight})'
#아래 예시는 sort를 통해 오름차순을 했을 때 특별한 기준이 없기 때문에 에러가 발생한다.

tools = [
    Tool('수준계', 3.5),
    Tool('해머', 1.25),
    Tool('스크류드라이버', 0.5),
    Tool('끌', 0.25),
]
tools.sort()

TypeError Traceback (most recent call last)
in
7 Tool('끌', 0.25),
8 ]
----> 9 tools.sort()

TypeError: '<' not supported between instances of 'Tool' and 'Tool'

- 정렬에 사용하고 싶은 애트리뷰트가 객체에 들어있는 경우

  • 이런 상황을 지원하기 위해 sort에 key라는 파라미터가 있으며 key는 함수여야 한다.
  • key 함수 에는 정렬 중인 리스트 원소가 전달된다.
  • key 값은 정렬기준으로 사용할, 비교 가능한 값이어야만 한다.
# lambda에 오름차순
tools = [
    Tool('수준계', 3.5),
    Tool('해머', 1.25),
    Tool('스크류드라이버', 0.5),
    Tool('끌', 0.25),
]

print('미정렬:', repr(tools))
tools.sort(key=lambda x: x.name)
# lambda를 사용하여 name이라는 key를 기준으로 오름차순으로 정렬
print('\n정렬: ', tools)

미정렬: [Tool('수준계', 3.5), Tool('해머', 1.25), Tool('스크류드라이버', 0.5), Tool('끌', 0.25)]

정렬: [Tool('끌', 0.25), Tool('수준계', 3.5), Tool('스크류드라이버', 0.5), Tool('해머', 1.25)]

#람다에 무게를 오름차순으로
tools.sort(key=lambda x: x.weight)
print('무게순 정렬:', tools)

무게순 정렬: [Tool('끌', 0.25), Tool('스크류드라이버', 0.5), Tool('해머', 1.25), Tool('수준계', 3.5)]

정렬할 때 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']

여러 기준 정렬의 상황

  • ex 전동 공구 정보가 들어 있는 리스트가 있는데 weight로 먼저 정렬하고 name으로 정렬하고 싶을때
    - tuple 타입을 사용 (불변 값)
    - 튜플은 비교가 가능하며 sort에 필요한 It정의가 들어있다.
  • 아래 예시는 튜플의 각 위치를 이터레이션하면서 각 인덱스에 해당하는 원소를 한 번에 하나씩 비교하는 방식으로 구현돼 있다.
#튜플을 이용한 예시
power_tools = [
    Tool('드릴', 4),
    Tool('원형 톱', 5),
    Tool('착암기', 40),
    Tool('연마기', 4),
]
  saw = (5, '원형 톱')
jackhammer = (40, '착암기')
assert not (jackhammer < saw) # 예상한 대로 결과가 나온다 (Flase)
print(jackhammer < saw)

False

  • 위 예시와 같이 비교 두 튜플에서
    - 첫 번째 위치 값이 서로 같으면, 두 번째 위치를 비교
    - 두 번째 위치 값이 같으면 세 번째 ...
# 위 내용을 증명하는 코드
drill = (4, '드릴')
sander = (4, '연마기')
assert drill[0] == sander[0] # 무게가 같다
assert drill[1] < sander[1]  # 알파벳순으로 볼 때 더 작다
assert drill < sander        # 그러므로 드릴이 더 먼저다
print(drill[0] == sander[0])
print(drill[1] < sander[1])
print(drill < sander   )

True
True
True

튜플 비교 동작 방식 활용으로 sort를 이용하여 우선순위를 변경할 수 있다.

- 여기까지의 방식에서 제약식은 모두 오름차순이거나 내림차순이어야 한다. 
#weight을 먼저 오름차순으로 비교하고 name을 다음 오름차순으로 비교
power_tools = [
    Tool('드릴', 4),
    Tool('원형 톱', 5),
    Tool('착암기', 40),
    Tool('연마기', 4),
]
power_tools.sort(key=lambda x: (x.weight, x.name))
print(power_tools)

[Tool('드릴', 4), Tool('연마기', 4), Tool('원형 톱', 5), Tool('착암기', 40)]

# 모두 오름차순 or 내림차순
power_tools.sort(key=lambda x: (x.weight, x.name),
                 reverse=True) # 모든 비교 기준을 내림차순으로 만든다
print(power_tools)

[Tool('착암기', 40), Tool('원형 톱', 5), Tool('연마기', 4), Tool('드릴', 4)]

모두 오름차순 or 내림차순을 해결할 수 있는 방법으로 - 연산자를 사용한다.

  • 하지만 reverse와 -연산자를 섞으면 불가능
power_tools.sort(key=lambda x: (-x.weight, x.name))
print(power_tools)

[Tool('착암기', 40), Tool('원형 톱', 5), Tool('드릴', 4), Tool('연마기', 4)]

#문자열은 역순이 불가능함
power_tools.sort(key=lambda x: (-x.weight, -x.name))
print(power_tools)

NameError Traceback (most recent call last)
in
----> 1 power_tools.sort(key=lambda x: (-x.weight, -x.name))
2 print(power_tools)

NameError: name 'power_tools' is not defined

# reverse와 -연산자를 섞으면 불가능
power_tools.sort(key=lambda x: (x.weight, -x.name),
                 reverse=True)

TypeError Traceback (most recent call last)
in
1 # reverse와 -연산자를 섞으면 불가능
----> 2 power_tools.sort(key=lambda x: (x.weight, -x.name),
3 reverse=True)

in (x)
1 # reverse와 -연산자를 섞으면 불가능
----> 2 power_tools.sort(key=lambda x: (x.weight, -x.name),
3 reverse=True)

TypeError: bad operand type for unary -: 'str'

sort를 두번 호출하는 방법으로 정렬을 해도 된다. (안정적인 정렬 알고리즘 제공)

#두번의 호출로 정렬된 모습
power_tools.sort(key=lambda x: x.name)   # name 기준 오름차순
power_tools.sort(key=lambda x: x.weight, # weight 기준 내림차순
                 reverse=True)
print(power_tools)

Tool('착암기', 40), Tool('원형 톱', 5), Tool('드릴', 4), Tool('연마기', 4)]

최종적으로 리스트에서 얻어내고 싶은 정렬 기준 우선순위의 역순으로 정렬을 수행해야 한다.

  • 예를 들면 weight을 먼저 내림차순하고 name을 오름차순으로 정렬된 리스트를 원할 경우
  • name을 오름차순으로 먼저 정렬하고 weight을 내림차순으로 정렬하는 방법으로 수행해야한다.
# lamda에 길이순
tools = [
    Tool('수준계', 3.5),
    Tool('해머', 1.25),
    Tool('스크류드라이버', 0.5),
    Tool('끌', 0.25),
]

print('미정렬:', repr(tools))
tools.sort(key=lambda x:len(x.name))
# lambda를 사용하여 name이라는 key를 기준으로 길이순으로 정렬
print('\n정렬: ', tools)

미정렬: [Tool('수준계', 3.5), Tool('해머', 1.25), Tool('스크류드라이버', 0.5), Tool('끌', 0.25)]

정렬: [Tool('끌', 0.25), Tool('해머', 1.25), Tool('수준계', 3.5), Tool('스크류드라이버', 0.5)]

profile
인공지능 파이팅!

0개의 댓글