7. 단순 문장

7. 단순 문장 (Simple statements)

단순 문장(simple statement)은 하나의 논리 행 안에 포함돼요. 세미콜론으로 구분된 여러 단순 문장이 한 줄에 나타날 수 있어요. 이 장에서는 표현식, 할당, assert, pass, del, return, yield, raise, break, continue, import, global, nonlocal, type 문장을 다룹니다.

출처: 7. Simple statements

본문

단순 문장은 하나의 논리 행 안에 포함돼요. 세미콜론으로 구분된 여러 단순 문장이 한 줄에 나타날 수 있어요. 단순 문장의 문법은 다음과 같아요:

simple_stmt: expression_stmt | assert_stmt | assignment_stmt | augmented_assignment_stmt | annotated_assignment_stmt | pass_stmt | del_stmt | return_stmt | yield_stmt | raise_stmt | break_stmt | continue_stmt | import_stmt | future_stmt | global_stmt | nonlocal_stmt | type_stmt

7.1. 표현식 문장 (Expression statements)

표현식 문장은 (주로 대화형에서) 값을 계산하고 쓰는 데, 또는 (보통) 프로시저(의미 있는 결과를 반환하지 않는 함수. Python에서 프로시저는 None 값을 반환해요)를 호출하는 데 사용돼요. 표현식 문장의 다른 용도도 허용되며 가끔 유용해요. 표현식 문장의 문법은 다음과 같아요:

expression_stmt: starred_expression

표현식 문장은 표현식 목록(단일 표현식일 수 있어요)을 평가해요.

대화형 모드에서 값이 None이 아니면, 내장 repr() 함수를 사용해 문자열로 변환되고, 결과 문자열이 자체 줄로 표준 출력에 쓰여져요 (결과가 None이면 프로시저 호출이 어떤 출력도 일으키지 않도록 그렇지 않아요.)

7.2. 할당 문장 (Assignment statements)

할당 문장은 이름을 값에 (다시) 바인딩하고 가변 객체의 속성이나 항목을 수정하는 데 사용돼요:

assignment_stmt: (target_list "=")+ (starred_expression | yield_expression) target_list: target ("," target)* [","] target: identifier | "(" [target_list] ")" | "[" [target_list] "]" | attributeref | subscription | "*" target

(attributerefsubscription의 문법 정의는 Primaries 섹션을 참고하세요.)

할당 문장은 표현식 목록을 평가하고(단일 표현식이거나 쉼표로 구분된 목록일 수 있으며, 후자는 튜플을 산출해요), 결과 객체 하나를 각 대상 목록에 왼쪽에서 오른쪽으로 할당해요.

할당은 대상(목록)의 형태에 따라 재귀적으로 정의돼요. 대상이 가변 객체(속성 참조 또는 구독)의 일부일 때, 가변 객체가 궁극적으로 할당을 수행하고 그것의 유효성을 결정해야 하며, 할당이 받아들여지지 않으면 예외를 발생시킬 수 있어요. 다양한 타입이 관찰하는 규칙과 발생하는 예외는 객체 타입의 정의와 함께 제공돼요 (The standard type hierarchy 섹션 참조).

선택적으로 괄호나 대괄호로 둘러싸인 대상 목록으로의 객체 할당은 다음과 같이 재귀적으로 정의돼요.

  • 대상 목록이 뒤따르는 쉼표가 없는 단일 대상이고, 선택적으로 괄호에 있으면, 객체가 그 대상에 할당돼요.
  • 그렇지 않으면:
    • 대상 목록에 별표(asterisk)가 접두사로 붙은 하나의 대상, 즉 "starred" 대상이 포함되어 있으면: 객체는 대상 목록의 대상 수보다 하나 적은 만큼의 항목 이상을 가진 iterable이어야 해요. iterable의 처음 항목들이 왼쪽에서 오른쪽으로 starred 대상 앞의 대상들에 할당돼요. iterable의 마지막 항목들이 starred 대상 뒤의 대상들에 할당돼요. 그런 다음 iterable의 나머지 항목들의 목록이 starred 대상에 할당돼요 (그 목록은 비어 있을 수 있어요).
    • 그렇지 않으면: 객체는 대상 목록의 대상 수와 같은 수의 항목을 가진 iterable이어야 하고, 항목들이 왼쪽에서 오른쪽으로 해당 대상들에 할당돼요.

