Skip to content
This page has been auto-translated and may contain errors.View in English

제어 흐름

docs.scrimba.com

지금까지 작성한 모든 프로그램은 같은 방식으로 실행됩니다: 위에서 아래로, 한 번에 한 줄씩. 단순한 스크립트에는 충분하지만, 실제 프로그램은 의사 결정을 하고 작업을 반복해야 합니다. 퀴즈는 답이 맞는지 확인해야 합니다. 게임은 플레이어가 이기거나 질 때까지 계속 실행되어야 합니다. 이 장에서는 프로그램을 분기하고 반복하는 방법을 다룹니다.

제어 흐름은 프로그램이 가는 경로를 결정합니다. 조건(if/elif/else)은 분기 간 선택, 반복(while, for)은 블록 반복입니다. Python의 for는 인덱스를 세는 대신 항목을 하나씩 살펴보므로 많은 것들을 깔끔하게 반복할 수 있습니다. 이 장에서는 각 도구를 언제 사용할지, 각 도구가 초래할 수 있는 실수가 무엇인지를 다룹니다.

Python은 세 가지 제어 흐름 도구를 제공합니다: 분기를 위한 if/elif/else과 반복을 위한 whilefor. for 반복문은 반복 가능한 객체(리스트나 파일처럼 항목을 하나씩 살펴볼 수 있는 것)를 살펴보고 항목이 끝나면 스스로 멈추므로, 인덱스를 수동으로 관리할 일이 거의 없습니다. while 반복문은 조건이 참인 동안 계속 실행됩니다. breakcontinue는 자신을 둘러싼 가장 가까운 반복문만 영향을 주며, 반복문의 else 절(나중에 다룸)은 break 없이 반복문이 완료될 때만 실행됩니다. 이 장의 나머지 부분은 올바른 것을 선택하고 각각이 초래할 수 있는 실패 모드를 피하는 것에 관한 것입니다.

비교

의사 결정을 하기 전에 사물을 비교해야 합니다. 비교 연산자는 True 또는 False를 반환합니다. 초반에 올바르게 하는 것이 가장 중요한 것: =는 값을 할당하고, ==는 두 값이 같은지 확인합니다. 이 둘을 헷갈리는 것이 초보자가 가장 흔히 하는 실수입니다.

비교 연산자는 bool을 반환하며, Python은 이들을 연결할 수 있습니다: 0 < x < 100 < x and x < 10을 의미하며, 대부분의 다른 언어는 좌에서 우로 읽으면서 틀립니다. 문자열은 사전 순서로 문자 단위로 비교되므로 대문자가 소문자보다 먼저 정렬됩니다. 값을 비교할 때는 ==를 사용하세요; is를 원할 일은 거의 없습니다(심화 내용에서 다룸).

비교는 bool을 반환하며, 실제 코드에서는 두 가지 세부 사항이 중요합니다. 첫째, Python은 연결된 비교를 지원합니다: a < b < ca < b and b < c로 읽으며, 중간 값 b는 한 번만 평가됩니다(비용이 많이 드는 호출이라면 이를 알 가치가 있습니다). 둘째, ==는 두 값이 같은지 확인하고, is동일성(두 이름이 메모리의 정확히 같은 객체를 가리키는지)을 확인합니다. 이 둘은 서로 바꿀 수 없습니다: 값을 비교할 때는 ==를 사용하고, None, True, False에는 is를 사용하세요(유일한 객체를 원할 때). 문자열이나 숫자에 is를 사용하면 작은 입력에서는 통과하고 큰 입력에서는 실패하는 버그가 발생합니다.

python
5 > 3     # True
5 < 3     # False
5 == 5    # True   (주의: 이중 등호; =는 할당, ==는 비교)
5 != 3    # True   ("같지 않음")
5 >= 5    # True   ("이상")
5 <= 4    # False  ("이하")

===의 구분은 초보자를 거의 항상 혼란스럽게 합니다. 할당(=)은 값을 저장하고, 비교(==)는 두 값이 같은지 확인합니다.

문자열도 비교할 수 있습니다. Python은 문자 단위로 알파벳 순서대로 비교하며, 이는 문자열 장에서 더 자세히 다룹니다:

python
"apple" == "apple"   # True
"apple" < "banana"   # True  (a가 b보다 먼저 옴)
"apple" == "Apple"   # False (대소문자 구분)
Juno비교=는 값을 저장하고, ==는 두 값이 같은지 확인합니다. 피곤할 때도 느려서 그것을 두 번 확인합니다. 그것 때문에 혼란스러워하면 당신만 그런 게 아닙니다. 비교는 True 또는 False를 반환하며, 문자열에도 작동하고, 알파벳 순서입니다.
Juno비교 비교는 `bool`을 반환합니다. `0 < x < 10`처럼 연결된 형태는 `0 < x and x < 10`으로 읽으며, 대부분의 다른 언어는 이것을 틀립니다. 문자열 비교는 Unicode 순서이므로 대소문자를 구분하여 정렬합니다.
Juno비교 값 비교에는 `==`, 동일성 비교에는 `is`를 사용하고, `is`는 `None`, `True`, `False`에만 사용하세요. 문자열이나 정수에 `is`를 사용하면 작은 입력에서는 통과하고 큰 입력에서 실패합니다. 연결된 비교는 중간 항을 한 번만 평가하므로 비용이 많이 드는 경우 유용합니다.

조건 결합

