Batter way14

매일 공부(ML)·2022년 2월 7일
0

파이썬 코딩의 기술

목록 보기
13/27

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

sort

  • list 내장 타입에 들어있는 리스트의 원소를 여러 기준에 따라 정렬하는 메서드

  • 자연스러운 순서가 있어야하는 경우, 필요한 메서드를 정의하면 별도의 인자를 넘기지 않고 sort 가능

    • sort는 리스트의 내용을 원소 타입에 따른 오름차순으로 정렬

    • sort메서드는 내장타입(문자열, 부동소수점)등에 잘 작동

    • sort메서드가 호출하는 객체 비교 특별 메서드가 정의되지 않으면 객체 정렬 불가


*예시1: 자연스러운 순서로 오름차순 정렬

numbers = [93,86,11,68,70]
numbers.sort()
print(numbers) # [11,68,70,86,93]

*예시2: sort메서드 자연스럽게 순서 정하기(in 내장타입)

class Tool:
    def __init__(self, name, weight):
        self.name = name
        self.weight = weight
        
    def __repr__(self):
        return f'Tool({self.name!r}, {self.weight})'

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

*예시3: sort메서드가 호출 시 특별 메서드가 정의되지 않으면 객체 정렬 불가

tools.sort()

#Traceback...
#TypeError: '<' not supported between instances of 'Tool' and 'Tool'

  • 실제로는 저희가 만든 객체가 여러 가지 순서를 지원해야하는 경우가 더 많습니다.

    • 정렬에 사용하고 싶은 애트리뷰트가 객체에 들어있는 경우
    • key함수를 통해 정렬 중인 리스트의 원소가 전달
    • key함수가 반환하는 값은 원소 보단 정렬 기준으로 사용하고 비교가 가능한 값만 됨

*예시1: lambda 키워드로 함수를 정의함 for name따라 정렬

  • key로 사용하면 Tool객체로 이뤄진 리스트를 이름에 딸 알파벳 순으로 정렬
print('미정렬:', repr(tools)) #미정렬: [Tool('수준계',3.5), Tool('해머', 1.25), Tool('스크류드라이버',0.5), Tool('끝',0.25)]
tools.sort(key=lambda x: x.name)
print('\n정렬:', tools) # 정렬: [Tool('끝',0.25), Tool('수준계',3.5), Tool('스크류드라이버',0.5), Tool('해머', 1.25)]

*예시2: weight로 정렬하는 람다 함수 만들어서 sort의 key 파라미터 전달

  • key로 전달된 내부 람다 함수는 원소 애트리뷰트에 접근하거나 인덱스를 써서 값을 얻는 등에 적용할 수 있습니다.
tools.sort(key=lambda x:x.weight)
print('무게 순 정렬:', tools)

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

*예시3: lower메서드 사용하여 장소 이름 소문자화 후 알파벳 순으로 비교

  • 문자열 같은 기본 타입의 경우 정렬 전 key함수 사용 시 원소 값 변형 가능
places = ['home', 'work', 'New York', 'Paris']
places.sort()
print('대소문자 구분:', places) #대소문자 구분: ['New York', 'Paris', 'home', 'work']

places.sort(key=lambda x:x.lower)
print('대소문자 무시:', places)#대소문자 무시: ['home', 'New York', 'Paris', 'work']

*예시4: tuple타입을 사용하여 weight 먼저 정렬 후 name으로 정렬

  • 튜플은 임의의 파이썬 값을 넣는 불변의 값이다.

  • 비교 가능하며 자연스러운 순서가 정해져있다.

  • 특별 비교 메서드는 튜플의 각 위치를 이터레이션하면서 각 인덱스에 해당하는 원소를 한 번에 하나씩 비교방식 구현

  • 비교하는 두 튜플의 첫 번째 위치에 있는 값이 서로 같으면 튜플의 비교 메서드는 두 번째 위치에 있는 값과 서로 비교

  • 두 번째 위치도 같으면 세 번째 이우 위치 등에도 비교 반복

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

saw = (5, '원형 톱')
jackhammer = (40, '착암기')
assert not (jackhammer < saw) #예상대로 결과 나온다

drill = (4, '드릴')
sander = (4, '연마기')
assert drill[0] == sander[0] #무게가 같다
assert drill[1] < sander[1] #알파벳 순으로 볼 때 더 작다
assert drill < sander #그러므로 드릴 먼저

power_tools.sort(key=lambda x: (x.weigh, x.name))
print(power_tools) #[Tool('드릴', 4),Tool('연마기', 4),Tool('원형 톱', 5),Tool('착암기', 40)]

*예시5: 단항 부호 반전(-) 연산자를 사용해서 정렬 방향을 혼합하여 정렬 순서는 그대로 둔 채 반전된 값의 정렬 순서를 바꾼다.

  • weight기준으로 내림차순 정렬 후 name기준으로 오름차순 정렬

  • 모든 타입에 부호 반전이 가능한 것은 아니다.

power_tools.sort(key=lambda x: (-x.weight, x.name))
print(power_tools)

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

*안정적인 정렬 알고리즘

  • 리스트 타입의 sort메서드는 key함수가 반환하는 값이 같을 경우 원래 순서 그대로 유지

  • 리스트에 대해 서로 다른 기준으로 sort를 여러 번 호출

  • weight기준 내림차순, name기준 오름차순으로 정렬 (sort 두 번 호출 방식)

power_tools.sort(key =lambda x:x.name) #name기준 오름차순

power_tools.sort(key=lambda x:x.weight,
                 reverse=True) # weight 기준 내림차순
                 
print(power_tools)

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

정리

  • key함수를 사용해 tuple을 반환하게 하고 단항 부호로 반전 연산을 활용하는 접근 방식이 코드가 적고 읽기 쉽다.

  • 꼭 필요할 때만 sort를 여러 번 호출하는 방법 사용 권장한다.

  • 리스트 타입에 들어 있는 sort메서드를 사용하면 자연스러운 순서로 리스트의 원소 정렬한다.

  • 원소타입에 특별 메소드를 지정하지 않으면 sort메서드 사용 불가하다

  • sort메서드의 key파라미터를 사용하면 리스트의 각 원소 대신 비교에 사용할 객체를 반환하는 도우미 함수 제공한다.

  • key함수에서 튜플을 반환하면 여러 정렬 기준을 하나로 엮을 수 있다

  • 부호를 바꿀 수 없는 타입의 경우 여러 정렬 기준을 조합하여 각 정렬 기준마다 reverse 값으로 정렬 순서를 지정하면서 sort메서드 여러 번 사용

profile
성장을 도울 아카이빙 블로그

0개의 댓글