단일 대상으로의 객체 할당은 다음과 같이 재귀적으로 정의돼요.

  • 대상이 식별자(이름)이면:
    • 그 이름이 현재 코드 블록의 global 또는 nonlocal 문에 나타나지 않으면: 그 이름은 현재 지역 네임스페이스의 객체에 바인딩돼요.
    • 그렇지 않으면: 그 이름은 각각 전역 네임스페이스 또는 nonlocal이 결정하는 바깥 네임스페이스의 객체에 바인딩돼요.
    • 이름이 이미 바인딩되어 있으면 다시 바인딩돼요. 이로 인해 이전에 그 이름에 바인딩된 객체의 참조 횟수가 0이 되어, 객체가 할당 해제되고 (있으면) 소멸자가 호출될 수 있어요.
  • 대상이 속성 참조이면: 참조의 기본(primary) 표현식이 평가돼요. 그것은 할당 가능한 속성을 가진 객체를 산출해야 해요. 그렇지 않으면 TypeError가 발생해요. 그런 다음 그 객체에 주어진 속성에 할당된 객체를 할당하도록 요청돼요. 그것이 할당을 수행할 수 없으면 (보통이지만 반드시 그런 것은 아닌 AttributeError) 예외를 발생시켜요.
    • 참고: 객체가 클래스 인스턴스이고 속성 참조가 할당 연산자의 양쪽에 나타나면, 오른쪽 표현식 a.x는 인스턴스 속성 또는 (인스턴스 속성이 없으면) 클래스 속성에 접근할 수 있어요. 왼쪽 대상 a.x는 항상 인스턴스 속성으로 설정되며, 필요하면 생성해요. 따라서 a.x의 두 발생이 반드시 같은 속성을 가리키는 것은 아니에요: 오른쪽 표현식이 클래스 속성을 가리키면, 왼쪽은 할당의 대상으로서 새 인스턴스 속성을 만들어요:
      class Cls:
          x = 3             # class variable
      inst = Cls()
      inst.x = inst.x + 1   # writes inst.x as 4 leaving Cls.x as 3
      
    • 이 설명은 @property로 만든 프로퍼티 같은 디스크립터 속성에는 반드시 적용되지 않아요.
  • 대상이 구독(subscription)이면: 참조의 기본 표현식이 평가돼요. 다음으로 구독 표현식이 평가돼요. 그런 다음 기본의 __setitem__() 메서드가 두 개의 인자, 즉 구독과 할당된 객체로 호출돼요.
    • 일반적으로 __setitem__()은 가변 시퀀스 객체(목록 같은)와 매핑 객체(사전 같은)에 정의되며, 다음과 같이 동작해요.
    • 기본이 가변 시퀀스 객체(목록 같은)이면, 구독은 정수를 산출해야 해요. 음수이면 시퀀스의 길이가 더해져요. 결과 값은 시퀀스 길이보다 작은 비음수 정수여야 하고, 시퀀스는 그 인덱스의 항목에 할당된 객체를 할당하도록 요청돼요. 인덱스가 범위를 벗어나면 IndexError가 발생해요 (구독된 시퀀스에 대한 할당은 목록에 새 항목을 추가할 수 없어요).
    • 기본이 매핑 객체(사전 같은)이면, 구독은 매핑의 키 타입과 호환되는 타입이어야 하고, 매핑은 구독을 할당된 객체에 매핑하는 키/값 쌍을 만들도록 요청돼요. 이는 같은 키 값을 가진 기존 키/값 쌍을 대체하거나, (같은 값을 가진 키가 없었으면) 새 키/값 쌍을 삽입할 수 있어요.
  • 대상이 슬라이싱이면: 기본 표현식은 가변 시퀀스 객체(목록 같은)로 평가되어야 해요. 할당된 객체는 iterable이어야 해요. 슬라이싱의 하한과 상한은 정수여야 해요. None이면(또는 없으면), 기본값은 0과 시퀀스의 길이에요. 어느 경계라도 음수면 시퀀스의 길이가 더해져요. 결과 경계는 0과 시퀀스 길이 사이(포함)에 있도록 자릴라져요. 마지막으로 시퀀스 객체는 슬라이스를 할당된 시퀀스의 항목들로 대체하도록 요청돼요. 대상 시퀀스가 허용하면 슬라이스의 길이는 할당된 시퀀스의 길이와 달라질 수 있어, 대상 시퀀스의 길이를 바꿀 수 있어요.

할당의 정의가 왼쪽과 오른쪽 사이의 겹침이 '동시적'임을 암시하지만(예: a, b = b, a는 두 변수를 교환해요), 할당 대상의 집합 안의 겹침은 왼쪽에서 오른쪽으로 발생해, 때때로 혼란을 일으켜요. 예를 들어 다음 프로그램은 [0, 2]를 출력해요:

