파이썬 선형 구조의 특징
파이썬에서 리스트, 튜플, 문자열과 같은 선형 구조는 다음과 같은 주요 특징을 가집니다:
- 반복 가능 (Iterable):
for ... in구문을 사용하여 요소들을 순회할 수 있습니다. - 길이 측정 가능:
len(x)함수를 통해 길이를 얻을 수 있는 컨테이너 객체입니다. - 인덱스 접근: 정수 인덱스(양수 또는 음수)를 사용하여 개별 요소에 접근할 수 있습니다.
- 슬라이싱 가능: 특정 범위의 요소들을 새로운 시퀀스로 추출하는 슬라이싱을 지원합니다.
슬라이싱 기본 개념
파이썬의 슬라이싱은 선형 구조에서 특정 인덱스 범위를 지정하여 부분 시퀀스를 얻는 강력한 기능입니다.
sequence[start:stop]
sequence[start:stop:step]
여기서 start, stop, step은 정수 값으로, 양수, 음수, 또는 0이 될 수 있습니다.
start: 슬라이싱을 시작할 인덱스입니다. 생략 시 기본값은 0 (처음)입니다.stop: 슬라이싱을 종료할 인덱스입니다. 이 인덱스의 요소는 포함되지 않습니다 ([start, stop)범위). 생략 시 기본값은 시퀀스의 끝입니다.step: 슬라이싱 시 요소를 건너뛸 간격입니다. 생략 시 기본값은 1입니다.
인덱스 범위를 벗어나더라도 오류를 발생시키지 않고, start가 하한을 초과하면 처음부터, stop이 상한을 초과하면 끝까지 처리합니다.
슬라이싱 시 중요한 점은 start와 stop으로 정의되는 "방향"과 step의 "방향"이 일치해야 한다는 것입니다. step이 양수이면 오른쪽(정방향), 음수이면 왼쪽(역방향)으로 진행합니다. 만약 두 방향이 일치하지 않으면 해당 타입의 빈 객체가 반환됩니다.
my_list = [10, 20, 30, 40, 50]
print(f"기본 슬라이싱: {my_list[1:4]}") # [20, 30, 40]
print(f"처음부터: {my_list[:3]}") # [10, 20, 30]
print(f"끝까지: {my_list[2:]}") # [30, 40, 50]
print(f"전체 복사: {my_list[:]}") # [10, 20, 30, 40, 50]
print(f"음수 인덱스: {my_list[-3:-1]}") # [30, 40]
print(f"스텝 지정: {my_list[::2]}") # [10, 30, 50]
print(f"역순 슬라이싱: {my_list[::-1]}") # [50, 40, 30, 20, 10]
print(f"방향 불일치 예시 (빈 리스트 반환): {my_list[3:1]}") # []
print(f"역방향 스텝으로 유효한 슬라이싱: {my_list[3:1:-1]}") # [40, 30]
객체 식별자: id()
id(object) 함수는 CPython에서 객체의 메모리 주소를 반환합니다. 이 값은 두 변수가 메모리 상에서 동일한 객체를 참조하는지 확인할 때 유용합니다.
data_a = [1, 2, 3]
data_b = data_a
data_c = [1, 2, 3]
print(f"data_a의 id: {id(data_a)}")
print(f"data_b의 id: {id(data_b)}") # data_a와 동일한 객체
print(f"data_c의 id: {id(data_c)}") # 다른 객체
print(f"data_a와 data_b는 같은 객체? {id(data_a) == id(data_b)}") # True
print(f"data_a와 data_c는 같은 객체? {id(data_a) == id(data_c)}") # False
print(f"data_a == data_c? {data_a == data_c}") # True (값은 같음)
슬라이스 할당
슬라이스 할당은 시퀀스의 특정 부분을 다른 이터러블 객체의 내용으로 교체하거나, 삽입 또는 삭제하는 데 사용됩니다. 이는 등호(=) 왼쪽에 슬라이스 표현식을 사용하여 수행합니다.
my_collection = []
print(f"초기 리스트: {my_collection}")
# 빈 리스트에 요소 추가 (초기화)
my_collection[0:] = [10, 20, 30, 40]
print(f"초기 슬라이스 할당 후: {my_collection}") # [10, 20, 30, 40]
# 특정 범위 대체 (개수 유지)
my_collection[1:3] = [50, 60]
print(f"부분 슬라이스 대체 후: {my_collection}") # [10, 50, 60, 40]
# 특정 위치에 삽입 (할당되는 이터러블의 길이가 다름)
my_collection[2:2] = [70, 80, 90]
print(f"중간 삽입 후: {my_collection}") # [10, 50, 70, 80, 90, 60, 40]
# 끝 부분 대체
my_collection[5:] = [100, 110]
print(f"끝 부분 대체 후: {my_collection}") # [10, 50, 70, 80, 90, 100, 110]
# 슬라이스를 빈 리스트로 할당하여 삭제
my_collection[1:4] = []
print(f"슬라이스 삭제 후: {my_collection}") # [10, 90, 100, 110]
슬라이스 할당은 간결한 구문을 제공하지만, 리스트는 동적 배열(순서표) 구조이므로 요소를 삽입하거나 삭제할 때 내부적으로 데이터 이동이 발생하여 성능에 영향을 줄 수 있습니다. 특히 대규모 리스트에서 빈번한 슬라이스 할당은 피하는 것이 좋습니다.
문자열 분할 메서드
split(sep=None, maxsplit=-1)
문자열을 sep으로 지정된 구분자를 기준으로 왼쪽부터 분할하여 문자열 리스트를 반환합니다.
sep: 구분자로 사용할 문자열입니다.None일 경우, 모든 공백 문자를 구분자로 사용하며, 결과 리스트에는 빈 문자열이 포함되지 않습니다.maxsplit: 분할할 최대 횟수입니다.-1(기본값)은 문자열 전체를 분할합니다.
sentence_data = 'Python is a powerful language'
print(f"공백으로 분리 (기본값): {sentence_data.split()}")
# ['Python', 'is', 'a', 'powerful', 'language']
log_message = 'ERROR: File not found: C:\\path\\to\\file.txt'
print(f"콜론 기준으로 분리: {log_message.split(': ')}")
# ['ERROR', 'File not found', 'C:\\path\\to\\file.txt']
csv_row = 'apple,banana,cherry,date,elderberry'
print(f"첫 두 개의 쉼표로만 분리: {csv_row.split(',', 2)}")
# ['apple', 'banana', 'cherry,date,elderberry']
rsplit(sep=None, maxsplit=-1)
split()과 동일하지만, 문자열을 오른쪽부터 분할합니다. 반환되는 문자열 리스트의 순서는 일반적인 왼쪽-오른쪽 순서를 유지합니다.
path_str = 'usr/local/bin/python'
print(f"마지막 슬래시로 분리 (rsplit): {path_str.rsplit('/', 1)}")
# ['usr/local/bin', 'python']
version_info = '1.2.3.4.5'
print(f"rsplit으로 마지막 버전 정보 추출: {version_info.rsplit('.', 2)}")
# ['1.2.3', '4', '5']
splitlines(keepends=False)
문자열을 행 구분자(\n, \r\n, \r 등)를 기준으로 분할하여 문자열 리스트를 반환합니다.
keepends:True로 설정하면 결과 리스트에 행 구분자가 포함됩니다.
multi_line_content = "첫 번째 줄\n두 번째 줄\r\n세 번째 줄\r"
print("원본 다중 라인 텍스트:")
print(multi_line_content)
print(f"줄바꿈 문자 제외 분리: {multi_line_content.splitlines()}")
# ['첫 번째 줄', '두 번째 줄', '세 번째 줄']
print(f"줄바꿈 문자 포함 분리: {multi_line_content.splitlines(True)}")
# ['첫 번째 줄\n', '두 번째 줄\r\n', '세 번째 줄\r']
partition(sep)
문자열을 sep으로 지정된 첫 번째 구분자를 기준으로 세 부분((head, sep, tail))으로 분할한 튜플을 반환합니다. 구분자를 찾지 못하면 (원본 문자열, '', '') 형태의 튜플을 반환합니다.
sep: 분할에 사용할 구분자 문자열입니다. 반드시 지정해야 합니다.
full_domain = "www.example.com"
print(f"첫 번째 점으로 파티션: {full_domain.partition('.')}")
# ('www', '.', 'example.com')
email_address = "user@domain.net"
print(f"골뱅이 기호로 파티션: {email_address.partition('@')}")
# ('user', '@', 'domain.net')
no_separator_text = "hello world"
print(f"구분자가 없는 경우: {no_separator_text.partition('x')}")
# ('hello world', '', '')
rpartition(sep)
partition()과 동일하지만, 문자열을 sep으로 지정된 마지막 구분자를 기준으로 세 부분((head, sep, tail))으로 분할한 튜플을 반환합니다. 구분자를 찾지 못하면 ('', '', 원본 문자열) 형태의 튜플을 반환합니다.
sep: 분할에 사용할 구분자 문자열입니다. 반드시 지정해야 합니다.
file_with_ext = "document.report.2023.pdf"
print(f"마지막 점으로 파티션: {file_with_ext.rpartition('.')}")
# ('document.report.2023', '.', 'pdf')
url_path = "http://localhost:8000/api/v1/users"
print(f"마지막 슬래시로 파티션: {url_path.rpartition('/')}")
# ('http://localhost:8000/api/v1', '/', 'users')
no_separator_text_r = "python learning"
print(f"구분자가 없는 경우 (rpartition): {no_separator_text_r.rpartition('z')}")
# ('', '', 'python learning')