
파이썬은 for와 while 루프 바로 뒤에 else 블록을 붙일 수 있게 허용합니다. 다른 언어에서 보기 힘든 문법이라 처음 발견하면 뭔가 대단한 기능처럼 느껴지지만, 실제 동작을 확인하고 나면 대부분 고개를 갸웃하게 됩니다.
혼란의 원인은 else라는 단어가 파이썬 안에서 이미 두 가지 의미로 쓰이고 있기 때문입니다. if/else에서는 "앞 조건이 거짓일 때", try/except/else에서는 "예외가 없었을 때" 실행됩니다. 앞의 경험을 그대로 가져오면 루프 뒤의 else는 "루프가 한 번도 안 돌았을 때"로 읽히는데, 실제로는 정반대에 가깝습니다. 루프가 끝까지 돌면서 break를 만나지 않은 경우에 실행됩니다. 문법 하나를 이해하려고 매번 문서를 다시 열어야 한다면, 그 문법은 팀 코드에 들어올 자격이 없습니다.
for i in range(3):
print('루프', i)
else:
print('else 블록 실행') # break가 없었으므로 실행됩니다
# 더 헷갈리는 경우입니다
for x in []:
print('절대 실행되지 않습니다')
else:
print('그런데 이 줄은 실행됩니다') # 빈 시퀀스여도 else는 돕니다
while False:
print('여기도 실행되지 않습니다')
else:
print('이 줄도 실행됩니다') # 조건이 처음부터 거짓이어도 마찬가지입니다
루프가 한 번도 돌지 않았는데 else가 실행된다는 사실이 직관과 정면으로 충돌합니다. 실제 규칙은 "break로 빠져나오지 않았다면 실행된다"이지만, 이 규칙을 코드만 보고 떠올릴 수 있는 사람은 많지 않습니다. 읽는 사람이 규칙을 외워야만 이해되는 코드는 유지보수 비용을 그대로 남에게 떠넘기는 것과 같습니다.
def coprime(a, b):
"""두 수가 서로소인지 판별합니다."""
for i in range(2, min(a, b) + 1):
if a % i == 0 and b % i == 0:
return False # 조건을 만난 즉시 결과를 확정합니다
return True # 끝까지 돌았다면 서로소입니다
print(coprime(4, 9)) # True
print(coprime(3, 6)) # False
같은 로직을 for/else로 쓰면 루프와 else 사이의 숨은 규칙을 알아야 읽히지만, 헬퍼 함수로 감싸면 return 위치만 보고도 흐름이 즉시 파악됩니다. 결과를 변수에 담아야 하는 상황이라면 플래그 변수를 쓰는 방식도 있습니다.
def coprime_alternate(a, b):
is_coprime = True
for i in range(2, min(a, b) + 1):
if a % i == 0 and b % i == 0:
is_coprime = False
break
return is_coprime
두 방식 모두 줄 수는 비슷하지만, 읽는 사람이 파이썬의 예외적인 문법 규칙을 기억할 필요가 없다는 점에서 확실히 낫습니다. 가독성은 짧게 쓰는 것이 아니라 오해할 여지를 줄이는 것으로 얻어집니다.
else 블록은 break를 만나지 않고 루프가 끝났을 때 실행됩니다.else는 실행됩니다.if/else, try/except/else와 의미가 달라 오해를 부르기 쉽습니다.return이나 플래그 변수로 표현합니다.이 Better Way는 "쓸 곳"보다 "걷어낼 곳"을 찾는 항목에 가깝습니다. 목록에서 조건에 맞는 항목을 찾고 없으면 기본값을 쓰는 탐색 코드가 대표적인 후보입니다. 재고에서 사용 가능한 창고를 고르는 로직 같은 것이 여기에 해당하며, find_available_warehouse()처럼 이름 붙은 함수로 빼내고 못 찾았을 때 None을 반환하게 하면 호출부가 훨씬 명확해집니다.
리뷰어 입장에서도 유용한 신호입니다. 코드에서 for ... else를 발견하면 그 자리에 이름이 필요한 함수가 숨어 있을 가능성이 높습니다. 문법을 지적하기보다 "이 루프 전체가 무엇을 판정합니까?"라고 물어보면, 대개 함수 이름이 자연스럽게 나옵니다.