x = [0, 1]
i = 0
i, x[i] = 1, 2         # i is updated, then x[i] is updated
print(x)

참고 자료

PEP 3132 - Extended Iterable Unpacking: *target 기능의 명세.

7.2.1. 증강 할당 문장 (Augmented assignment statements)

증강 할당은 단일 문장에서 이항 연산과 할당 문장을 결합한 것이에요:

augmented_assignment_stmt: augtarget augop (expression_list | yield_expression) augtarget: identifier | attributeref | subscription augop: "+=" | "-=" | "*=" | "@=" | "/=" | "//=" | "%=" | "**=" | ">>=" | "<<=" | "&=" | "^=" | "|="

(마지막 세 기호의 문법 정의는 Primaries 섹션을 참고하세요.)

증강 할당은 대상(일반 할당 문장과 달리 언패킹일 수 없어요)과 표현식 목록을 평가하고, 두 피연산자에 할당 타입에 특정한 이항 연산을 수행한 다음, 결과를 원래 대상에 할당해요. 대상은 한 번만 평가돼요.

x += 1 같은 증강 할당 문장은 x = x + 1로 다시 써서 비슷하지만 정확히 같지는 않은 효과를 얻을 수 있어요. 증강 버전에서 x는 한 번만 평가돼요. 또한 가능하면 실제 연산이 in-place로 수행돼요. 즉 새 객체를 만들어 대상에 할당하는 대신 이전 객체가 수정돼요.

일반 할당과 달리, 증강 할당은 오른쪽을 평가하기 전에 왼쪽을 평가해요. 예를 들어 a[i] += f(x)는 먼저 a[i]를 조회하고, 그 다음 f(x)를 평가하고 덧셈을 수행하며, 마지막으로 결과를 a[i]에 다시 써요.

단일 문장에서 튜플과 여러 대상에 할당하는 것을 제외하면, 증강 할당 문장이 하는 할당은 일반 할당과 같은 방식으로 처리돼요. 마찬가지로 가능한 in-place 동작을 제외하면, 증강 할당이 수행하는 이항 연산은 일반 이항 연산과 같아요.

속성 참조인 대상에 대해서는 일반 할당과 같은 클래스 및 인스턴스 속성에 관한 주의 사항이 적용돼요.

7.2.2. 어노테이션된 할당 문장 (Annotated assignment statements)

어노테이션 할당은 단일 문장에서 변수 또는 속성 어노테이션과 선택적 할당 문장을 결합한 것이에요:

annotated_assignment_stmt: augtarget ":" expression ["=" (starred_expression | yield_expression)]

일반 할당 문장과의 차이는 단일 대상만 허용된다는 점이에요.

할당 대상이 괄호로 둘러싸이지 않은 단일 이름으로 구성되면 "단순(simple)"한 것으로 간주돼요. 단순 할당 대상의 경우, 클래스나 모듈 스코프에 있으면 어노테이션은 지연 평가되는 어노테이션 스코프에 수집돼요. 어노테이션은 클래스나 모듈의 __annotations__ 속성이나 annotationlib 모듈의 기능을 사용해 평가될 수 있어요.

할당 대상이 단순하지 않으면(속성, 구독 노드, 또는 괄호로 둘러싸인 이름), 어노테이션은 절대 평가되지 않아요.

이름이 함수 스코프에서 어노테이션되면, 그 이름은 그 스코프에 대해 지역이에요. 어노테이션은 함수 스코프에서 절대 평가되거나 저장되지 않아요.

오른쪽이 있으면, 어노테이션된 할당은 어노테이션이 없는 것처럼 실제 할당을 수행해요. 표현식 대상에 대해 오른쪽이 없으면, 인터프리터는 마지막 __setitem__() 또는 __setattr__() 호출을 제외하고 대상을 평가해요.

참고 자료

PEP 526 - Syntax for Variable Annotations: 변수의 타입(클래스 변수와 인스턴스 변수 포함)을 주석을 통해 표현하는 대신 어노테이션하기 위한 문법을 추가한 제안.

PEP 484 - Type hints: 정적 분석 도구와 IDE에서 사용할 수 있는 타입 어노테이션의 표준 문법을 제공하기 위해 typing 모듈을 추가한 제안.

버전 3.8에서 변경: 이제 어노테이션된 할당은 오른쪽에 일반 할당과 동일한 표현식을 허용해요. 이전에는 일부 표현식(괄호로 묶지 않은 튜플 표현식 같은)이 문법 오류를 일으켰어요.

버전 3.14에서 변경: 어노테이션은 이제 별도의 어노테이션 스코프에서 지연 평가돼요. 할당 대상이 단순하지 않으면 어노테이션은 절대 평가되지 않아요.