and, or, not은 비교를 결합합니다. and는 양쪽이 모두 참이어야 합니다. or는 한쪽이라도 참이면 됩니다. not은 결과를 뒤집습니다. 이를 사용하면 "점수가 통과 AND 사용자가 활성" 같은 실제 조건을 표현할 수 있습니다.

andor는 단락 평가(short-circuit): and는 첫 번째 거짓 값에서 멈추고, or은 첫 번째 참 값에서 멈춥니다. 이들은 단순한 True 또는 False가 아니라 실제 피연산자를 반환하므로, name or "guest"는 깔끔한 대체 값 제공 방법입니다. not은 항상 일반 bool을 제공합니다.

andorTrue 또는 False를 반환하지 않고, 피연산자 중 하나를 반환합니다. and는 첫 번째 거짓 값을 반환하거나, 모두 참이면 마지막 값을 반환합니다; or은 첫 번째 참 값을 반환하거나, 모두 거짓이면 마지막 값을 반환합니다. 왼쪽이 결과를 정하면 오른쪽은 평가되지 않으며, 이 단락 평가 보장(Python이 답을 알게 되면 멈춤)이 핵심입니다: user and user.nameuser가 존재할 때만 user.name을 읽으므로 누락된 값에 오류가 발생하지 않습니다. 반대 측면은 실제 실패 모드입니다: 오른쪽이 로깅이나 쓰기 같이 당신이 의존하던 부작용을 가지고 있다면, 왼쪽이 단락 평가될 때 조용히 실행되지 않습니다.

python
age = 25
score = 88

age >= 18 and score >= 80    # True  (둘 다 참이어야 함)
age < 18 or score >= 80      # True  (최소한 하나는 참이어야 함)
not age >= 18                # False (결과를 뒤집음)

and는 양쪽을 필요로 합니다. or는 한쪽이라도 필요로 합니다. not은 반대로 합니다.

Juno조건 결합and는 양쪽이 참이어야 하고, or은 한쪽이라도 참이어야 하며, not은 답을 뒤집습니다. 이것이 "점수가 통과이고 사용자가 활성"처럼 대부분의 실제 조건을 작성하는 방법입니다. Python은 답을 알게 되자마자 확인을 멈춥니다.
Juno조건 결합and는 첫 번째 거짓 값에서 멈추고, or은 첫 번째 참 값에서 멈추며, 둘 다 실제 피연산자를 반환하지만 항상 `True`/`False`는 아닙니다. 그 단락 평가 때문에 `x and x.method()`는 `x`가 비어 있을 때도 안전합니다.
Juno조건 결합and/or은 피연산자를 반환하고, 왼쪽이 결과를 정하면 오른쪽은 건너뜁니다. `user and user.name` 같은 보호 조건에 의존하세요. 다른 측면을 주의하세요: 오른쪽에 부작용이 있으면 단락 평가될 때 조용히 실행되지 않습니다.

참 같은 값과 거짓 같은 값

Python의 모든 값은 True 또는 False가 아니어도 부울 해석을 가집니다. 빈 문자열, 0, 빈 리스트, None은 모두 조건에서 False처럼 동작합니다. 다른 모든 것은 True처럼 동작합니다. 이는 if results:if len(results) > 0:을 작성하지 않고도 리스트가 비어 있지 않은지 확인한다는 의미입니다.

Python의 거짓 값은 False, 0, 0.0, "", [], (), {}, set(), None입니다. 다른 모든 것은 참이므로, 빈 컨테이너는 거짓이고 비어 있지 않은 컨테이너는 참입니다. 이것이 if results:가 관례적인 빈 확인인 이유입니다: if len(results) > 0: 대신 이것을 작성하세요.

객체를 조건에 넣으면 Python은 그것의 __bool__ 메서드를 호출하여 예 또는 아니오 답을 요청합니다(Python이 뒤에서 호출하는 던더, 이중 밑줄 메서드 중 하나). 타입이 __bool__을 정의하지 않으면 Python은 __len__으로 돌아가므로, 길이가 0인 anything은 거짓입니다. 표준 거짓 값은 False, 0, 0.0, "", 빈 컨테이너([], (), {}, set()), None입니다; 다른 모든 것은 참입니다. 주의할 함정: if results:는 빈 리스트뿐만 아니라 None에도 거짓이므로, "비어 있음"과 "아직 설정되지 않음"을 구분해야 하면 참성에 의존하지 말고 if results is None:을 테스트하세요.

python
# 이것들은 모두 조건에서 False처럼 동작합니다:
False, 0, 0.0, "", [], {}, (), None

# 다른 모든 것은 True처럼 동작합니다

이는 if results:가 "리스트가 비어 있지 않으면"이라고 말하는 자연스러운 방식이며, if name:은 문자열에 내용이 있는지 확인합니다.

Juno참 같은 값과 거짓 같은 값 조건에서 빈 것들과 0은 `False`로 계산됩니다: `0`, `""`, `[]`, `None`. 다른 모든 것은 `True`로 계산됩니다. 그래서 `if results:`가 "리스트에 뭔가 있으면" 정도로 읽히는 이유는 `len()`이 필요 없기 때문입니다.
Juno참 같은 값과 거짓 같은 값 거짓은 `False`, 0, 빈 문자열, 빈 컨테이너, `None`입니다; 다른 모든 것은 참입니다. 빈 리스트는 거짓이고 가득 찬 것은 참입니다. 그래서 `if my_list:`가 `len()` 없이도 빈 확인으로 작동하는 전부입니다.
Juno참 같은 값과 거짓 같은 값 조건은 `__bool__`을 호출하고, 그것이 없으면 `__len__`을 호출하므로, 당신의 고유한 클래스는 자신의 참성을 선택할 수 있습니다. 함정은: `if results:`는 빈 리스트와 `None`을 구분할 수 없습니다. 그 구분이 중요하면 대신 `is None`을 사용하세요.

