
파이썬은 리스트를 일정한 간격으로 건너뛰며 잘라내는 문법을 제공합니다. somelist[start:end:stride] 형태이고, 이 세 번째 값을 스트라이드(stride)라고 부릅니다. 짝수 번째 원소만 뽑거나 문자열을 뒤집을 때 x[::2], x[::-1] 한 줄이면 끝나니 처음 배울 때는 꽤 매력적으로 보입니다.
문제는 이 편리함이 시작 인덱스, 끝 인덱스와 만나는 순간부터입니다. 숫자 세 개가 대괄호 하나에 몰리면 코드 밀도가 지나치게 높아지고, 여기에 음수가 하나라도 섞이면 결과를 머릿속으로 추적하기가 사실상 불가능해집니다. 리뷰어가 "이 줄이 무엇을 반환합니까?"라고 물었을 때 작성자 본인도 인터프리터를 켜 봐야 한다면, 그 코드는 이미 버그의 온상입니다.
x = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
# 세 자리가 다 채워지면 결과를 눈으로 예측하기 어렵습니다
print(x[2::2]) # ['c', 'e', 'g']
print(x[-2::-2]) # ['g', 'e', 'c', 'a']
print(x[-2:2:-2]) # ['g', 'e']
print(x[2:2:-2]) # [] <- 정말 의도한 결과였습니까?
# 바이트 문자열을 뒤집으면 인코딩이 깨집니다
w = '안녕'
encoded = w.encode('utf-8')
flipped = encoded[::-1] # 문자 경계를 무시하고 바이트만 뒤집습니다
print(flipped.decode('utf-8')) # UnicodeDecodeError
x[::-1]은 아스키 문자열에서는 잘 동작하지만, UTF-8로 인코딩된 바이트에서는 멀티바이트 문자의 내부 순서까지 뒤집어 디코딩 자체가 실패합니다. 한 줄짜리 관용구가 데이터 종류에 따라 조용히 무너지는 셈입니다.
from itertools import islice
x = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
# 1) 스트라이드로 먼저 걸러내고
strided = x[::2]
# 2) 그 결과를 다시 슬라이스합니다
result = strided[1:-1]
print(result) # ['c', 'e']
# 복사가 부담되는 큰 시퀀스라면 islice로 한 번에 처리합니다
lazy = list(islice(x, 2, 7, 2)) # 음수 인덱스와 음수 증가값을 아예 허용하지 않습니다
print(lazy) # ['c', 'e', 'g']
두 단계로 나누면 한 줄이 하는 일이 하나로 줄어들어 가독성이 확실히 좋아집니다. 중간 리스트가 하나 더 생기지만 대부분의 상황에서 그 비용은 무시할 만하고, 메모리가 아깝다면 이터레이터(iterator)를 반환하는 islice가 대안이 됩니다. islice는 음수 값을 문법 차원에서 막아 주기 때문에 실수의 여지 자체를 없애 줍니다.
[::-1]은 아스키에서만 안전하며, UTF-8 바이트 문자열에서는 디코딩 오류를 냅니다.itertools.islice를 사용합니다.센서 로그나 시계열 CSV를 다룰 때 "10초 간격 원본에서 30초 단위만 남기는" 다운샘플링을 자주 하게 됩니다. 이때 rows[::3][10:-10]처럼 간격과 구간을 분리해 두면 나중에 샘플링 주기만 바꾸는 수정이 훨씬 수월해집니다.
파일을 바이트로 읽어 처리하는 파서라면 data[::-1] 같은 코드를 팀 컨벤션에서 금지 목록에 올려 두는 것이 좋습니다. 테스트 데이터가 영문일 때는 멀쩡히 통과하다가 한글 데이터가 들어오는 운영 환경에서만 터지는 유형의 버그이기 때문입니다.