
파이썬의 내장 시퀀스는 두 가지 기준으로 분류 가능하다:
list, tuple, collections.deque.str, bytes, array.array.가변 시퀀스 : 생성 후 수정 가능.
list, bytearray, array.array, collections.deque.
불변 시퀀스 : 생성 후 수정 불가능.
tuple, str, bytes.
가변 시퀀스가 불변 시퀀스를 상속하면서 여러 메서드를 추가로 구현하였다.
수정 가능/불가능이 무슨 뜻?🤔
다음 코드를 통해 이해할 수 있을 것이다.
my_list = [1, 2, 3]
my_list[0] = 10 # 수정 가능
my_list.append(4) # 추가 가능
print(my_list) # 출력: [10, 2, 3, 4]
my_tuple = (1, 2, 3)
# my_tuple[0] = 10 # 에러 발생! 수정 불가
new_tuple = my_tuple + (4,) # 새로운 튜플 생성
print(new_tuple) # 출력: (1, 2, 3, 4)
결론적으로 핵심 시퀀스형은 list
가변적이며 혼합된 자료형을 담을 수 있다.
(다음 섹션 스포 😅)
listcomp는 기존의 for 루프를 한 줄로 표현할 수 있는 방식인데, 이 구문이 두 줄을 넘어간다면 코드를 분할하거나 for문을 이용하는 것이 더 낫다고 한다.
- 리스트만 만들 수 있고, 다른 시퀀스를 만들려면 제너레이터 표현식 genexp를 사용해야 한다.
바다코끼리 연산자 !=

(반가운 바다코끼리)
이 연산자는 변수에 값을 할당하고 동시에 사용할 수 있게 해주는데, listcomp에서 사용되는 경우가 종종 있다.
람다(lambda)를 사용하지 않고도 함수 map() 혹은filter()를 함께 쓸 수 있다.
- 실제 스터디 팀원 분들 어떤 상황의 코드에서 쓰시는지 궁금.
데카르트 곱 : listcomp는 두 개 이상의 반복 가능한 자료형의 데카르트 곱을 나타내는 리스트를 만들 수 있는데, 곱의 각 항목은 입력으로 받은 iterable한 데이터의 각 요소에서 만들어진 튜플로 구성된다.
- itertools.product : 데카르트 곱 예제를 출력하며 보다가, 문득 생각났다.
옛날에 코테 문제 풀면서 알게 된 함수이다.
이 책에서는 listcomp와 함께 데카르트 곱을 구하는 예제가 나왔지만, 요 함수 하나로 여러 리스트의 모든 가능한 조합을 바로 확인할 수 있다!!
colors = ['black', 'white']
sizes = ['S', 'M', 'L']
tshirts = [(color, size) for color in colors for size in sizes]
print(tshirts) --- (a)
print(list(itertools.product(colors, sizes))) --- (b)
(a)와 (b) 모두 출력 결과는 동일!
제너레이터 표현식 : 튜플, 배열 등의 시퀀스형을 초기화하려면 listcomp를 사용할 수도 있지만 genexp가 메모리를 더 적게 사용한다.
=> 반복자 프로토콜을 이용해 항목을 하나씩 생성하기 때문.
- 대괄호 [] 대신 소괄호 () 사용.
constant folding이라는 키워드를 찾을 수 있는데, 이를 이해하면 도움이 될 듯 하다.튜플 언패킹은 병렬 할당(parallel assignment) 할 때 가장 두드러짐.
병렬 할당이란?
: 반복형 데이터를 변수로 구성된 튜플에 할당하는 것
a, b, c = (1, 2, 3)
print(a) # 출력 : 1
print(b) # 출력 : 2
print(c) # 출력 : 3
변수의 값 맞바꾸기
b, a = a, b
인수 앞에 * 붙이기
first, *rest = [1, 2, 3, 4, 5]
print(first) # 출력: 1
print(rest) # 출력: [2, 3, 4, 5]
별표로 할당을 하는 건 직접 써보지 않았는데, 꽤 편할 듯 하다.
단, 하나의 변수에만 적용 가능. 위치에는 제한 X
list, tuple, set 형의 리터럴 정의할 때도 사용 가능
중첩 구조 언패킹
# 중첩 구조 언패킹
data = (1, (2, 3), 4)
a, (b, c), d = data
print(a) # 출력: 1
print(b) # 출력: 2
print(c) # 출력: 3
print(d) # 출력: 4
a * n과 같은 표현식 사용하려면 주의해야 한다.my_list = [[]] * 3으로 초기화하면 동일한 내부 리스트에 대한 참조 세 개를 가진 리스트가 만들어져서 원치 않는 결과가..!+=, *=+= : __iadd__() 메서드 통해 구현a += b에서 a가 list, bytearray, array.array 등 가변 시퀀스라면 a의 값이 변경된다. (a.extend(b)와 유사)*= : __imul__() 메서드 통해 구현list.sort()와 sorted() 내장 함수list.sort() : 사본을 만들지 않고 리스트 내부를 변경해 정렬. receiver를 변경하고 새로운 리스트를 생성하지 않았음을 알려주려고 None 반환random.shuffle() 함수도 동일하게 작용한다.sorted() 내장 함수 : 새로운 리스트를 생성해 반환하므로, 불변 시퀀스 및 제너레이터 등 모든 iterable한 객체를 인수로 받는다.array.array) >>> 리스트보다 효율pop(), insert(), extend()을 모두 지원,frombytes(), tofile() 메서드도 지원deque, queue, multiprocessing, asyncio, heapq ‼️‼️돌아온 특별 메서드!
__len__(), __getitem__() 메서드를 동반S.indices(len) -> (start, stop, stride) : 길이가 len인 시퀀스 S가 나타내는 확장된 슬라이스의 start와 stop 인덱스 및 stride 길이를 계산한다._components 배열에 처리하도록 하므로 slice.indices() 메서드를 구현할 필요가 없다. def __len__(self):
return len(self._components)
def __getitem__(self, key):
if isinstance(key, slice):
cls = type(self)
return cls(self._components[key])
index = operator.index(key)
return self._components[index]
operator.index() 함수가 __index__() 메서드 호출__getattr__ 메서드를 사용하여 구현 가능__getattr__() + __setattr__()도 구현할 것.__hash__ 메서드reduce()로 해결!__eq__ 메서드all() zip() 함수 : 반복형에서 나온 항목을 튜플로 묶어서 두 개 이상의 반복형을 병렬로 반복하기 쉽게 해 준다. def __eq__(self, other):
return len(self) == len(other) and all(a == b for a, b in zip(self, other))