7.3. assert 문장 (The assert statement)

assert 문장은 프로그램에 디버깅 assert를 삽입하는 편리한 방법이에요:

assert_stmt: "assert" expression ["," expression]

단순 형태 assert expression은 다음과 동등해요:

if __debug__:
    if not expression: raise AssertionError

확장 형태 assert expression1, expression2는 다음과 동등해요:

if __debug__:
    if not expression1: raise AssertionError(expression2)

이 동등성은 __debug__AssertionError가 그 이름을 가진 내장 변수를 가리킨다고 가정해요. 현재 구현에서 내장 변수 __debug__는 정상적인 상황에서 True이고, 최적화가 요청되면(명령줄 옵션 -O) False예요. 현재 코드 생성기는 컴파일 시점에 최적화가 요청되면 assert 문장에 대해 코드를 생성하지 않아요. 실패한 표현식의 소스 코드를 오류 메시지에 포함할 필요는 없어요. 스택 추적의 일부로 표시될 거예요.

__debug__에 대한 할당은 불법이에요. 내장 변수의 값은 인터프리터가 시작될 때 결정돼요.

7.4. pass 문장 (The pass statement)

pass_stmt: "pass"

pass는 null 연산이에요 — 실행될 때 아무 일도 일어나지 않아요. 문장이 문법적으로 요구되지만 실행할 코드가 필요 없을 때 플레이스홀더로 유용해요. 예를 들어:

def f(arg): pass    # a function that does nothing (yet)

class C: pass       # a class with no methods (yet)

7.5. del 문장 (The del statement)

del_stmt: "del" target_list

삭제는 할당이 정의되는 방식과 매우 유사하게 재귀적으로 정의돼요. 모든 세부 사항을 써 내려가는 대신, 몇 가지 힌트를 드릴게요.

대상 목록의 삭제는 각 대상을 왼쪽에서 오른쪽으로 재귀적으로 삭제해요.

이름의 삭제는 같은 코드 블록의 global 문에 그 이름이 나타나는지 여부에 따라, 지역 또는 전역 네임스페이스에서 그 이름의 바인딩을 제거해요. 바인딩되지 않은 이름을 삭제하려 하면 NameError 예외가 발생해요.

속성 참조와 구독의 삭제는 관련된 기본 객체에 전달돼요. 슬라이싱의 삭제는 일반적으로 올바른 타입의 빈 슬라이스를 할당하는 것과 동등해요 (하지만 이것조차 슬라이스되는 객체가 결정해요).

버전 3.2에서 변경: 이전에는 중첩 블록에서 자유 변수로 나타나는 이름을 지역 네임스페이스에서 삭제하는 것이 불법이었어요.

7.6. return 문장 (The return statement)

return_stmt: "return" [expression_list]

return은 함수 정의 안에 문법적으로 중첩될 수만 있고, 중첩된 클래스 정의 안에서는 안 돼요.

표현식 목록이 있으면 평가되고, 없으면 None이 대체돼요.

return은 표현식 목록(또는 None)을 반환 값으로 하여 현재 함수 호출을 떠나요.

returnfinally 절이 있는 try 문 밖으로 제어를 넘기면, 함수를 정말로 떠나기 전에 그 finally 절이 실행돼요.

제너레이터 함수에서 return 문장은 제너레이터가 끝났음을 나타내고 StopIteration을 발생시킬 거예요. 반환된 값(있으면)은 StopIteration을 구성하는 인자로 사용되며 StopIteration.value 속성이 돼요.

비동기 제너레이터 함수에서 빈 return 문장은 비동기 제너레이터가 끝났음을 나타내고 StopAsyncIteration을 발생시킬 거예요. 비어 있지 않은 return 문장은 비동기 제너레이터 함수에서 문법 오류예요.

7.7. yield 문장 (The yield statement)

yield_stmt: yield_expression

yield 문장은 의미상 yield 표현식과 동등해요. yield 문장은 동등한 yield 표현식 문장에서 그렇지 않으면 요구될 괄호를 생략하는 데 사용될 수 있어요. 예를 들어 yield 문장

yield <expr>
yield from <expr>

은 yield 표현식 문장

(yield <expr>)
(yield from <expr>)

과 동등해요.

yield 표현식과 문장은 제너레이터 함수를 정의할 때만 사용되고, 제너레이터 함수의 본문에서만 사용돼요. 함수 정의에서 yield를 사용하는 것은 그 정의가 일반 함수 대신 제너레이터 함수를 만들게 하기에 충분해요.

yield 의미론의 전체 세부 사항은 Yield expressions 섹션을 참고하세요.