if / elif / else

if 문은 조건이 True일 때만 코드 블록을 실행합니다. elif는 첫 번째가 거짓이었으면 확인할 더 많은 조건을 추가합니다. else는 어떤 조건과도 일치하지 않은 모든 것을 처리합니다. Python은 중괄호가 아닌 들여쓰기를 사용하여 각 블록에 속하는 것을 정의합니다.

if/elif/else는 위에서 아래로 조건을 평가하고 첫 번째 일치 블록을 실행합니다. Python은 들여쓰기(관례상 4개 공백)를 사용하여 블록 범위를 정의합니다; 불일치한 들여쓰기는 SyntaxError입니다. 한 분기만 실행됩니다: 조건이 일치하면 후속 elifelse는 모두 건너뜁니다.

Python은 위에서 아래로 조건을 확인하고 정확히 한 분기를 실행합니다: 조건이 참인 첫 번째 분기이거나, 어느 것도 일치하지 않으면 else. 각 조건은 게으르게 평가됩니다. 즉, Python은 그 위의 모든 조건이 거짓이었으면 그것만 테스트합니다. 따라서 순서는 논리의 일부입니다. 가장 저렴하거나 가능성이 높은 테스트를 먼저 배치하고, 겹치는 범위를 더 좁은 경계가 더 넓은 경계 앞에 오도록 정렬하거나, 후속 분기가 도달 불가능해집니다. 들여쓰기는 블록 구조입니다(스타일 선택이 아니기 때문에 탭과 공백을 섞으면 TabError이며 의미가 조용히 이동하지 않습니다).

python
score = 87

if score >= 90:
    print("A 학점")
elif score >= 80:
    print("B 학점")
elif score >= 70:
    print("C 학점")
else:
    print("C 미만")

규칙:

  • if는 필수이며 항상 먼저 옵니다
  • elif("else if"의 약자)는 선택사항이며 필요한 만큼 많이 사용할 수 있습니다
  • else는 선택사항이며 일치하지 않은 모든 것을 처리하고 마지막에 옵니다
  • Python은 들여쓰기(4개 공백)를 사용하여 각 블록에 속하는 것을 표시합니다; 중괄호는 없습니다

들여쓰기는 선택사항이거나 미용적이지 않습니다. Python은 이것을 사용하여 구조를 정의합니다. 불일치한 들여쓰기는 구문 오류입니다.

Junoif / elif / else `if`는 조건이 참일 때 블록을 실행하고, `elif`는 더 많은 경우를 확인하며, `else`는 나머지를 처리합니다. 첫 번째 일치만 실행됩니다. 초기에 나를 당황하게 한 것: 들여쓰기는 Python에 각 블록이 시작되고 끝나는 위치를 알려주므로 일관성 있게 유지하세요.
Junoif / elif / else 조건은 위에서 아래로 확인되고 첫 번째 일치만 실행되므로 순서가 중요합니다: 좁은 범위를 더 넓은 범위 앞에 배치하세요. 들여쓰기(4개 공백)는 블록을 표시하며, 이것을 섞으면 `SyntaxError`입니다(조용한 버그가 아님).
Junoif / elif / else 조건은 게으르게 평가되고 위에서 아래로 진행되므로 가장 저렴하거나 가능성이 높은 테스트를 먼저 배치하고 겹치는 경계를 좁은 것-넓은 것 순서로 배치하거나 분기가 죽습니다. 들여쓰기는 스타일이 아니라 구조이며, 탭-공백은 의미를 조용히 이동시키지 않는 `TabError`입니다.

한 줄 조건

단순한 예/아니오 할당의 경우 Python에는 삼항 표현식이라는 컴팩트한 한 줄 형태가 있습니다: true일_때_값 if 조건 else false일_때_값. 논리가 작고 문장처럼 읽힐 때만 사용하세요.

조건부 표현식(삼항 연산자)은 조건을 기반으로 두 값 중 하나로 평가됩니다. 이는 명령문이 아니라 표현식이므로 값이 예상되는 모든 곳에 나타날 수 있습니다: f-문자열 내부, 함수 인수로, 할당에서. 단순한 예/아니오의 경우 사용하세요; elif를 포함하는 것은 전체 버전을 작성하세요.

조건부 표현식 x if 조건 else y는 조건을 평가하고 다른 하나를 실행하지 않고 한쪽을 반환하므로, 한 분기가 오류를 발생시킬 곳에서 사용하기에 안전합니다. 표현식이므로 값이 가는 어디든 맞습니다: f-문자열, 함수 인수, 기본값. 규칙은 멈출 때를 아는 것입니다. elif를 보유할 수 없으며 이를 연결하면(a if p else b if q else c) 대체하는 블록보다 나쁘게 읽히므로, 한 가지 이상의 결정이 있으면 전체 if/elif/else를 사용하세요. 나중에 이것을 유지하는 사람이 당신에게 감사할 것입니다.

python
label = "통과" if score >= 50 else "불통과"

이는 삼항 표현식입니다; 문장처럼 읽힙니다. 논리가 작을 때 사용하세요. elif를 포함하는 것은 전체 버전을 작성하세요.

