Chapter 2 리스트와 딕셔너리
BETTER WAY 11 시퀀스를 슬라이싱하는 방법을 익혀라
슬라이싱 ☆
- getitem과 setitem 특별 메서드를 구현한 모든 파이썬 클래스에도 슬라이싱을 추가할 수 있다.
- 리스트의 맨 앞부터 슬라이싱할 때는 시각적인 잡음을 없애기 위해 0을 생략해야 한다.
- 리스트의 끝까지 슬라이싱할 때는 쓸데없이 끝 인덱스를 적지 말라.
- 리스트의 끝에서부터 원소를 찾고 싶을 때는 음수 인덱스를 사용하면 된다.
- 슬라이싱할 때 인덱스 범위를 넘어가는 시작과 끝 인덱스는 무시된다.
- 리스트를 슬라이싱한 결과는 완전히 새로운 리스트가 반환된다.
- 리스트를 슬라이싱한 결과는 얕은 복사 (shallow copy) 되므로 내부에 존재하는 List는 동일한 주솟값을 참조한다. ☆☆☆
- 대입에 슬라이스를 사용하면 지정한 범위에 들어 있는 원소를 변경한다.
- 지정한 슬라이스 길이보다 대입되는 배열의 길이가 더 짧다면 리스트가 줄어든다.
- 지정한 슬라이스 길이보다 대입되는 배열의 길이가 더 길다면 리스트가 늘어난다.
- 슬라이싱에서 시작과 끝 인덱스를 모두 생략하면 원래 리스트를 복사한 새 리스트를 얻는다.
- 시작과 끝 인덱스가 없는 슬라이스에 대입하면 슬라이스가 참조하는 리스트의 복사본으로 덮어쓴다.
기억해야 할 내용
- 슬라이싱할 때는 간결하게 하라. 시작 인덱스에 0을 넣거나, 끝 인덱스에 시퀀스 길이를 넣지 말라.
- 슬라이싱은 범위를 넘어가는 시작 인덱스나 끝 인덱스도 허용한다.
- 시퀀스의 시작이나 끝에서 길이를 제한하는 슬라이스 (a[:20]이나 a[-20:])를 쉽게 표현할 수 있다.
- 리스트 슬라이스에 대입하면 원래 시퀀스에서 슬라이스가 가리키는 부분을 대입 연산자 오른쪽에 있는 시퀀스로 대치한다.
- 리스트 슬라이스에 대입할 때, 대치되는 시퀀스의 길이가 달라도 대입이 가능하다.
BETTER WAY 12 스트라이드와 슬라이스를 한 식에 함께 사용하지 말라
슬라이싱과 스트라이드
- 슬라이싱 : List[시작:끝]으로 리스트를 순서대로 슬라이싱할 수 있는 문법
- 스트라이드 : List[시작:끝:증가값]으로 (증가값을 지정한) 일정 간격을 두고 슬라이싱을 할 수 있는 특별한 문법
스트라이드
- 일반적인 유니코드 문자열은 정상 동작하지만, UTF-8로 인코딩한 유니코드 문자열은 오류가 발생한다.
- 시작값이나 끝값을 증가값과 함께 사용하지 않아야 가독성이 좋아진다.
- 증가값을 사용해야 하는 경우 양수값을 만들고 시작과 끝 인덱스를 생략해야 한다.
스트라이드 예제
- List[ : : -1] : 문자열을 역으로 뒤집는 방법
- List[ : : 2] : 시작부터 매 두 번째 원소를 선택한다.
- List[ : : -2] : 끝에서 시작해 앞으로 가면서 두 번째 원소를 선택한다.
itertools.islice : 스트라이드보다 더 깔끔하며 시작, 끝 증가값에 음수를 사용할 수 없다.
기억해야 할 내용
- 슬라이스에 시작, 끝, 증가값을 함께 지정하면 코드의 의미를 혼동하기 쉽다.
- 시작이나 끝 인덱스가 없는 슬라이스를 만들 때는 양수 증가값을 사용하라. 가급적 음수 증가값은 피하라
- 한 슬라이스 안에서 시작, 끝, 증가값은 함께 사용하지 마라.
- 한 슬라이스 안에서 세 파라미터를 모두 써야 할 경우, 두 번 대입하거나 itertools 내장 모듈인 islice를 사용하라.
BETTER WAY 13 슬라이싱보다는 나머지를 모두 잡아내는 언패킹을 사용하라
언패킹 (Unpacking)
- 기본 언패킹의 한계점은 언패킹할 시퀀스의 길이를 미리 알고 있어야 하는 단점이 존재한다.
별표 식 (startred expression)
- Unpacking 패턴의 다른 부분에 들어가지 못하는 모든 값을 별이 붙은 부분에 다 담을 수 있다.
- 일반적인 Unpacking과 다르게 더 짧고, 읽기 쉽고, 여러 줄 사이에 인덱스 경계 값에서 오류가 발생할 여지도 없다.
- Unpacking해야만 하는 값 외에 여분의 슬라이스가 하나 또는 나머지를 모두 잡아낼 때 사용할 수 있다.
- Unpacking할 때 필수인 부분이 적어도 하나 이상은 있어야 별표 식을 사용할 수 있다. ☆
- 별표 식 만으로 Unpacking 할 수 없다. ☆
- 한 수준의 Unpacking 패턴에서 별표 식을 두 개 이상 사용할 수 없다.
- 여러 계층으로 이뤄진 구조를 Unpacking할 때는 서로 다른 부분에서는 별표 식을 여럿 사용해도 된다.
- 별표 식은 항상 List 인스턴스가 된다. ☆
- Unpacking 결과값에 남는 원소가 없다면 별표 식 인스턴스는 빈 리스트가 된다. []
- iterator의 내용을 Header와 Body로 쉽게 나눠서 처리할 수 있고, 깔끔하게 코드를 구성할 수 있다.
- 별표 식은 새로운 List를 만들어내기 때문에 컴퓨터 메모리를 전부 사용해 프로그램이 멈출 수 있다. ☆
- Javascript의 객체 펼침 연산자( ... )와 비슷한 행동을 한다. ☆
기억해야 할 내용
- 언패킹 대입에 별표 식을 사용하면 언패킹 패턴에서 대입되지 않는 모든 부분을 리스트에 잡아낼 수 있다.
- 별표 식은 언패킹 패턴의 어떤 위치에든 놓을 수 있다.
- 별표 식에 대입된 결과는 항상 리스트가 되며, 0개 또는 그 이상이 들어간다.
- 리스트를 겹치지 않게 여러 조각으로 나눌 경우, 슬라이싱, 인덱스보다 별표 식 언패킹이 더 깔끔하고 효율적이다.
BETTER WAY 14 복잡한 기준을 사용해 정렬할 때는 key 파라미터를 사용하라
list.sort() : list의 내부 인자들을 정렬한다. (Default 오름차순)
key 파라미터
- sort()의 파라미터로 입력받는다.
- key는 함수여야 한다.
- key 함수에는 sort를 실행한 list의 원소가 전달된다.
- key 함수가 반환하는 값은 원소 대신 정렬 기준으로 사용할, 비교 가능한 값이어야만 한다.
list.sort()
- 대 소문자가 동시에 존재할 경우 ASCII값이 'A' = 65, 'a' = 97 인데, 여기서 대문자가 더 작으므로 우선 정렬된다. ☆
- 숫자의 경우 부호 반전 (-) 연산자를 사용해 정렬 방향을 혼합할 수 있다.
- key 함수가 반환하는 값이 서로 같은 경우 리스트에 들어 있던 원래 순서를 그대로 유지해준다.
- 최종적으로 리스트에서 얻고 싶은 정렬 데이터를 우선순위의 역순으로 정렬을 하면 원래의 리스트를 얻을 수 있다.
- key 함수르 사용해 tuple을 반환하고, 부호 반전 연산을 활용하는 코드가 더 적고, 읽기 쉽다.
- sort를 여러 번 호출하는 방법은 꼭 필요할 때만 사용해야 한다.
튜플 (Tuple)
- 튜플은 비교 가능하며, 내부 인자의 순서가 미리 정해져 있다.
- 튜플의 비교 메서드는 튜플의 각 위치를 이터레이션하면서 각 인덱스에 해당하는 원소를 한 번에 하나씩 비교한다.
기억해야 할 내용
- list.sort()를 사용하면 문자열, 정수, 튜플 등과 같은 타입인 경우 자연스러운 순서로 리스트의 원소를 정렬할 수 있다.
- Class와 같은 원소 타입에 순서가 정의되어 있지 않다면 sort 메서드를 쓸 수 없다.
- Class와 같은 원소 타입에 순서 특별 메서드를 정의하는 경우는 드물다.
- sort 메서드의 key 파라미터를 사용하면 list의 각 원소 대신 비교에 사용할 객체를 반환하는 도우미 함수를 사용할 수 있다.
- key 함수에서 tuple을 반환하면 여러 정렬 기준을 하나로 엮을 수 있다.
- 단항 부호 반전 연산자 (-)를 사용하면 부호를 바꿀 수 있는 타입이 정렬 기준인 경우 정렬 순서를 반대로 바꿀 수 있다.
- 부호를 바꿀 수 없는 타입에 여러 정렬 기준을 조합하려면 각 정렬 기준마다 reverse 값으로 순서를 지정해야 한다.
BETTER WAY 15 딕셔너리 삽입 순서에 의존할 때는 조심하라
Dictionary (사전)
- v.3.5 이전 : 이터레이션을 수행하면 키를 임의의 순서로 반환하고, 삽입된 순서와 일치하지 않음
- v.3.6 이후 : 이터레이션을 수행하면 키를 입력된 순서로 반환하고, 삽입된 순서와 일치
collections.OrderdDict
- Python의 버전과 상관없이 삽입 순서를 유지한다.
- 키 삽입과 popitem 호출을 자주 처리해야 한다면 표준 dict보다 성능이 우수하다.
collections.abc.MutableMapping
- Python의 dictionary를 커스텀 컨테이너로 제작할 수 있도록 도와주는 추상 베이스 클래스
- 버그 발생률이 낮고, 서브 클래싱이 간단하지만, 느리고, 더 많은 메모리를 필요로한다.
- doc : https://docs.python.org/3/library/collections.abc.html
커스텀 컨테이너 타입의 오류를 해결하는 방법
- dictionary가 특정 순서로 이터레이션 된다고 가정하지 않고, 별도의 도우미 함수를 구현하여 해결한다.
- 함수 맨 앞에 특정 타입이 우리가 원하는 타입인지 검사하는 코드를 추가한다.
- 타입 애너테이션을 사용해서 get_winner에 전달되는 값이 dict 인스턴스가 되도록 강제한다.
기억해야 할 내용
- 파이썬 3.7부터는 dict 인스턴스에 들어 있는 내용을 이터레이션할 때 키를 삽입한 순서대로 돌려받는 사실에 의존할 수 있다.
- 파이썬은 딕셔너리와 비슷한 객체를 쉽게 만들 수 있게 해준다. ( collections.abc.MutableMapping )
- 추상 베이스 클래스를 사용해 커스텀 컨테이너 dict를 생성할 경우 키 삽입 순서가 그대로 보존된다고 가정할 수 없다.
- 추상 베이스 클래스로 생성된 dict와 비슷한 클래스를 조심스럽게 다루는 방법은
- dict 인스턴스의 삽입 순서 보존에 의존하지 않고 코드를 작성하는 방법
- 실행 시점에 명시적으로 dict 타입을 검사하는 방법
- 타입 애너테이션과 정적 분석(static analysis)을 사용해 dict 값을 요구하는 방법이 있다.
BETTER WAY 16 in을 사용하고 딕셔너리 키가 없을 때 KeyError를 처리하기보다는 get을 사용하라
dictionary.get
- 호출하는 key 값이 존재하지 않아도 KeyError가 발생하지 않는다.
- key값이 존재하지 않을 때 발생하는 Default Value 값을 지정해 원하는 반환값을 정의할 수 있다.
dictionary.items() : dictionary의 key, value 값을 동시에 반환한다.
이중 대입문 ☆
- 키 대입을 두 줄이 아니라 한 줄로 처리한다.
- ex) votes[key] = names = []
- Default 값으로 빈 리스트를 dictionary에 넣고 나면 참조를 통해 리스트의 내용을 변경할 수 있다.
dictionary.setdefault ☆
- dictionary에서 key를 사용해 value를 호출한다.
- key가 없으면 제공받은 default value를 key에 연관시켜 dictionary에 대입한 후 key와 연관된 값을 반환한다.
- get과 대입식을 사용하는 것보다 더 짧은 코드를 구성할 수 있다.
- 호출 함수명이 set으로 시작해 동작을 직접적으로 드러내지 않아 가독성이 좋지 않다.
- key가 없으면 setdefault에 전달된 default 값이 별도로 복사되지 않고 딕셔너리에 직접 대입된다. ☆
- 호출할 때마다 리스트를 새롭게 만들어야 하므로 성능이 크게 저하될 수 있다. ☆☆☆
- default 값을 만들어내기 쉽거나, default 값이 변경 가능하거나, 값을 만들 때 예외가 발생할 가능성이 없는 경우 사용한다.
기억해야 할 내용
- dictionary 키가 없는 경우를 처리하는 방법으론, in, KeyError, get, setdefault 메서드를 사용하는 방법이 있다.
- counter와 같이 기본적인 타입의 값이 들어가는 dictionary를 다룰 때는 get 메서드가 가장 좋다.
- dictionary에 넣을 값을 만드는 비용이 비싸거나 과정에 예외가 발생할 수 있는 경우에도 get 메서드를 사용하는 게 더 낫다.
- dictionary의 setdefault 메서드를 사용하는 것 보단 defaultdict를 사용할지 고려해보라.
BETTER WAY 17 내부 상태에서 원소가 없는 경우를 처리할 때는 setdefault보다 defaultdict를 사용하라
collections.defaultdict
- key가 없을 때 자동으로 Default Value를 저장한다.
- setdefault를 사용하는 것 보다 코드의 길이도 짧고 가독성이 향상된다.
기억해야 할 내용
- Key로 어떤 값이 들어올지 모른다면 collections.defaultdict를 사용하라
- 임의의 키로 dictionary를 받았고, 어떻게 dictionary가 생성되었는지 모르는 경우 get 메서드를 이용해 원소에 접근하라
- get 메서드보다 setdefault가 더 짧은 코드를 만들어 내는 경우 setdefault를 사용하는 것도 고려해볼 만하다.
BETTER WAY 18 __missing__을 사용해 키에 따라 다른 디폴트 값을 생성하는 방법을 알아두라
dictionary.__missing__() ☆
- dictioanry의 Key가 없는 경우 처리하는 로직을 커스텀화 할 수 있는 특별 메서드
file.seek(offset, whence) : 파일 객체를 읽는 포인터 위치를 변경하는 메서드
기억해야 할 내용
- Default 값을 만드는 계산 비용이 높거나 만드는 과정에서 에러가 발생할 경우 dict.setdefault 메서드는 사용하지 않는다.
- defaultdict에 전달되는 함수는 인자를 받지않아 접근에 사용한 Key 값에 맞는 Default Value르 생성하는 것은 불가능
- Default Key를 만들 때 어떤 키를 사용했는지 알아야 한다면 dict의 하위 클래스와 missing 특별 메서드를 정의하면 된다.
'필기노트' 카테고리의 다른 글
[Effective Typescript] Chapter 1 타입스크립트 알아보기 (0) | 2022.01.17 |
---|---|
[Python 코딩의 기술] Chapter 3 함수 (0) | 2021.10.23 |
[Python 코딩의 기술] Chapter 1 파이썬 답게 생각하기 (0) | 2021.10.12 |
[Javascript 코딩의 기술] Chapter 4 조건문을 깔끔하게 작성하라 (0) | 2021.10.04 |
[Javascript 코딩의 기술] Chapter 3 특수한 컬렉션을 이용해 코드 명료성을 극대화하라 (0) | 2021.10.04 |