7.8. raise 문장 (The raise statement)

raise_stmt: "raise" [expression ["from" expression]]

표현식이 없으면, raise는 현재 처리 중인 예외, 즉 *활성 예외(active exception)*를 다시 발생시켜요. 현재 활성 예외가 없으면, 이것이 오류임을 나타내는 RuntimeError 예외가 발생해요.

그렇지 않으면 raise는 첫 번째 표현식을 예외 객체로 평가해요. 그것은 BaseException의 하위 클래스이거나 인스턴스여야 해요. 클래스이면, 필요할 때 그 클래스를 인자 없이 인스턴스화하여 예외 인스턴스가 얻어져요.

예외의 타입은 예외 인스턴스의 클래스이고, 은 인스턴스 자체예요.

추적(traceback) 객체는 예외가 발생할 때 보통 자동으로 생성되고 __traceback__ 속성으로 첨부돼요. with_traceback() 예외 메서드(추적이 인자로 설정된 같은 예외 인스턴스를 반환해요)를 사용해 예외를 만들고 자신의 추적을 한 단계로 설정할 수 있어요. 예를 들어:

raise Exception("foo occurred").with_traceback(tracebackobj)

from 절은 예외 체이닝에 사용돼요: 주어지면 두 번째 표현식은 다른 예외 클래스나 인스턴스여야 해요. 두 번째 표현식이 예외 인스턴스이면, 발생된 예외에 __cause__ 속성(쓰기 가능)으로 첨부돼요. 표현식이 예외 클래스이면 그 클래스가 인스턴스화되고 결과 예외 인스턴스가 발생된 예외에 __cause__ 속성으로 첨부돼요. 발생된 예외가 처리되지 않으면 두 예외가 모두 출력돼요:

>>> try:
...     print(1 / 0)
... except Exception as exc:
...     raise RuntimeError("Something bad happened") from exc
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
    print(1 / 0)
          ~~^~~
ZeroDivisionError: division by zero

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
    raise RuntimeError("Something bad happened") from exc
RuntimeError: Something bad happened

예외가 이미 처리되는 동안 새 예외가 발생하면 비슷한 메커니즘이 암시적으로 작동해요. except 또는 finally 절, 또는 with 문이 사용될 때 예외가 처리될 수 있어요. 그러면 이전 예외가 새 예외의 __context__ 속성으로 첨부돼요:

>>> try:
...     print(1 / 0)
... except:
...     raise RuntimeError("Something bad happened")
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
    print(1 / 0)
          ~~^~~
ZeroDivisionError: division by zero

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
    raise RuntimeError("Something bad happened")
RuntimeError: Something bad happened

from 절에 None을 지정하면 예외 체이닝을 명시적으로 억제할 수 있어요:

>>> try:
...     print(1 / 0)
... except:
...     raise RuntimeError("Something bad happened") from None
...
Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
RuntimeError: Something bad happened

예외에 대한 추가 정보는 Exceptions 섹션, 예외 처리에 대한 정보는 The try statement 섹션에서 찾을 수 있어요.

버전 3.3에서 변경: None이 이제 raise X from Y에서 Y로 허용돼요.

예외 컨텍스트의 자동 표시를 억제하는 __suppress_context__ 속성이 추가되었어요.

버전 3.11에서 변경: 활성 예외의 추적이 except 절에서 수정되면, 이후의 raise 문장은 수정된 추적으로 예외를 다시 발생시켜요. 이전에는 예외가 잡힐 때 가졌던 추적으로 다시 발생되었어요.

7.9. break 문장 (The break statement)

break_stmt: "break"

breakfor 또는 while 루프 안에 문법적으로 중첩될 수만 있고, 그 루프 안의 함수나 클래스 정의에는 중첩될 수 없어요.

그것은 가장 가까운 바깥 루프를 종료하고, 루프에 있으면 선택적 else 절을 건너뛰어요.

for 루프가 break로 종료되면, 루프 제어 대상은 현재 값을 유지해요.

breakfinally 절이 있는 try 문 밖으로 제어를 넘기면, 루프를 정말로 떠나기 전에 그 finally 절이 실행돼요.

7.10. continue 문장 (The continue statement)

continue_stmt: "continue"

continuefor 또는 while 루프 안에 문법적으로 중첩될 수만 있고, 그 루프 안의 함수나 클래스 정의에는 중첩될 수 없어요. 그것은 가장 가까운 바깥 루프의 다음 주기로 계속돼요.

continuefinally 절이 있는 try 문 밖으로 제어를 넘기면, 다음 루프 주기를 정말로 시작하기 전에 그 finally 절이 실행돼요.