Juno한 줄 조건 `true일_때_값 if 조건 else false일_때_값`은 한 줄에 두 값 중 하나를 선택합니다. `"통과" if score >= 50 else "불통과"` 같은 깔끔한 작은 선택에 좋습니다. 논리가 그것 이상으로 성장하면 일반적인 `if` 블록으로 돌아가세요, 더 잘 읽힐 것입니다.
Juno한 줄 조건 삼항 표현식은 표현식이므로 값이 가는 모든 곳에 맞습니다: f-문자열, 인수, 할당. 단순한 두 방향 선택에 사용하세요. `elif`가 있는 것은 전체 블록에 속합니다.
Juno한 줄 조건 `x if cond else y`는 선택한 한쪽만 실행하므로 한 분기가 발생시킬 곳에서 안전하고, 모든 표현식 슬롯에 떨어집니다. 연결하지 마세요: 스택된 삼항은 대체하는 블록보다 나쁘게 읽히므로 한 가지 결정을 지나 명확히 작성하세요.

while 반복문

while 반복문은 조건이 True인 동안 블록을 반복합니다. 반복문이 몇 번 실행되어야 하는지 미리 알 수 없을 때 사용하세요. 예를 들어 유효한 입력을 기다리거나 작업이 성공할 때까지 재시도할 때입니다.

while은 각 반복 전에 조건을 평가하고 조건이 참일 때만 블록을 실행합니다. 종료 조건이 반복문 내부에서 변경되는 것에 따라 달라질 때 사용하세요. 반복 횟수가 알려져 있거나 컬렉션을 반복하면, for가 보통 더 깔끔합니다.

while은 매번 조건을 다시 테스트하며, 본문이 그 조건을 변경하므로 실패 모드는 구조적입니다: 종료를 향해 이동하는 것을 잊으면(카운터 감소, 큐 소비) 끝나지 않는 반복문이 되고 프로세스가 멈춥니다. 종료가 본문의 중간이나 끝에서 결정되어야 할 때 관례적인 형태는 while True와 내부 break입니다. 이는 조건을 머리글에 강제하는 대신 실제로 일어나는 곳에 보관합니다. 컬렉션을 걷거나 알려진 횟수만큼 실행할 때는, for 반복문이 더 깔끔하고 카운터의 오류 제거 위험을 제거합니다.

python
lives = 3

while lives > 0:
    print(f"남은 목숨: {lives}")
    lives -= 1

print("게임 오버")

while은 미리 반복 횟수를 알 수 없을 때 가장 좋습니다. 알 때, 또는 컬렉션을 반복할 때는 for가 더 깔끔합니다.

Junowhile 반복문 `while`은 조건이 참인 동안 블록을 계속 실행합니다. 유효한 입력을 기다리는 등 몇 라운드가 필요한지 모를 때 사용하세요. 반복문 내부의 무언가가 종료를 향해 이동하는지 확인하거나 영원히 실행될 것입니다. 나는 한 번 이상 그 실수를 했습니다.
Junowhile 반복문 `while`은 각 통과 전에 조건을 확인하고 본문이 그것을 변경합니다. 반복문 내부에서 일어나는 것에 따라 종료가 달라질 때 사용하세요. 세거나 컬렉션을 걸으면 `for`가 더 깔끔한 선택입니다.
Junowhile 반복문 본문이 종료를 향해 이동해야 하거나 멈춤이 발생하므로 먼저 확인할 것입니다. `while True`와 내부 `break`는 종료가 본문 중간에 있을 때의 깔끔한 형태입니다. 알려진 개수나 컬렉션의 경우 `for`를 선호하고 오류 제거 위험을 건너뜁니다.

break 및 continue

break는 반복문을 즉시 종료하며, 남은 반복이 몇 개이든 무시합니다. continue는 현재 반복의 나머지를 건너뛰고 조건 확인으로 돌아갑니다. 둘 다 자신이 내부에 있는 가장 안쪽 반복문에만 영향을 줍니다.

break는 가장 가까운 둘러싼 반복문을 종료하고 그 이후의 첫 번째 명령문으로 제어를 이동합니다. continue는 현재 반복문 본문의 나머지를 건너뛰고 조건 확인에서 다시 시작합니다(for 반복문에서 다음 반복). 둘 다 가장 안쪽 둘러싼 반복문에만 영향을 줍니다.

break는 반복문을 즉시 떠나며 else 절이 있으면 건너뜁니다; continue는 다음 통과를 위해 반복문 머리글로 직접 뛰어갑니다. 둘 다 가장 가까운 둘러싼 반복문에 바인딩되며, Python에는 이름 붙은 break이 없으므로 내부 반복문에서 외부 반복문에 도달할 수 없습니다. 한 번에 여러 수준을 벗어나야 하면 깔끔한 움직임은 중첩된 반복문을 함수에 올리고 return하는 것입니다. 이는 누락된 이름표를 피하기 위해 두 반복문을 통해 플래그 변수를 실행하는 것보다 더 잘 읽힙니다. 플래그는 작동하지만 누락된 이름표를 피하기 위해 추가하는 if found:는 미래의 편집이 그것을 잘못 얻을 수 있는 곳입니다.

break는 반복문을 즉시 종료합니다:

python
target = 5
num = 0

while True:
    num += 1
    if num == target:
        print(f"{target}을 찾았습니다")
        break   # 반복문 종료

while True:break는 종료 조건이 복잡하거나 반복문 본문의 끝에서 일어나야 할 때의 유효하고 일반적인 패턴입니다.

continue는 현재 반복의 나머지를 건너뛰고 조건 확인으로 돌아갑니다:

