
파이썬을 처음 배우는 입문자부터 실무에서 복잡한 알고리즘을 구현하는 전문가까지, 가장 빈번하게 사용하면서도 간혹 헷갈리는 개념이 바로 range() 함수의 범위입니다. "range(0, 10)을 호출하면 10이 포함될까?"라는 질문에 대한 답은 명확합니다. "10은 포함되지 않습니다." 하지만 단순히 '포함되지 않는다'는 사실을 암기하는 것보다, 파이썬이 왜 이런 설계를 선택했는지 그 기저에 깔린 컴퓨터 과학적 철학을 이해하는 것이 중요합니다. 본 포스팅에서는 range() 함수의 동작 원리부터 'Off-by-one error'를 방지하는 설계의 이점, 그리고 실무 활용 팁까지 심도 있게 다룹니다.
1. range() 함수의 구조와 기본 원리
파이썬의 range()는 연속된 정수를 생성하는 불변(Immutable) 시퀀스 타입입니다. 기본 구조는 다음과 같습니다.
range(stop): 0부터 stop 전까지의 정수 생성range(start, stop): start부터 stop 전까지의 정수 생성range(start, stop, step): start부터 stop 전까지 step만큼 건너뛰며 생성
핵심은 'start는 포함(inclusive), stop은 제외(exclusive)'라는 규칙입니다. 수학적으로 표현하면 $[start, stop)$의 반개구간(half-open interval) 형태를 띱니다.
2. 왜 마지막 숫자를 제외하는가? (설계의 미학)
파이썬의 창시자 귀도 반 로섬(Guido van Rossum)을 비롯한 초기 설계자들이 마지막 인덱스를 제외한 이유는 매우 실용적입니다.
가. 길이 계산의 편의성
range(start, stop)에서 생성되는 요소의 개수는 단순히 stop - start로 계산됩니다. 예를 들어 range(0, 10)의 요소 개수는 $10 - 0 = 10$개입니다. 만약 10을 포함했다면 $10 - 0 + 1$이라는 불필요한 연산이 추가되었을 것입니다.
나. 인덱싱과의 조화
파이썬의 리스트 인덱스는 0부터 시작합니다. 길이가 10인 리스트 my_list의 마지막 인덱스는 9입니다. range(10)을 사용하여 루프를 돌리면 인덱스 범위와 정확히 일치하게 됩니다.
다. 연속적인 구간 표현
두 개의 range를 이어 붙일 때 경계값이 겹치지 않습니다. range(0, 5)와 range(5, 10)을 사용하면 0부터 9까지 빈틈없이 연결됩니다. 만약 끝값을 포함했다면 5가 중복되었을 것입니다.
3. range() 범위 및 동작 비교 요약
다양한 조건에서의 range() 동작을 표를 통해 비교해 보겠습니다.
| 코드 예시 | 시작값(Inclusive) | 종료값(Exclusive) | 생성되는 숫자 목록 | 요소 개수 |
|---|---|---|---|---|
range(10) |
0 | 10 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 | 10 |
range(5, 10) |
5 | 10 | 5, 6, 7, 8, 9 | 5 |
range(0, 10, 2) |
0 | 10 | 0, 2, 4, 6, 8 | 5 |
range(10, 0, -1) |
10 | 0 | 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 | 10 |
4. Sample Example: 실무 활용 및 실수 방지
실제 코드에서 range()를 사용할 때 겪을 수 있는 상황들을 살펴보겠습니다.
사례 1: 리스트 순회 (Standard Way)
fruits = ["apple", "banana", "cherry"]
# fruits의 길이는 3, range(3)은 0, 1, 2를 생성
for i in range(len(fruits)):
print(f"Index {i}: {fruits[i]}")
사례 2: 1부터 10까지 포함하여 출력하고 싶을 때
현실 세계의 요구사항이 "1부터 10까지 출력해줘"라면 코드는 다음과 같이 작성해야 합니다.
# 10을 포함하려면 stop 인자에 11을 전달해야 함
for n in range(1, 11):
print(n, end=" ")
# 출력: 1 2 3 4 5 6 7 8 9 10
사례 3: range 객체의 메모리 효율성
range()는 숫자를 미리 다 만들어두지 않고, 필요할 때마다 생성하는 'Lazy Evaluation' 방식을 사용합니다. 따라서 range(0, 100000000)을 작성해도 메모리를 거의 차지하지 않습니다.
5. 고급 팁: 인덱싱과 슬라이싱의 일관성
파이썬의 슬라이싱 규칙(list[start:stop]) 역시 range()와 동일한 반개구간 논리를 따릅니다. my_list[0:10]은 0번부터 9번 인덱스까지를 의미합니다. 이러한 '일관성'은 파이썬이 코드 가독성이 높고 오류가 적은 언어로 평가받는 이유 중 하나입니다.
6. 결론
range(0, 10)에 10이 포함되지 않는 것은 단순한 문법적 약속이 아니라, 인덱싱의 편의성, 길이 계산의 직관성, 메모리 효율성을 모두 고려한 결과입니다. "종료값(stop)은 항상 내가 원하는 마지막 숫자보다 1이 커야 한다"는 점만 기억한다면, 루프 제어에서 발생할 수 있는 대부분의 논리 오류를 예방할 수 있을 것입니다.
내용 출처 및 참고 자료:
- Python Official Documentation: The range Iterable
- "Fluent Python" by Luciano Ramalho (O'Reilly Media)
- Python Software Foundation - Built-in Functions: range
'Artificial Intelligence > 60. Python' 카테고리의 다른 글
| [PYTHON] if, elif, else 사용 시 주의할 점 : 효율적인 조건문 설계의 정석 (0) | 2026.02.07 |
|---|---|
| [PYTHON] 논리 연산자 and, or, not의 우선순위와 실무 활용 가이드 (0) | 2026.02.07 |
| [PYTHON] 리스트 정렬의 모든 것 : sort()와 sorted()의 내부 메커니즘과 실전 최적화 전략 (0) | 2026.02.06 |
| [PYTHON] 튜플의 불변성(Immutability) : 수정 불가능한 구조가 설계된 이유와 실전 활용 가치 (0) | 2026.02.06 |
| [PYTHON] 딕셔너리 안전 접근 가이드 : KeyError 방지를 위한 get() 메서드와 고급 방어 기법 (0) | 2026.02.06 |