7.11. import 문장 (The import statement)

import_stmt: "import" module ["as" identifier] ("," module ["as" identifier])* | "from" relative_module "import" identifier ["as" identifier] ("," identifier ["as" identifier])* | "from" relative_module "import" "(" identifier ["as" identifier] ("," identifier ["as" identifier])* [","] ")" | "from" relative_module "import" "" module: (identifier ".") identifier relative_module: "."* module | "."+

기본 import 문장(from 절이 없는)은 두 단계로 실행돼요:

  • 필요하면 모듈을 찾아 로드하고 초기화해요
  • import 문장이 발생하는 스코프에 대해 현재 네임스페이스에 이름(들)을 할당 문장이 하듯이 정의해요 (globalnonlocal 의미론 포함).

문장이 여러 절(쉼표로 구분된)을 포함하면, 두 단계가 각 절에 대해 마치 그 절들이 개별 import 문장으로 분리된 것처럼 별도로 수행돼요.

첫 번째 단계인 모듈 찾기와 로드의 세부 사항은 import 시스템 섹션에서 더 자세히 설명돼요. 이 섹션은 또한 import될 수 있는 다양한 타입의 패키지와 모듈, 그리고 import 시스템을 사용자 지정하는 데 사용할 수 있는 모든 훅을 설명해요. 이 단계의 실패가 모듈을 찾을 수 없음을 나타내거나, 모듈 코드의 실행을 포함한 모듈 초기화 중에 오류가 발생했음을 나타낼 수 있다는 점에 유의하세요.

요청된 모듈이 성공적으로 검색되면, 세 가지 방법 중 하나로 지역 네임스페이스에서 사용 가능해져요:

  • 모듈 이름 뒤에 as가 오면, as 뒤의 이름이 import된 모듈에 직접 바인딩돼요.
  • 다른 이름이 지정되지 않고 import되는 모듈이 최상위 모듈이면, 모듈의 이름이 import된 모듈에 대한 참조로 지역 네임스페이스에 바인딩돼요.
  • import되는 모듈이 아닌 최상위 모듈이면, 그 모듈을 포함하는 최상위 패키지의 이름이 최상위 패키지에 대한 참조로 지역 네임스페이스에 바인딩돼요. import된 모듈은 직접이 아니라 완전히 정규화된 이름을 사용해 접근해야 해요.

from 형태는 약간 더 복잡한 과정을 사용해요:

  • from 절에 지정된 모듈을 찾아, 필요하면 로드하고 초기화해요;
  • import 절에 지정된 각 식별자에 대해:
    • import된 모듈에 그 이름의 속성이 있는지 확인해요
    • 없으면, 그 이름의 하위 모듈을 import하려 시도한 다음 import된 모듈을 다시 그 속성에 대해 확인해요
    • 속성이 발견되지 않으면 ImportError가 발생해요.
    • 그렇지 않으면, 그 값에 대한 참조가 현재 네임스페이스에 저장되고, as 절에 있으면 그 이름, 그렇지 않으면 속성 이름을 사용해요.

예시:

import foo                 # foo imported and bound locally
import foo.bar.baz         # foo, foo.bar, and foo.bar.baz imported, foo bound locally
import foo.bar.baz as fbb  # foo, foo.bar, and foo.bar.baz imported, foo.bar.baz bound as fbb
from foo.bar import baz    # foo, foo.bar, and foo.bar.baz imported, foo.bar.baz bound as baz
from foo import attr       # foo imported and foo.attr bound as attr

식별자 목록이 별표('*')로 대체되면, 모듈에 정의된 모든 공개 이름이 import 문장이 발생하는 스코프에 대해 지역 네임스페이스에 바인딩돼요.

모듈이 정의하는 공개 이름은 그 모듈의 네임스페이스에서 __all__이라는 변수를 확인함으로써 결정돼요. 정의되어 있으면, 그것은 그 모듈이 정의하거나 import한 이름인 문자열들의 시퀀스여야 해요. 비-ASCII 문자를 포함하는 이름은 정규화 형식 NFKC여야 해요. 자세한 내용은 이름의 비-ASCII 문자를 참고하세요. __all__에 주어진 이름은 모두 공개로 간주되고 존재해야 해요. __all__이 정의되지 않으면, 공개 이름 집합은 모듈의 네임스페이스에서 밑줄 문자('_')로 시작하지 않는 모든 이름을 포함해요. __all__은 전체 공개 API를 포함해야 해요. API의 일부가 아닌 항목(모듈 안에서 import되어 사용된 라이브러리 모듈 같은)을 실수로 내보내는 것을 피하기 위한 것이에요.