python
num = 0

while num < 10:
    num += 1
    if num % 2 == 0:
        continue    # 짝수 건너뜀
    print(num)      # 홀수만 인쇄: 1, 3, 5, 7, 9
Junobreak 및 continue `break`는 바로 반복문 밖으로 뛰어갑니다, `continue`는 이 라운드의 나머지를 건너뛰고 조건 확인으로 돌아갑니다. 둘 다 직접 내부에 있는 반복문에만 영향을 줍니다. `while True:`는 처음에 이상하게 읽히지만 뭔가 일어날 때까지 반복하는 일반적인 방법입니다.
Junobreak 및 continue `break`는 가장 가까운 반복문을 떠나고, `continue`는 그것을 조건부터 다시 시작합니다. 어느 것도 외부 반복문에 도달하지 않습니다. 중첩된 반복문을 벗어나려면 플래그를 설정하거나 내부 반복문을 함수로 끌어올리고 `return`하세요.
Junobreak 및 continue 둘 다 가장 가까운 반복문에 바인딩되며 이름 붙은 `break`이 없으므로 여러 수준을 벗어나면 플래그 또는 더 깔끔하게, `return`이 있는 함수를 의미합니다. `break`는 반복문의 `else`를 건너뛰기도 하며, 이것이 정확히 검색 패턴이 의존하는 것입니다.

for 반복문

for 반복문은 시퀀스를 한 번에 한 항목씩 살펴봅니다: 리스트, 문자열, 숫자 범위. for 이후에 명명한 변수는 차례로 각 항목을 받습니다. 카운터를 관리하거나 길이를 직접 확인하지 않습니다.

for는 주어진 것에 한 번에 한 항목을 요청하고 항목이 끝나면 멈춥니다. 인덱스 위치를 세는 대신. 이것이 리스트보다 훨씬 더 많은 것에서 작동하는 이유입니다: 문자열, 딕셔너리, 범위, 파일 객체 모두 같은 방식으로 반복되며, 길이 확인이나 카운터가 없습니다. 컬렉션을 걸을 때마다 while 대신 for를 사용하세요.

for는 인덱스를 세지 않으며, 주어진 것에 한 번에 한 항목을 주고 멈출 때까지 기억하는 반복자(객체)를 요청하고 항목을 끌어당깁니다. 실제 보상은 for가 리스트보다 훨씬 더 많이 작동한다는 것입니다: 문자열, 딕셔너리, 범위, 파일 객체, 게으른 소스(필요에 따라 계산된 시퀀스, 메모리에 모두 보유되지 않음)인 생성기는 모두 같은 방식으로 반복됩니다. 마지막 그룹이 하나의 함정이 있는 곳입니다: 게으른 반복자는 단일 사용이므로, for 반복문이 생성기나 파일을 소비한 후 다시 반복하면 아무것도 나오지 않습니다. 항목이 두 번 필요하면 먼저 리스트로 구체화하거나 소스를 다시 여세요.

python
players = ["민지", "준호", "승미"]

for player in players:
    print(f"안녕하세요, {player}님!")

for 반복문은 또한 문자열(문자 단위로 반복)과 다른 모든 시퀀스 유형에서 작동합니다.

Junofor 반복문 `for item in sequence`는 차례로 각 항목을 살펴보며, 카운터나 길이에 직접 손을 댑니다. 리스트, 문자열, 범위, 많은 것에서 작동합니다. 다른 언어에서 인덱스를 관리하지 않는 것이 처음에는 이상하게 느껴졌지만 그다음 그것이 선물처럼 느껴졌습니다.
Junofor 반복문 `for`는 한 번에 한 항목을 끌어당기다가 끝날 때까지 멈추므로 인덱싱된 시퀀스에 연결되지 않습니다. 리스트, 문자열, 딕셔너리, 범위, 파일: 모두에 같은 반복문입니다. 인덱스 부기 없음, 길이 확인 없음.
Junofor 반복문 `for`는 모든 반복자를 구동하므로 리스트, 파일, 생성기가 모두 같게 반복됩니다. 함정은 게으른 것들입니다: 생성기나 파일 반복자는 단일 사용이며, 한 통과 후 소비됩니다. 항목이 두 번 필요하면 먼저 리스트로 끌어당기거나 소스를 다시 여세요.

range()

range()는 반복할 숫자 시퀀스를 생성합니다. range(5)0, 1, 2, 3, 4를 제공합니다. 시작, 끝, 스텝 크기를 제어할 수 있습니다. 반복문을 특정 횟수만큼 실행하려면 사용하세요.

range(start, stop, step)start에서 stop(포함 제외)까지, step으로 건너뛰는 정수를 생성합니다. 게으른 시퀀스입니다: 리스트를 생성하지 않으며, 필요에 따라 숫자를 생성합니다. 이렇게 하면 range(10_000_000)이 메모리 효율적입니다. 3가지 형태 모두 역순 계산을 위해 음수 인수를 허용합니다.

range는 숫자 리스트를 구성하지 않으며, 시작, 정지, 스텝만 저장하고 진행하면서 각 값을 함수합니다. 이렇게 하면 range(10_000_000)range(10)과 같은 메모리 비용을 가지므로 반복만 필요하면 range를 직접 반복하고 list()로 래핑하지 마세요. 천만 개의 저장된 정수를 지불합니다. 그래도 시퀀스처럼 동작합니다: 인덱싱, 슬라이싱, len() 요청, in으로 멤버십 테스트(모두 저렴함). list(range(n))을 호출하는 것은 실제로 보관할 실제 재사용 가능한 리스트가 필요할 때만 가치가 있습니다.