import의 와일드카드 형태 — from module import * — 는 모듈 레벨에서만 허용돼요. 클래스나 함수 정의에서 사용하려 하면 SyntaxError가 발생해요.

어떤 모듈을 import할지 지정할 때 모듈의 절대 이름을 지정할 필요는 없어요. 모듈이나 패키지가 다른 패키지 안에 포함되어 있으면, 패키지 이름을 언급하지 않고 같은 최상위 패키지 안에서 상대 import를 할 수 있어요. from 뒤의 지정된 모듈이나 패키지에서 선행 점을 사용하면, 정확한 이름을 지정하지 않고 현재 패키지 계층을 얼마나 위로 올라갈지 지정할 수 있어요. 선행 점 하나는 import하는 모듈이 존재하는 현재 패키지를 의미해요. 두 점은 패키지 레벨 하나 위를 의미해요. 세 점은 두 레벨 위, 식으로요. 그래서 pkg 패키지의 모듈에서 from . import mod를 실행하면 pkg.mod를 import하게 돼요. pkg.subpkg1 안에서 from ..subpkg2 import mod를 실행하면 pkg.subpkg2.mod를 import할 거예요. 상대 import의 명세는 Package Relative Imports 섹션에 포함돼 있어요.

importlib.import_module()은 로드할 모듈을 동적으로 결정하는 애플리케이션을 지원하기 위해 제공돼요.

인자 module, filename, sys.path, sys.meta_path, sys.path_hooks로 감사(auditing) 이벤트 import를 발생시켜요.

7.11.1. Future 문장 (Future statements)

future 문장은 특정 모듈이 그 기능이 표준이 되는 지정된 미래 Python 릴리스에서 사용 가능할 문법이나 의미론을 사용해 컴파일되어야 한다는 컴파일러에 대한 지시자예요.

future 문장은 언어에 호환되지 않는 변경을 도입하는 미래 Python 버전으로의 마이그레이션을 완화하려는 의도예요. 그것은 기능이 표준이 되는 릴리스 전에 모듈별로 새 기능의 사용을 허용해요.

future_stmt: "from" "future" "import" feature ["as" identifier] ("," feature ["as" identifier])* | "from" "future" "import" "(" feature ["as" identifier] ("," feature ["as" identifier])* [","] ")" feature: identifier

future 문장은 모듈의 상단 근처에 나타나야 해요. future 문장 앞에 나타날 수 있는 유일한 줄들은:

  • 모듈 docstring (있으면),
  • 주석,
  • 빈 줄, 그리고
  • 다른 future 문장들.

future 문장을 사용해야 하는 유일한 기능은 annotations예요 (PEP 563 참조).

future 문장이 활성화하는 모든 역사적 기능은 여전히 Python 3에서 인식돼요. 목록에는 absolute_import, division, generators, generator_stop, unicode_literals, print_function, nested_scopes, with_statement가 포함돼요. 그것들은 항상 활성화되어 중복이며, 역방향 호환성을 위해 유지될 뿐이에요.

future 문장은 컴파일 시점에 인식되고 특별히 처리돼요: 핵심 구조의 의미론에 대한 변경은 종종 다른 코드를 생성함으로써 구현돼요. 새 기능이 새 호환되지 않는 문법(새 예약어 같은)을 도입하는 경우도 있는데, 그러면 컴파일러가 모듈을 다르게 파싱해야 할 수 있어요. 그런 결정은 런타임까지 미룰 수 없어요.

주어진 릴리스에 대해 컴파일러는 어떤 기능 이름이 정의되었는지 알고 있고, future 문장에 자신이 모르는 기능이 포함되면 컴파일 시점 오류를 발생시켜요.

직접적인 런타임 의미론은 다른 import 문장과 동일해요: 나중에 설명하는 표준 모듈 __future__가 있고, future 문장이 실행될 때 일반적인 방식으로 import될 거예요.

흥미로운 런타임 의미론은 future 문장이 활성화하는 특정 기능에 달려 있어요.

다음 문장에는 특별한 것이 없다는 점에 유의하세요:

import __future__ [as name]

그것은 future 문장이 아니에요. 특별한 의미론이나 문법 제한이 없는 일반 import 문장이에요.

future 문장을 포함하는 모듈 M에서 발생하는 내장 함수 exec()compile() 호출에 의해 컴파일된 코드는, 기본적으로 future 문장과 연관된 새 문법이나 의미론을 사용할 거예요. 이것은 compile()의 선택적 인자로 제어될 수 있어요 — 자세한 내용은 그 함수의 문서를 참고하세요.