python
for i in range(5):
    print(i)    # 0, 1, 2, 3, 4

range()는 3가지 형태가 있습니다:

호출생성
range(5)0, 1, 2, 3, 4
range(2, 6)2, 3, 4, 5
range(0, 10, 2)0, 2, 4, 6, 8 (스텝 2)
range(5, 0, -1)5, 4, 3, 2, 1 (역순 계산)

range()은 리스트를 생성하지 않습니다. 한 번에 한 숫자를 생성하므로 매우 큰 범위에도 효율적입니다.

Junorange() `range(5)`는 `0, 1, 2, 3, 4`를 제공하며, 시작, 정지, 스텝을 설정할 수 있습니다. 반복문을 정해진 횟수만큼 실행하려면 사용하세요. 리스트를 구성하지 않으므로 라운드당 한 숫자를 제공합니다.
Junorange() `range(start, stop, step)`은 `start`에서 `stop` 직전까지 `step`으로 계산합니다. 게으른 시퀀스이므로 리스트를 구성하는 대신 필요에 따라 숫자를 생성하므로 `range(10_000_000)`이 거의 아무것도 아닙니다. 음수 스텝은 역순 계산합니다.
Junorange() `range`는 시작, 정지, 스텝만 저장하므로 크기에 관계없이 메모리가 평평합니다. 정말로 재사용 가능한 리스트가 필요하지 않으면 직접 반복하고 `list(range(n))`을 사용하지 마세요. 인덱싱, 슬라이싱, `in`과 `len()`을 여전히 저렴하게 지원합니다.

enumerate()

enumerate()는 반복하면서 인덱스와 값을 모두 제공하므로 카운터를 별도로 추적할 필요가 없습니다. i, player 부분은 각 반복마다 자동으로 값의 쌍을 받습니다.

enumerate(iterable, start=0)은 모든 반복자를 래핑하고 (index, value) 튜플을 생성합니다. start 매개변수는 카운터를 오프셋하지만 기본 인덱스는 변경하지 않습니다. 카운터 변수를 수동으로 관리하는 대신 enumerate()를 선호하세요; 더 깔끔하고 오류가 적습니다.

enumerate는 모든 반복 가능을 래핑하고 (count, value) 쌍을 반환하며, 이는 for 머리글의 언팩(Python이 한 단계에서 튜플의 각 부분을 자체 이름에 할당)으로 두 이름으로 분할합니다. 게으르고 카운터 이상을 추가하지 않으므로 맨몸의 반복문과 같은 비용입니다. 수동으로 관리되는 index = 0; index += 1 대신 항상 이것을 사용하는 이유는 신뢰성입니다: 수동 버전은 증분을 잊거나 잘못된 곳에 범프하는 버그를 초래하며, enumerate는 그 전체 실수 클래스를 제거합니다. 사람을 위해 출력을 번호 매길 때 start=1을 전달하세요. 사람은 0이 아닌 1부터 셉니다.

python
players = ["민지", "준호", "승미"]

for i, player in enumerate(players):
    print(f"{i + 1}. {player}")
# 1. 민지
# 2. 준호
# 3. 승미

i, player 구문은 언팩이라고 불립니다. Python은 (index, value) 쌍을 두 이름으로 자동 분할합니다.

기본적으로 enumerate()는 0에서 시작합니다. 시작 값을 전달하여 변경하세요:

python
for i, player in enumerate(players, start=1):
    print(f"{i}. {player}")    # 1에서 시작
Junoenumerate() `enumerate()`는 위치와 값을 함께 제공하므로 동기화 유지할 별도의 카운터가 없습니다. `i, player` 부분이 한 번에 둘 다 잡습니다. 사람이 읽을 리스트를 번호 매길 때 `start=1`을 전달하세요.
Junoenumerate() `enumerate(iterable, start=0)`은 `(index, value)` 쌍을 생성하며 `for` 머리글에서 바로 언팩합니다. 카운터 변수보다 선호하세요. 더 깔끔하고 증분하는 것을 잊을 수 없습니다. `start`는 표시된 숫자만 이동하며 실제 인덱스는 이동하지 않습니다.
Junoenumerate() 게으르고, 카운터 이상으로 무료이며, 수동 인덱스가 초래하는 "증분 잊음" 버그를 모두 삭제하므로 기본값으로 사용하세요. 각 항목은 튜플이므로 `for i, v` 언팩이 작동합니다. 인간 대면 번호 매김에는 `start=1`.

중첩 반복문

반복문을 다른 반복문 안에 넣을 수 있습니다. 내부 반복문은 외부 반복문의 각 단일 반복에 대해 완전히 실행됩니다. 이것은 격자, 조합 또는 두 가지 구조 수준의 모든 데이터를 처리하는 방법입니다.

중첩 반복문은 외부 길이 m, 내부 길이 n에 대해 O(m × n) 반복 개수를 가집니다. 중첩 반복문 내부의 breakcontinue는 가장 안쪽 반복문에만 영향을 줍니다. 여러 수준에서 벗어나려면 플래그 변수를 사용하거나 함수로 재구성하세요.

중첩 반복문은 곱셈됩니다: 길이 n의 내부 주위의 길이 m의 외부 반복문은 본문을 m 배 n 배 실행하므로 비용이 빠르게 오르고 큰 입력에 대한 반복문 쌍은 느린 스크립트 뒤의 보통의 용의자입니다. 세 번째 수준에 도달하기 전에, 데이터 구조가 실제 수정인지 묻기(딕셔너리 조회가 내부 검색 반복문을 붕괴시킬 수 있음). 두 시퀀스의 모든 조합이 필요하면(카르테시안 곱, 하나와의 모든 쌍), 표준 라이브러리에서 itertools.product이 중첩보다 더 잘 읽힙니다. 그리고 break는 오직 내부 반복문만 떠나는 것을 기억하세요: 둘 다 떠나려면 그것들을 함수로 끌어올리고 return하세요.

python
rows = [1, 2, 3]
cols = ["A", "B"]

for row in rows:
    for col in cols:
        print(f"{col}{row}", end=" ")
    print()   # 각 행 후 줄 바꿈
# A1 B1
# A2 B2
# A3 B3

중첩 반복문 내부의 breakcontinue는 가장 안쪽 반복문에만 영향을 줍니다.

Juno중첩 반복문 반복문 안의 반복문은 외부 반복문의 모든 단일 라운드에 대해 전체 내부 반복문을 실행합니다. 그것이 격자와 조합을 처리하는 방법입니다. `break`와 `continue`는 그것들이 앉은 반복문에만 작동하며, 여기서는 내부입니다.
Juno중첩 반복문 내부 반복문은 외부 스텝당 완전히 실행되므로 개수는 m 배 n, 큰 입력에서 보세요. `break`와 `continue`는 가장 안쪽 반복문에만 도달합니다. 둘을 벗어나려면 플래그를 사용하거나 그것들을 함수로 끌어올리고 `return`하세요.
Juno중첩 반복문 비용이 곱하므로 큰 입력에 대한 중첩 반복문은 보통의 느린 지점입니다; 때때로 딕셔너리 조회가 내부를 완전히 대체합니다. 모든 쌍에는 `itertools.product`이 중첩을 이깁니다. `break`는 오직 내부 반복문만 치므로 `return`이 있는 함수가 둘에서 나가는 깔끔한 방법입니다.

반복문-else

Python 반복문은 반복문이 break를 치지 않고 종료되었을 때만 실행되는 else 절을 가질 수 있습니다. 일반적으로 사용되지 않지만 "리스트를 검색하고 아무것도 찾지 못했으면 이것을 하세요"를 작성하는 가장 깔끔한 방법입니다.

for 또는 while 반복문의 else는 반복문이 정상적으로 완료(반복 가능을 소비하거나 조건이 거짓이 됨)될 때 break 없이 실행됩니다. 별도의 발견 플래그 변수를 필요로 하지 않고 "검색 및 발견되지 않으면 보고" 패턴입니다.

반복문 else는 반복문이 break 없이 완료될 때만 실행되며, 이는 대부분의 사람들이 읽는 것과 반대입니다(반복문이 실행되지 않았다는 의미가 아님). 그 이름이 정확히 독자를 혼동시키는 이유이므로 프로덕션의 규칙은 한 가지와 한 가지만을 위해 사용하는 것입니다: 검색-및-미발견 패턴. break는 히트를 표시하고 else는 미스를 보고합니다. 그것은 반복문을 통해 수행할 found = False 플래그를 대체합니다. 다른 곳에서는 영리함이 혼동된 검토자보다 그것을 절약하는 것보다 더 큰 비용을 지불하므로 else에 대한 주석이 그 줄의 가치가 있습니다.

python
target = "민지"
names = ["준호", "승미", "준영"]

for name in names:
    if name == target:
        print(f"{target}을 찾았습니다")
        break
else:
    print(f"{target}은 리스트에 없습니다")   # break가 실행되지 않아서 실행됨

break가 실행되면 else는 건너뜁니다. 반복문이 시퀀스를 소비하면 else는 실행됩니다. 틈새 패턴이지만 플래그 변수보다 깔끔합니다.

Juno반복문-else 반복문은 반복문이 `break` 없이 끝날 때만 실행되는 `else`를 가질 수 있습니다. 자주 사용되지 않지만 "이 리스트를 검색하고 아무것도 일치하지 않으면 이것을 하세요"를 말하는 가장 깔끔한 방법입니다. 이름이 약간 오해할 수 있으므로 짧은 주석이 도움이 됩니다.
Juno반복문-else 반복문 `else`는 반복문이 `break` 없이 정상적으로 종료될 때 실행됩니다. 발견 플래그를 필요로 하지 않고 "검색 및 발견되지 않으면 보고" 패턴입니다. 그 이름이 사람들을 혼동시키므로 한 가지 용도에만 사용하세요.
Juno반복문-else 반복문이 `break` 없이 끝날 때 실행되며, 이는 단어가 읽히는 것과 반대입니다. 검색-및-미발견 경우에만 유지하세요. `found = False` 플래그를 대체하고 주석을 추가하세요. 다른 곳에서는 그 영리함이 혼동된 검토자보다 더 많은 비용을 지불합니다.

정렬

sorted()는 새로운 정렬된 리스트를 반환하고 원본을 변경하지 않습니다. .sort()는 제자리에서 리스트를 정렬하고 None을 반환합니다. key= 인수를 사용하면 원시 값이 아닌 다른 것으로 정렬할 수 있습니다. 예를 들어 이름을 대소문자 구분 없이 정렬하거나 플레이어 튜플을 점수로 정렬하세요.