대화형 인터프리터 프롬프트에 입력된 future 문장은 인터프리터 세션의 나머지 부분에 대해 효력을 가질 거예요. 인터프리터가 -i 옵션으로 시작되고, 실행할 스크립트 이름이 전달되며, 그 스크립트가 future 문장을 포함하면, 스크립트가 실행된 후 시작된 대화형 세션에서 효력을 가질 거예요.

참고 자료

PEP 236 - Back to the future: future 메커니즘의 원래 제안.

7.12. global 문장 (The global statement)

global_stmt: "global" identifier ("," identifier)*

global 문장은 나열된 식별자를 전역으로 해석되게 해요. global 없이는 전역 변수에 할당하는 것이 불가능해요. 자유 변수는 전역으로 선언되지 않고 전역을 가리킬 수는 있어요.

global 문장은 현재 전체 스코프(모듈, 함수 본문 또는 클래스 정의)에 적용돼요. 변수가 그 스코프에서 global 선언 이전에 사용되거나 할당되면 SyntaxError가 발생해요.

모듈 레벨에서 모든 변수는 전역이므로 global 문장은 효과가 없어요. 그러나 변수는 여전히 global 선언 이전에 사용되거나 할당되지 않아야 해요. 이 요구 사항은 대화형 프롬프트(REPL)에서는 완화돼요.

프로그래머 참고: global은 파서에 대한 지시자예요. 그것은 global 문장과 동시에 파싱된 코드에만 적용돼요. 특히 내장 exec() 함수에 제공된 문자열이나 코드 객체에 포함된 global 문장은 함수 호출을 포함하는 코드 블록에 영향을 주지 않고, 그러한 문자열에 포함된 코드는 함수 호출을 포함하는 코드의 global 문장의 영향을 받지 않아요. eval()compile() 함수에도 동일하게 적용돼요.

7.13. nonlocal 문장 (The nonlocal statement)

nonlocal_stmt: "nonlocal" identifier ("," identifier)*

함수나 클래스의 정의가 다른 함수의 정의 안에 중첩(둘러싸여)되어 있을 때, 그것의 nonlocal 스코프는 바깥 함수들의 지역 스코프예요. nonlocal 문장은 나열된 식별자가 nonlocal 스코프에서 이전에 바인딩된 이름을 가리키게 해요. 그것은 캡슐화된 코드가 그러한 nonlocal 식별자를 다시 바인딩할 수 있게 해요. 이름이 둘 이상의 nonlocal 스코프에 바인딩되어 있으면 가장 가까운 바인딩이 사용돼요. 이름이 어떤 nonlocal 스코프에도 바인딩되어 있지 않거나, nonlocal 스코프가 없으면 SyntaxError가 발생해요.

nonlocal 문장은 함수나 클래스 본문의 전체 스코프에 적용돼요. 변수가 그 스코프에서 nonlocal 선언 이전에 사용되거나 할당되면 SyntaxError가 발생해요.

참고 자료

PEP 3104 - Access to Names in Outer Scopes: nonlocal 문장의 명세.

프로그래머 참고: nonlocal은 파서에 대한 지시자이고 그것과 함께 파싱된 코드에만 적용돼요. global 문장의 참고를 보세요.

7.14. type 문장 (The type statement)

type_stmt: 'type' identifier [type_params] "=" expression

type 문장은 typing.TypeAliasType의 인스턴스인 타입 별칭을 선언해요.

예를 들어 다음 문장은 타입 별칭을 만들어요:

type Point = tuple[float, float]

이 코드는 대략적으로 다음과 같아요:

annotation-def VALUE_OF_Point():
    return tuple[float, float]
Point = typing.TypeAliasType("Point", VALUE_OF_Point())

annotation-def는 어노테이션 스코프를 나타내며, 함수와 대부분 비슷하게 동작하지만 몇 가지 작은 차이점이 있어요.

타입 별칭의 값은 어노테이션 스코프에서 평가돼요. 타입 별칭이 생성될 때가 아니라, 값이 타입 별칭의 __value__ 속성을 통해 접근될 때만 평가돼요 (Lazy evaluation 참조). 이는 타입 별칭이 아직 정의되지 않은 이름을 가리킬 수 있게 해줘요.

타입 별칭은 이름 뒤에 타입 매개변수 목록을 추가해 제네릭으로 만들 수 있어요. 자세한 내용은 Generic type aliases를 참고하세요.

type은 소프트 키워드예요.

버전 3.12에서 추가.

참고 자료

PEP 695 - Type Parameter Syntax: type 문장과 제네릭 클래스·함수 문법을 도입했어요.

더 알아보기 (Learn more)