sorted()는 안전한 기본값입니다: 원본을 수정하지 않습니다. .sort()는 제자리에서 수정하고 None을 반환합니다. 둘 다 내림차순을 위해 reverse=True를 허용합니다. key= 인수는 비교 전에 각 요소에 적용되는 함수를 받습니다. 이것은 정렬 기준과 데이터를 분리합니다.

Python의 정렬은 안정적이므로 같은 것으로 비교되는 항목은 원래 순서를 유지합니다. 이것은 당신이 단계적으로 정렬할 수 있게 합니다: 먼저 이름으로 정렬한 다음 점수로, 같은 점수의 플레이어는 이름 순서로 유지됩니다. key= 함수는 비교 당 한 번이 아닌 요소당 한 번 실행되므로 비용이 높은 키가 n 배가 아닌 n log n 배로 계산되고 일반적으로 직접 캐시할 필요가 없습니다. .sort()는 목적상 None을 반환합니다: scores = scores.sort()를 작성하는 것을 멈추기 위해 조용히 당신의 리스트를 지웁니다. 그래서 제자리 호출은 자체 줄에 서야 합니다. 기본값으로 sorted()를 사용하고 .sort()를 실제로 원본을 변경하려는 경우에만 사용하세요.

python
scores = [87, 42, 96, 55, 71]

ranked = sorted(scores)           # [42, 55, 71, 87, 96] (새 리스트)
scores.sort()                     # 원본 리스트 정렬, None 반환
scores.sort(reverse=True)         # [96, 87, 71, 55, 42]

둘 다 key= 인수를 허용합니다: 비교 전에 각 항목에 적용되는 함수:

python
names = ["승미", "민지", "준호"]
sorted(names, key=str.lower)       # 대소문자 구분 없이 정렬

players = [("민지", 87), ("준호", 96), ("승미", 55)]
sorted(players, key=lambda p: p[1])   # 점수로 정렬

lambda p: p[1]은 한 줄 함수입니다: 플레이어 튜플을 받아서 점수를 반환합니다. 람다는 람다 및 이해 장에서 자세히 다룹니다.

단순한 경우 sorted()를 사용하세요. 제자리에서 수정하려는 리스트의 경우 .sort()를 사용하세요.

Juno정렬 `sorted()`는 새로운 정렬된 리스트를 반환하고 원본은 혼자 두고, `.sort()`는 리스트 자체를 재배치하고 `None`을 반환합니다. `key=`를 전달하여 원시 값이 아닌 것으로 정렬하세요(예: 이름을 대소문자 구분 없이). 의심할 때 `sorted()`에 도달하세요, 그것이 안전합니다.
Juno정렬 `sorted()`는 원본을 결코 건드리지 않고, `.sort()`는 제자리에서 변경하고 `None`을 반환합니다. 둘 다 `reverse=True`와 비교 전에 각 요소에 적용되는 `key=` 함수를 받습니다. 기본값으로 `sorted()`; 리스트를 변경하는 의도가 있을 때만 `.sort()`를 사용하세요.
Juno정렬 정렬은 안정적이므로 단계적으로 정렬하고 타이는 자신의 순서를 유지합니다. `key=`는 비교가 아닌 요소당 한 번 실행되므로 미리 캐시하지 마세요. 그리고 `.sort()`는 목적상 `None`을 반환합니다: `x = x.sort()`는 당신의 리스트를 지웁니다. 그래서 제자리 호출을 자체 줄에 두세요.

실제로

점수를 반복하고, 합계를 축적하고, 통과 학점을 세고, 요약을 인쇄하세요:

python
raw_scores = [87, 42, 96, 55, 71, 63]

total = 0
passing = 0

for score in raw_scores:
    total += score
    if score >= 60:
        passing += 1

average = total / len(raw_scores)
print(f"평균: {average:.1f}")
print(f"통과: {passing}/{len(raw_scores)}")
print(f"최고 점수: {sorted(raw_scores, reverse=True)[0]}")

정렬된 순서로 파일 리스트를 처리하고, 너무 큰 것을 건너뛰고, 몇 개가 건너뜀되었는지 보고하세요:

python
files = [
    {"name": "report_jan.csv", "size_mb": 12},
    {"name": "report_feb.csv", "size_mb": 850},
    {"name": "report_mar.csv", "size_mb": 7},
]

MAX_SIZE = 100
skipped = 0

for f in sorted(files, key=lambda x: x["name"]):
    if f["size_mb"] > MAX_SIZE:
        print(f"{f['name']} 건너뜀 ({f['size_mb']} MB, 너무 큼)")
        skipped += 1
    else:
        print(f"{f['name']} 처리 중...")

print(f"\n완료. {skipped} 파일 건너뜀.")

요청 로그에서 오류를 스캔한 다음 성공하거나 시도 한계에 도달할 때 종료되는 재시도 반복문을 사용하세요:

python
requests = [
    {"method": "GET", "path": "/users", "status": 200},
    {"method": "POST", "path": "/users", "status": 201},
    {"method": "GET", "path": "/broken", "status": 500},
]

errors = []

for req in requests:
    if req["status"] >= 400:
        errors.append(req)

if errors:
    print(f"요청 로그에 {len(errors)} 오류:")
    for err in errors:
        print(f"  {err['method']} {err['path']} -> {err['status']}")
else:
    print("모든 요청 성공")

attempts = 0
max_retries = 3
success = False

while attempts < max_retries and not success:
    attempts += 1
    print(f"시도 {attempts}...")
    success = attempts >= 2   # 두 번째 시도에서 성공 시뮬레이션

print("연결됨" if success else "모든 재시도 후 실패")