2. 어휘 분석

2. 어휘 분석 (Lexical Analysis)

파이썬 프로그램은 파서(parser) 가 읽어요. 그리고 그 파서가 입력으로 받는 건 토큰(token) 의 흐름인데, 이 토큰을 만들어내는 게 바로 어휘 분석기(lexical analyzer) 죠. 토크나이저(tokenizer)라고도 불러요. 이 장에서는 어휘 분석기가 어떻게 토큰을 만들어내는지 살펴볼게요.

어휘 분석기가 하는 일은 크게 두 가지예요. 먼저 프로그램 텍스트의 인코딩을 결정하는데, 기본값은 UTF-8이에요. 그리고 텍스트를 소스 문자(source character) 로 디코딩해요. 이때 텍스트를 디코딩하지 못하면 SyntaxError가 발생해요.

다음으로는 이 소스 문자들을 이용해 토큰의 흐름을 만들어내죠. 만들어지는 토큰의 종류는 대체로 다음에 처리할 소스 문자에 따라 달라지고, 분석기의 다른 특별한 동작도 아직 처리되지 않은 첫 소스 문자에 의존해요. 아래 표는 이런 소스 문자들을 빠르게 요약해 둔 거예요. 더 자세한 내용이 있는 섹션 링크도 같이 걸어 두었어요.

문자 만들어지는 토큰(또는 관련 문서)
공백, 탭, 폼피드 공백 (Whitespace)
CR, LF 새 줄 (New line), 들여쓰기 (Indentation)
백슬래시 (\) 명시적 줄 연결 (Explicit line joining) — 문자열 이스케이프 시퀀스에서도 중요
해시 (#) 주석 (Comment)
따옴표 (', ") 문자열 리터럴 (String literal)
ASCII 글자 (a-z, A-Z), 비-ASCII 문자 이름 (Name), 접두사가 붙은 문자열·바이트 리터럴
밑줄 (_) 이름 (Name) — 숫자 리터럴의 일부로도 쓰여요
숫자 (0-9) 숫자 리터럴 (Numeric literal)
점 (.) 숫자 리터럴, 연산자 (Operator)
물음표 (?), 달러 ($), 백쿼트 (`), 제어 문자 오류 (Error) — 문자열 리터럴·주석 밖에서만
그 외 인쇄 가능 문자 연산자 또는 구분자 (Operator or delimiter)
파일 끝 끝 표시 (End marker)

출처: Python 언어 레퍼런스 — Lexical analysis

본문

2.1. 줄 구조 (Line structure)

파이썬 프로그램은 여러 개의 논리 줄(logical line) 로 나뉘어요.

2.1.1. 논리 줄 (Logical lines)

논리 줄의 끝은 NEWLINE 토큰으로 표현돼요. 문장(statement)은 NEWLINE이 문법상 허용되는 곳(예: 복합문 안의 문장들 사이)을 제외하고는 논리 줄 경계를 넘어갈 수 없어요. 논리 줄은 하나 이상의 물리 줄(physical line) 이 명시적 또는 암시적 줄 연결(line joining) 규칙을 따라 만들어지는 거예요.

2.1.2. 물리 줄 (Physical lines)

물리 줄은 다음 끝-줄 시퀀스 중 하나로 끝나는 문자들의 연속이에요.

  • ASCII LF(라인피드)를 쓰는 유닉스 형식
  • ASCII 시퀀스 CR LF(리턴 다음 라인피드)를 쓰는 윈도우 형식
  • ASCII CR(리턴) 문자를 쓰는 'Classic Mac OS' 형식

플랫폼과 관계없이 이 시퀀스들은 전부 단일 ASCII LF(라인피드) 문자로 치환돼요. 이 치환은 문자열 리터럴 안에서도 일어나요. 각 줄은 아무 시퀀스나 쓸 수 있고, 파일 안에서 일관될 필요도 없어요. 입력의 끝은 마지막 물리 줄의 묵시적 종결자 역할을 하기도 해요.

형식적으로는 이렇게 표현해요.

newline: <ASCII LF> | <ASCII CR> <ASCII LF> | <ASCII CR>

2.1.3. 주석 (Comments)

주석은 문자열 리터럴의 일부가 아닌 해시 문자(#)에서 시작해서 그 물리 줄의 끝에서 끝나요. 암시적 줄 연결 규칙이 적용되지 않는 한, 주석은 논리 줄의 끝을 의미해요. 주석은 문법에서 무시돼요.

2.1.4. 인코딩 선언 (Encoding declarations)

파이썬 스크립트의 첫 번째 또는 두 번째 줄에 있는 주석이 정규식 coding[=:]\s*([-\w.]+) 와 일치하면, 그 주석은 인코딩 선언으로 처리돼요. 이 정규식의 첫 번째 그룹이 소스 코드 파일의 인코딩을 가리켜요. 인코딩 선언은 반드시 그 자체로 한 줄을 차지해야 하고, 두 번째 줄에 쓴다면 첫 번째 줄도 주석만 있는 줄이어야 해요.

권장되는 인코딩 표현 형식은 아래 두 가지예요. 첫 번째는 GNU Emacs도 인식하는 형식이에요.

# -*- coding: <encoding-name> -*-
# vim:fileencoding=<encoding-name>

두 번째는 Bram Moolenaar의 VIM이 인식하는 형식이에요.

인코딩 선언을 찾지 못하면 기본 인코딩은 UTF-8이에요. 파일의 암시적 또는 명시적 인코딩이 UTF-8이라면, 앞에 붙어 있는 UTF-8 바이트 순서 표시(b'\xef\xbb\xbf')는 문법 오류가 아니라 무시돼요. 인코딩을 선언했다면, 그 인코딩 이름은 파이썬이 인식할 수 있어야 해요(Standard Encodings 참고). 이 인코딩은 문자열 리터럴·주석·식별자를 포함한 모든 어휘 분석에 사용돼요.

모든 어휘 분석 — 문자열 리터럴, 주석, 식별자 포함 — 은 소스 인코딩으로 디코딩된 유니코드 텍스트 위에서 동작해요. NUL 제어 문자를 제외한 모든 유니코드 코드 포인트가 파이썬 소스에 나타날 수 있어요.

source_character:  <any Unicode code point, except NUL>

2.1.5. 명시적 줄 연결 (Explicit line joining)

백슬래시 문자(\)를 쓰면 두 개 이상의 물리 줄을 논리 줄로 연결할 수 있어요. 문자열 리터럴이나 주석에 속하지 않는 백슬래시로 물리 줄이 끝나면, 그 백슬래시와 뒤의 끝-줄 문자는 삭제되고 다음 줄과 합쳐져 하나의 논리 줄이 돼요. 예를 들면 이렇게요.

if 1900 < year < 2100 and 1 <= month <= 12 \
   and 1 <= day <= 31 and 0 <= hour < 24 \
   and 0 <= minute < 60 and 0 <= second < 60:   # Looks like a valid date
        return 1

백슬래시로 끝나는 줄은 주석을 담을 수 없어요. 백슬래시는 주석을 이어주지도 않아요. 또 백슬래시는 문자열 리터럴을 제외한 토큰을 이어주지도 않는데, 즉 문자열 리터럴이 아닌 토큰은 백슬래시로 물리 줄을 나눌 수 없어요. 백슬래시는 문자열 리터럴 밖의 다른 곳에서는 불법이에요.

2.1.6. 암시적 줄 연결 (Implicit line joining)

괄호·대괄호·중괄호 안의 표현식은 백슬래시 없이도 여러 물리 줄에 걸쳐 쓸 수 있어요. 예시를 볼게요.

month_names = ['Januari', 'Februari', 'Maart',      # These are the
               'April',   'Mei',      'Juni',       # Dutch names
               'Juli',    'Augustus', 'September',  # for the months
               'Oktober', 'November', 'December']   # of the year

암시적으로 이어지는 줄에는 주석을 달 수 있어요. 이어지는 줄들의 들여쓰기는 중요하지 않고, 빈 이어지는 줄도 허용돼요. 암시적 줄 연결 사이에는 NEWLINE 토큰이 없어요. 암시적으로 이어지는 줄은 삼중 따옴표 문자열 안에서도 나타날 수 있는데, 그 경우에는 주석을 달 수 없어요.

2.1.7. 빈 줄 (Blank lines)

공백·탭·폼피드와 어쩌면 주석 하나만 포함한 논리 줄은 무시돼요 (NEWLINE 토큰이 생성되지 않아요). 인터랙티브 입력에서 문장을 받을 때 빈 줄을 처리하는 방식은 read-eval-print 루프의 구현에 따라 달라질 수 있어요. 표준 인터랙티브 인터프리터에서는 완전히 빈 논리 줄(공백조차, 주석조차 없는 줄)이 여러 줄 문장을 종료해요.

2.1.8. 들여쓰기 (Indentation)

논리 줄 시작 부분의 선행 공백(스페이스와 탭)은 그 줄의 들여쓰기 수준을 계산하는 데 쓰이고, 이 들여쓰기 수준이 문장들의 묶음(grouping)을 결정해요.

탭은 왼쪽에서 오른쪽으로, 치환 지점을 포함한 총 문자가 8의 배수가 되도록 1~8개의 스페이스로 치환돼요(유닉스에서 쓰는 것과 같은 규칙을 의도한 거예요). 그러면 첫 비-공백 문자 앞에 있는 스페이스의 총 개수가 그 줄의 들여쓰기가 돼요. 들여쓰기는 백슬래시로 여러 물리 줄에 걸쳐 나눌 수 없어요 — 첫 백슬래시까지의 공백이 들여쓰기를 결정해요.

소스 파일이 탭과 스페이스를 혼합하는데 그 의미가 스페이스로 환산한 탭의 값에 의존하게 되면, 들여쓰기는 일관되지 않다고 판정돼 TabError가 발생해요.

크로스 플랫폼 호환성 참고: 유닉스가 아닌 플랫폼의 텍스트 편집기 특성 때문에, 한 소스 파일 안에서 들여쓰기에 스페이스와 탭을 섞어 쓰는 건 현명하지 않아요. 게다가 플랫폼에 따라 최대 들여쓰기 수준을 명시적으로 제한할 수도 있어요.

줄 시작에 폼피드 문자가 있을 수 있는데, 위 들여쓰기 계산에서는 무시돼요. 선행 공백의 다른 위치에서 나타나는 폼피드 문자는 효과가 정의되지 않아요(예를 들어 공백 개수를 0으로 리셋할 수도 있어요).

연속된 줄들의 들여쓰기 수준은 스택을 사용해 INDENTDEDENT 토큰을 생성하는 데 쓰여요. 절차는 이래요.

파일의 첫 줄을 읽기 전에 스택에 0 하나가 푸시되고, 이 0은 다시 팝되지 않아요. 스택에 푸시되는 숫자는 아래에서 위로 항상 엄격히 증가해요. 각 논리 줄의 시작에서 그 줄의 들여쓰기 수준을 스택의 최상단과 비교해요. 같으면 아무 일도 일어나지 않아요. 더 크면 스택에 푸시하고 INDENT 토큰 하나를 생성해요. 더 작으면 반드시 스택에 있는 숫자 중 하나여야 하고, 스택에서 그보다 큰 모든 숫자를 팝하면서 숫자마다 DEDENT 토큰을 생성해요. 파일 끝에서는 0보다 큰 숫자가 스택에 남아 있으면 숫자마다 DEDENT 토큰을 하나씩 생성해요.

다음은 (혼란스럽지만) 올바르게 들여쓰기된 파이썬 코드 예시예요.

def perm(l):
        # Compute the list of all permutations of l
    if len(l) <= 1:
                  return [l]
    r = []
    for i in range(len(l)):
             s = l[:i] + l[i+1:]
             p = perm(s)
             for x in p:
              r.append(l[i:i+1] + x)
    return r

다음 예시는 여러 들여쓰기 오류를 보여줘요.

 def perm(l):                       # error: first line indented
for i in range(len(l)):             # error: not indented
    s = l[:i] + l[i+1:]
        p = perm(l[:i] + l[i+1:])   # error: unexpected indent
        for x in p:
                r.append(l[i:i+1] + x)
            return r                # error: inconsistent dedent

(사실 처음 세 오류는 파서가 탐지하고, 마지막 오류만 어휘 분석기가 찾아요 — return r의 들여쓰기가 스택에서 팝된 수준과 일치하지 않기 때문이에요.)

2.1.9. 토큰 사이의 공백 (Whitespace between tokens)

논리 줄 시작이나 문자열 리터럴 안을 제외하면, 스페이스·탭·폼피드 공백 문자를 서로 바꿔 쓰면서 토큰을 구분할 수 있어요.

whitespace:  ' ' | tab | formfeed

공백은 두 토큰이 합쳐졌을 때 다른 토큰으로 해석될 수 있는 경우에만 필요해요. 예를 들어 ab는 토큰 하나지만, a b는 토큰 두 개예요. 반면 +a+ a는 둘 다 +a 두 토큰을 만들어요. +a는 유효한 토큰이 아니기 때문이에요.

2.1.10. 끝 표시 (End marker)

비-인터랙티브 입력의 끝에서 어휘 분석기는 ENDMARKER 토큰을 생성해요.

2.2. 기타 토큰 (Other tokens)

NEWLINE, INDENT, DEDENT 외에도 다음과 같은 토큰 범주가 존재해요: 식별자와 키워드(NAME), 리터럴(예: NUMBERSTRING), 그리고 기타 기호(연산자와 구분자, OP)예요. 공백 문자(앞서 다룬 논리 줄 종결자는 제외)는 토큰이 아니라 토큰을 구분하는 역할만 해요. 모호함이 있으면 토큰은 왼쪽에서 오른쪽으로 읽을 때 가장 긴 유효한 토큰을 구성해요.

2.3. 이름 (Identifiers and keywords)

NAME 토큰은 식별자·키워드·소프트 키워드를 나타내요.

이름은 다음 문자들로 구성돼요.

  • 대문자와 소문자 (A-Za-z)
  • 밑줄 (_)
  • 숫자 (0~9) — 다만 첫 글자가 될 수는 없어요
  • 비-ASCII 문자 — 유효한 이름은 "글자류"와 "숫자류" 문자만 포함할 수 있어요 (Non-ASCII characters in names 참고)

이름은 최소한 한 글자 이상이어야 하지만, 상한 길이 제한은 없어요. 대소문자는 구분돼요.

형식적으로 이름은 다음 어휘 정의로 설명돼요.

NAME:          name_start name_continue*
name_start:    "a"..."z" | "A"..."Z" | "_" | <non-ASCII character>
name_continue: name_start | "0"..."9"
identifier:    <NAME, except keywords>

이 문법에 매칭되는 모든 이름이 유효한 것은 아니라는 점을 기억해 두세요 (Non-ASCII characters in names 참고).

2.3.1. 키워드 (Keywords)

다음 이름들은 언어의 예약어, 즉 키워드 로 쓰이며 보통의 식별자로는 사용할 수 없어요. 반드시 여기에 적힌 그대로 철자해야 해요.

False      await      else       import     pass
None       break      except     in         raise
True       class      finally    is         return
and        continue   for        lambda     try
as         def        from       nonlocal   while
assert     del        global     not        with
async      elif       if         or         yield

2.3.2. 소프트 키워드 (Soft Keywords)

버전 3.10에서 추가됐어요.

어떤 이름들은 특정 맥락에서만 예약되는데, 이걸 소프트 키워드(soft keyword) 라고 해요.

  • match 문에서 쓰이는 match, case, _
  • type 문에서 쓰이는 type

이들은 각자의 특정 맥락에서 문법적으로 키워드처럼 동작하지만, 이 구분은 토큰화 시점이 아니라 파서 수준에서 이루어져요. 소프트 키워드라서 이 이름들을 식별자 이름으로 쓰는 기존 코드와의 호환성을 유지하면서도 문법에서 사용할 수 있어요.

버전 3.12에서 변경: type이 이제 소프트 키워드예요.

2.3.3. 예약된 식별자 클래스 (Reserved classes of identifiers)

키워드 외에도 특별한 의미를 갖는 식별자 클래스들이 있어요. 이 클래스들은 앞뒤 밑줄 문자의 패턴으로 식별돼요.

_*from module import *로 import 되지 않아요.

_match 문 안의 case 패턴에서 _는 와일드카드를 뜻하는 소프트 키워드예요. 별도로, 인터랙티브 인터프리터는 마지막 평가 결과를 변수 _로 사용할 수 있게 해요(print 같은 내장 함수 옆에 builtins 모듈에 저장돼요). 그 외의 곳에서 _는 평범한 식별자예요. "특별한" 항목 이름으로 흔히 쓰이지만, 파이썬 자체가 특별하게 다루는 건 아니에요.

참고: 이름 _는 국제화(i18n)와 함께 자주 쓰여요. 이 관례에 대해선 gettext 모듈 문서를 참고하세요. 또 사용하지 않는 변수를 이름 짓는 데에도 흔히 쓰여요.

__*__ — 시스템 정의 이름으로, 흔히 "던더(dunder)" 이름이라고 불러요. 인터프리터와 그 구현(표준 라이브러리 포함)이 정의하는 이름들이에요. 현재 시스템 이름들은 Special method names 섹션과 다른 곳에서 다뤄져요. 앞으로 파이썬 버전에서 더 늘어날 가능성이 커요. 명시적으로 문서화된 용도를 따르지 않는 __*__ 이름의 사용은 어떤 맥락에서든 경고 없이 깨질 수 있어요.

__* — 클래스 전용(private) 이름이에요. 이 범주의 이름은 클래스 정의 맥락에서 쓰일 때, 기본 클래스와 파생 클래스의 "private" 속성 이름 충돌을 피하기 위해 맹글(mangle)된 형태로 다시 쓰여져요. Identifiers (Names) 섹션을 참고하세요.

2.3.4. 이름의 비-ASCII 문자 (Non-ASCII characters in names)

비-ASCII 문자를 포함하는 이름은 위에서 설명한 규칙과 문법 너머의 추가적인 정규화(normalization)와 검증이 필요해요. 예를 들어 ř_1, , साँप 는 유효한 이름이지만 r〰2, , 🐍 는 유효하지 않아요.

이 섹션에서 정확한 규칙을 설명할게요.

모든 이름은 파싱하는 동안 정규화 형식 NFKC로 변환돼요. 즉, 예를 들어 어떤 문자의 활자적 변형들이 "기본" 형식으로 변환돼요. 예를 들어 fiⁿₐˡᵢᶻₐᵗᵢᵒₙfinalization으로 정규화되므로, 파이썬은 둘을 같은 이름으로 취급해요.

>>> fiⁿₐˡᵢᶻₐᵗᵢᵒₙ = 3
>>> finalization
3

참고: 정규화는 어휘 수준에서만 일어나요. 이름을 문자열로 받는 런타임 함수는 보통 인자를 정규화하지 않아요. 예를 들어 위에서 정의한 변수는 런타임에 globals() 딕셔너리에서 globals()["finalization"]로는 접근할 수 있지만, globals()["fiⁿₐˡᵢᶻₐᵗᵢᵒₙ"]로는 접근할 수 없어요.

ASCII로만 된 이름이 글자·숫자·밑줄만 포함하고 숫자로 시작할 수 없는 것과 마찬가지로, 유효한 이름은 "글자류" 집합 xid_start에 속한 문자로 시작해야 하고, 나머지 문자들은 "글자·숫자류" 집합 xid_continue에 속해야 해요.

이 집합들은 Unicode 표준 부속서 UAX-31이 정의하는 *XID_Start과 *XID_Continue 집합에 기반해요. 파이썬의 xid_start는 추가로 밑줄(_)을 포함해요. 파이썬이 반드시 UAX-31을 완전히 따르는 건 아니라는 점에 유의하세요.

Unicode가 정의한 *XID_Start와 *XID_Continue 집합의 비-규범 목록은 Unicode Character Database의 DerivedCoreProperties.txt 파일에서 볼 수 있어요. 참고로, xid_* 집합의 구성 규칙은 아래와 같아요.

id_start 집합은 다음의 합집합으로 정의돼요.

  • Unicode 범주 <Lu> — 대문자 (A~Z 포함)
  • Unicode 범주 <Ll> — 소문자 (a~z 포함)
  • Unicode 범주 <Lt> — 타이틀케이스 문자
  • Unicode 범주 <Lm> — 수식자 문자
  • Unicode 범주 <Lo> — 기타 문자
  • Unicode 범주 <Nl> — 문자 숫자
  • {"_"} — 밑줄
  • <Other_ID_Start> — 역호환성을 지원하는 PropList.txt의 명시적 문자 집합

xid_start는 이 집합을 NFKC 정규화로 닫아서, 정규화 결과가 id_start id_continue* 형식이 아닌 모든 문자를 제거해요.

id_continue 집합은 다음의 합집합으로 정의돼요.

  • id_start (위 참고)
  • Unicode 범주 <Nd> — 십진 숫자 (0~9 포함)
  • Unicode 범주 <Pc> — 연결 구두점
  • Unicode 범주 <Mn> — 비-간격 결합 부호
  • Unicode 범주 <Mc> — 간격 결합 부호
  • <Other_ID_Continue> — 역호환성을 지원하는 PropList.txt의 또 다른 명시적 문자 집합

xid_continue도 마찬가지로 NFKC 정규화로 집합을 닫아요.

Unicode 범주는 unicodedata 모듈에 포함된 버전의 Unicode Character Database를 사용해요.

2.4. 리터럴 (Literals)

리터럴은 어떤 내장 타입의 상수 값을 나타내는 표기법이에요. 어휘 분석 측면에서 파이썬에는 문자열·바이트·숫자 리터럴이 있어요. 그 외의 "리터럴"은 키워드(None, True, False)와 특별한 생략(ellipsis) 토큰(...)으로 어휘적으로 표현돼요.

2.5. 문자열과 바이트 리터럴 (String and Bytes literals)

문자열 리터럴은 작은따옴표(')나 큰따옴표(")로 감싼 텍스트예요. 예시를 볼게요.

"spam"
'eggs'

리터럴을 시작한 따옴표가 그 리터럴을 끝내기도 하므로, 문자열 리터럴은 다른 쪽 따옴표만 담을 수 있어요(이스케이프 시퀀스는 예외 — 아래 참고). 예를 들면요.

'Say "Hello", please.'
"Don't do that!"

이 제한을 빼면 따옴표 문자('")를 어느 쪽을 쓰든 리터럴이 파싱되는 방식에는 영향이 없어요.

문자열 리터럴 안에서 백슬래시(\) 문자는 이스케이프 시퀀스를 도입해요. 의 의미는 백슬래시 다음 문자에 따라 달라져요. 예를 들어 \" 는 큰따옴표 문자를 뜻하며 문자열을 끝내지 않아요.

>>> print("Say \"Hello\" to everyone!")
Say "Hello" to everyone!

이런 시퀀스의 전체 목록과 더 자세한 내용은 아래 이스케이프 시퀀스를 참고하세요.

2.5.1. 삼중 따옴표 문자열 (Triple-quoted strings)

문자열은 작은따옴표나 큰따옴표 세 개의 짝으로도 감쌀 수 있어요. 이를 보통 삼중 따옴표 문자열(triple-quoted string) 이라고 해요.

"""This is a triple-quoted string."""

삼중 따옴표 리터럴에서는 이스케이프되지 않은 따옴표를 쓸 수 있고(그대로 유지되요), 단 시작할 때 쓴 것과 같은 종류('")의 이스케이프되지 않은 따옴표 세 개가 연달아 오면 그 리터럴을 끝내요.

"""This string has "quotes" inside."""

이스케이프되지 않은 새 줄도 허용되고 유지돼요.

'''This triple-quoted string
continues on the next line.'''

2.5.2. 문자열 접두사 (String prefixes)

문자열 리터럴은 리터럴 내용이 어떻게 파싱되는지에 영향을 주는 선택적 접두사를 가질 수 있어요. 예를 들면요.

b"data"
f'{result=}'

허용되는 접두사는 이래요.

  • b: 바이트 리터럴
  • r: 원시 문자열 (raw string)
  • f: 포맷 문자열 리터럴 ("f-string")
  • t: 템플릿 문자열 리터럴 ("t-string")
  • u: 효과 없음 (역호환성을 위해 허용)

각 타입의 자세한 내용은 연결된 섹션을 참고하세요.

접두사는 대소문자를 구분하지 않아요(예: 'B'는 'b'와 같은 동작). 'r' 접두사는 'f', 't', 'b'와 결합할 수 있어서 fr, rf, tr, rt, br, rb 도 유효한 접두사예요.

버전 3.3에서 추가: 원시 바이트 리터럴의 'rb' 접두사가 'br'의 동의어로 추가됐어요. 유니코드 레거시 리터럴(u'value') 지원은 Python 2.x와 3.x 코드베이스를 함께 유지보수하기 쉽도록 다시 도입된 거예요 (PEP 414 참고).

2.5.3. 형식 문법 (Formal grammar)

"f-string"과 "t-string"을 제외한 문자열 리터럴은 다음 어휘 정의로 설명돼요. 이 정의는 음수 lookahead(!)로 끝 따옴표가 리터럴을 끝낸다는 것을 나타내요.

STRING:          [stringprefix] (stringcontent)
stringprefix:    <("r" | "u" | "b" | "br" | "rb"), case-insensitive>
stringcontent:
   | "'''" ( !"'''" longstringitem)* "'''"
   | '"""' ( !'"""' longstringitem)* '"""'
   | "'" ( !"'" stringitem)* "'"
   | '"' ( !'"' stringitem)* '"'
stringitem:      stringchar | stringescapeseq
stringchar:      <any source_character, except backslash and newline>
longstringitem:  stringitem | newline
stringescapeseq: "\" <any source_character>

모든 어휘 정의에서 그렇듯 여기서도 공백이 중요해요. 특히 접두사(있으면)는 바로 뒤에 시작 따옴표가 와야 해요.

2.5.4. 이스케이프 시퀀스 (Escape sequences)

'r'나 'R' 접두사가 없으면, 문자열·바이트 리터럴의 이스케이프 시퀀스는 Standard C에서 쓰는 것과 비슷한 규칙에 따라 해석돼요. 인식되는 이스케이프 시퀀스는 다음과 같아요.

이스케이프 시퀀스 의미
\<newline> 무시되는 줄 끝
\\ 백슬래시
\' 작은따옴표
\" 큰따옴표
\a ASCII 벨 (BEL)
\b ASCII 백스페이스 (BS)
\f ASCII 폼피드 (FF)
\n ASCII 라인피드 (LF)
\r ASCII 캐리지 리턴 (CR)
\t ASCII 가로 탭 (TAB)
\v ASCII 세로 탭 (VT)
\ooo 8진수 문자
\xhh 16진수 문자
\N{name} 이름이 지정된 유니코드 문자
\uxxxx 16진수 유니코드 문자
\Uxxxxxxxx 16진수 유니코드 문자

2.5.4.1. 무시되는 줄 끝 (Ignored end of line)

줄 끝에 백슬래시를 붙이면 그 새 줄을 무시할 수 있어요.

>>> 'This string will not include \
... backslashes or newline characters.'
'This string will not include backslashes or newline characters.'

같은 결과는 삼중 따옴표 문자열이나, 괄호 + 문자열 리터럴 연결로도 얻을 수 있어요.

2.5.4.2. 이스케이프된 문자 (Escaped characters)

raw가 아닌 파이썬 문자열 리터럴에 백슬래시를 넣으려면 두 배로 써야 해요. \\ 이스케이프 시퀀스가 백슬래시 문자 하나를 뜻해요.

>>> print('C:\\Program Files')
C:\Program Files

비슷하게 \'\" 시퀀스는 각각 작은따옴표·큰따옴표 문자를 뜻해요.

>>> print('\' and \"')
' and "

2.5.4.3. 8진수 문자 (Octal character)

\ooo 시퀀스는 8진수(base 8) 값 ooo를 가진 문자를 뜻해요.

>>> '\120'
'P'

8진수 숫자(0~7)는 최대 세 자리까지 받아들여져요. 바이트 리터럴에서 문자는 해당 값을 가진 바이트를 뜻하고, 문자열 리터럴에서는 해당 값을 가진 유니코드 문자를 뜻해요.

버전 3.11에서 변경: 0o377(255)보다 큰 값을 가진 8진수 이스케이프는 DeprecationWarning을 발생시켜요. 버전 3.12에서 변경: 이제 SyntaxWarning을 발생시키고, 향후 파이썬 버전에서는 SyntaxError를 발생시킬 거예요.

2.5.4.4. 16진수 문자 (Hexadecimal character)

\xhh 시퀀스는 16진수(base 16) 값 hh를 가진 문자를 뜻해요.

>>> '\x50'
'P'

Standard C와 달리 정확히 두 자리의 16진수가 필요해요. 바이트 리터럴에서 문자는 해당 값을 가진 바이트를, 문자열 리터럴에서는 해당 값을 가진 유니코드 문자를 뜻해요.

2.5.4.5. 이름이 지정된 유니코드 문자 (Named Unicode character)

\N{name} 시퀀스는 주어진 name을 가진 유니코드 문자를 뜻해요.

>>> '\N{LATIN CAPITAL LETTER P}'
'P'
>>> '\N{SNAKE}'
'🐍'

이 시퀀스는 바이트 리터럴에는 나타날 수 없어요. 버전 3.3에서 이름 별칭 지원이 추가됐어요.

2.5.4.6. 16진수 유니코드 문자 (Hexadecimal Unicode characters)

\uxxxx\Uxxxxxxxx 시퀀스는 주어진 16진수(base 16) 값을 가진 유니코드 문자를 뜻해요. \u는 정확히 네 자리, \U는 정확히 여덟 자리가 필요해요. 후자는 어떤 유니코드 문자든 인코딩할 수 있어요.

>>> '\u1234'
'ሴ'
>>> '\U0001f40d'
'🐍'

이 시퀀스들은 바이트 리터럴에는 나타날 수 없어요.

2.5.4.7. 인식되지 않는 이스케이프 시퀀스 (Unrecognized escape sequences)

Standard C와 달리, 인식되지 않는 모든 이스케이프 시퀀스는 문자열에 그대로 남아요 — 즉 백슬래시가 결과에 남아요.

>>> print('\q')
\q
>>> list('\q')
['\\', 'q']

바이트 리터럴의 경우, 문자열 리터럴에서만 인식되는 이스케이프 시퀀스(\N..., \u..., \U...)는 인식되지 않는 이스케이프 범주에 들어가요.

버전 3.6에서 변경: 인식되지 않는 이스케이프 시퀀스는 DeprecationWarning을 발생시켜요. 버전 3.12에서 변경: 이제 SyntaxWarning을 발생시키고, 향후 파이썬 버전에서는 SyntaxError를 발생시킬 거예요.

2.5.5. 바이트 리터럴 (Bytes literals)

바이트 리터럴은 항상 'b'나 'B' 접두사가 붙어요. str 타입 대신 bytes 타입의 인스턴스를 만들어내요. ASCII 문자만 담을 수 있고, 값이 128 이상인 바이트는 이스케이프 시퀀스(보통 16진수 문자나 8진수 문자)로 표현해야 해요.

>>> b'\x89PNG\r\n\x1a\n'
b'\x89PNG\r\n\x1a\n'
>>> list(b'\x89PNG\r\n\x1a\n')
[137, 80, 78, 71, 13, 10, 26, 10]

마찬가지로 0바이트도 이스케이프 시퀀스(보통 \0\x00)로 표현해야 해요.

2.5.6. 원시 문자열 리터럴 (Raw string literals)

문자열·바이트 리터럴 모두 선택적으로 'r'나 'R' 문자 접두사를 붙일 수 있어요. 이런 구조를 각각 원시 문자열 리터럴(raw string literal)원시 바이트 리터럴(raw bytes literal) 이라고 하고, 백슬래시를 문자 그대로 취급해요. 그래서 원시 문자열 리터럴에서는 이스케이프 시퀀스가 특별하게 다뤄지지 않아요.

>>> r'\d{4}-\d{2}-\d{2}'
'\\d{4}-\\d{2}-\\d{2}'

원시 리터럴에서도 따옴표는 백슬래시로 이스케이프할 수 있지만, 그 백슬래시는 결과에 남아요. 예를 들어 r"\"" 는 백슬래시와 큰따옴표 두 문자로 이루어진 유효한 문자열 리터럴이에요. 반면 r"\" 는 유효한 문자열 리터럴이 아니에요(원시 문자열도 홀수 개의 백슬래시로 끝날 수 없어요). 구체적으로 말하면, 원시 리터럴은 백슬래시 하나로 끝날 수 없어요. 백슬래시가 뒤의 따옴표 문자를 이스케이프해 버리기 때문이에요. 또 백슬래시 하나 다음에 새 줄이 오는 것은 줄 연결이 아니라, 리터럴의 두 문자로 해석된다는 점도 기억하세요.

2.5.7. f-strings

버전 3.6에서 추가. 버전 3.7에서 변경: awaitasync for를 f-string 안의 표현식에 쓸 수 있어요. 버전 3.8에서 변경: 디버그 지정자(=)가 추가됐어요. 버전 3.12에서 변경: f-string 안에서 표현식에 대한 많은 제한이 제거됐어요. 특히 중첩 문자열·주석·백슬래시가 이제 허용돼요.

포맷 문자열 리터럴, 즉 f-string은 'f'나 'F' 접두사가 붙은 문자열 리터럴이에요. 다른 문자열 리터럴과 달리 f-string은 상수 값을 가지지 않아요. 중괄호 {}로 구분되는 치환 필드(replacement field) 를 담을 수 있고, 치환 필드에는 런타임에 평가되는 표현식이 들어가요. 예시를 볼게요.

>>> who = 'nobody'
>>> nationality = 'Spanish'
>>> f'{who.title()} expects the {nationality} Inquisition!'
'Nobody expects the Spanish Inquisition!'

치환 필드 밖의 두 배 중괄호({{}})는 대응하는 단일 중괄호로 대체돼요.

>>> print(f'{{...}}')
{...}

치환 필드 밖의 다른 문자들은 보통 문자열 리터럴처럼 취급돼요. 즉 이스케이프 시퀀스가 디코딩되고(raw 문자열 표시가 있으면 제외), 삼중 따옴표 f-string에서는 새 줄도 가능해요.

>>> name = 'Galahad'
>>> favorite_color = 'blue'
>>> print(f'{name}:\t{favorite_color}')
Galahad:       blue
>>> print(rf"C:\Users\{name}")
C:\Users\Galahad
>>> print(f'''Three shall be the number of the counting
... and the number of the counting shall be three.''')
Three shall be the number of the counting
and the number of the counting shall be three.

포맷 문자열 리터럴의 표현식은 보통 파이썬 표현식처럼 취급돼요. 각 표현식은 포맷 문자열 리터럴이 나타나는 맥락에서 왼쪽부터 오른쪽 순서로 평가돼요. 빈 표현식은 허용되지 않고, lambda와 할당 표현식 :=은 반드시 명시적 괄호로 둘러싸야 해요.

>>> f'{(half := 1/2)}, {half * 42}'
'0.5, 21.0'

치환 필드 안에서 바깥 f-string의 따옴표 종류를 재사용하는 것도 허용돼요.

>>> a = dict(x=2)
>>> f"abc {a["x"]} def"
'abc 2 def'

치환 필드 안에서 백슬래시도 허용되며 다른 맥락과 같은 방식으로 평가돼요.

>>> a = ["a", "b", "c"]
>>> print(f"List a contains:\n{"\n".join(a)}")
List a contains:
a
b
c

f-string을 중첩하는 것도 가능해요.

>>> name = 'world'
>>> f'Repeated:{f' hello {name}' * 3}'
'Repeated: hello world hello world hello world'

이식 가능한 파이썬 프로그램은 5단계보다 깊게 중첩하지 않는 게 좋아요. CPython 구현 세부사항: CPython은 f-string 중첩에 제한을 두지 않아요.

치환 표현식은 작은따옴표든 삼중 따옴표든 f-string 안에서 새 줄을 포함할 수 있고 주석도 포함할 수 있어요. 치환 필드 안에서 # 뒤에 오는 것은 전부 주석이에요(닫는 중괄호와 따옴표까지도). 즉 주석이 있는 치환 필드는 다른 줄에서 닫아야 해요.

>>> a = 2
>>> f"abc{a  # This comment  }"  continues until the end of the line
...       + 3}"
'abc5'

표현식 뒤에서, 치환 필드는 선택적으로 다음을 담을 수 있어요.

  • 디버그 지정자(debug specifier) — 등호(=), 한쪽 또는 양쪽에 선택적으로 공백을 둘 수 있어요
  • 변환 지정자(conversion specifier)!s, !r, !a 및/또는
  • 포맷 지정자(format specifier) — 콜론(:)으로 시작

이 필드들이 어떻게 평가되는지에 대한 자세한 내용은 Standard Library의 f-string 섹션을 참고하세요. 그 섹션에서 설명하듯, 포맷 지정자는 format() 함수의 두 번째 인자로 전달되어 치환 필드 값을 포맷해요. 예를 들어 Format Specification Mini-Language를 사용해 필드 너비와 패딩 문자를 지정할 수 있어요.

>>> number = 14.3
>>> f'{number:20.7f}'
'          14.3000000'

최상위 포맷 지정자는 중첩 치환 필드를 포함할 수 있어요.

>>> field_size = 20
>>> precision = 7
>>> f'{number:{field_size}.{precision}f}'
'          14.3000000'

이 중첩 필드들은 자기 자신의 변환 필드와 포맷 지정자를 담을 수 있어요.

>>> number = 3
>>> f'{number:{field_size}}'
'                   3'
>>> f'{number:{field_size:05}}'
'00000000000000000003'

다만 이 중첩 필드들은 더 깊이 중첩된 치환 필드를 담을 수는 없어요.

포맷 문자열 리터럴은 표현식을 포함하지 않더라도 docstring으로는 쓸 수 없어요.

>>> def foo():
...     f"Not a docstring"
...
>>> print(foo.__doc__)
None

2.5.8. t-strings

버전 3.14에서 추가.

템플릿 문자열 리터럴, 즉 t-string은 't'나 'T' 접두사가 붙은 문자열 리터럴이에요. 이 문자열들은 포맷 문자열 리터럴과 같은 문법 규칙을 따라요. 평가 규칙의 차이는 Standard Library의 t-string 섹션을 참고하세요.

2.5.9. f-string의 형식 문법 (Formal grammar for f-strings)

f-string은 어휘 분석기와 파서가 나눠 처리해요. 어휘 분석기는 FSTRING_START, FSTRING_MIDDLE, FSTRING_END 토큰을 만들고, 파서는 치환 필드 안의 표현식을 처리해요. 정확한 작업 분담은 CPython 구현 세부사항이에요. 그에 따라 f-string 문법은 어휘 정의와 구문 정의가 섞여 있어요.

다음 상황에서는 공백이 중요해요.

  • FSTRING_START에는 공백이 없어야 해요(접두사와 따옴표 사이).
  • FSTRING_MIDDLE의 공백은 리터럴 문자열 내용의 일부예요.
  • fstring_replacement_field에서 f_debug_specifier가 있으면, 여는 중괄호부터 f_debug_specifier까지의 모든 공백과 f_debug_specifier 바로 뒤의 공백은 표현식의 일부로 유지돼요.

CPython 구현 세부사항: 표현식은 토큰화 단계에서 처리되지 않아요. { 토큰과 = 뒤 토큰의 위치를 사용해 소스 코드에서 가져와요.

FSTRING_MIDDLE 정의는 음수 lookahead(!)로 특수 문자(백슬래시·새 줄·{·})와 시퀀스(f_quote)를 나타내요.

fstring:    FSTRING_START fstring_middle* FSTRING_END

FSTRING_START:      fstringprefix ("'" | '"' | "'''" | '"""')
FSTRING_END:        f_quote
fstringprefix:      <("f" | "fr" | "rf"), case-insensitive>
f_debug_specifier:  '='
f_quote:            <the quote character(s) used in FSTRING_START>

fstring_middle:
   | fstring_replacement_field
   | FSTRING_MIDDLE
FSTRING_MIDDLE:
   | (!"\" !newline !'{' !'}' !f_quote) source_character
   | stringescapeseq
   | "{{"
   | "}}"
   | <newline, in triple-quoted f-strings only>
fstring_replacement_field:
   | '{' f_expression [f_debug_specifier] [fstring_conversion]
         [fstring_full_format_spec] '}'
fstring_conversion:
   | "!" ("s" | "r" | "a")
fstring_full_format_spec:
   | ':' fstring_format_spec*
fstring_format_spec:
   | FSTRING_MIDDLE
   | fstring_replacement_field
f_expression:
   | ','.(conditional_expression | "*" or_expr)+ [","]
   | yield_expression

참고: 위 문법 조각에서 f_quoteFSTRING_MIDDLE 규칙은 맥락에 민감해요 — 가장 가까운 바깥 fstringFSTRING_START 내용에 의존하거든요. 이 템플릿에서 더 전통적인 형식 문법을 구축하는 것은 독자의 연습으로 남겨 둘게요.

t-string의 문법은 f-string과 동일하고, 규칙·토큰 이름·접두사 시작 부분에서 *f 대신 *t를 써요.

tstring:    TSTRING_START tstring_middle* TSTRING_END

<rest of the t-string grammar is omitted; see above>

2.6. 숫자 리터럴 (Numeric literals)

NUMBER 토큰은 숫자 리터럴을 나타내며, 정수·부동소수점 수·허수 세 가지 종류가 있어요.

NUMBER: integer | floatnumber | imagnumber

숫자 리터럴의 숫자 값은 그 문자열을 int, float, complex 클래스 생성자에 각각 전달한 것과 같아요. 그 생성자들의 모든 유효한 입력이 유효한 리터럴인 것은 아니라는 점에 유의하세요. 숫자 리터럴은 부호를 포함하지 않아요. -1 같은 구문은 사실 단항 연산자 '-'와 리터럴 1로 이루어진 표현식이에요.

2.6.1. 정수 리터럴 (Integer literals)

정수 리터럴은 정수를 나타내요. 예를 들면요.

7
3
2147483647

정수 리터럴의 길이에는 사용 가능한 메모리에 저장할 수 있는 것 외에 제한이 없어요.

7922816251426433759354395033679228162514264337593543950336

가독성을 높이기 위해 밑줄로 숫자를 묶을 수 있고, 리터럴의 숫자 값을 결정할 때는 무시돼요. 예를 들어 다음 리터럴들은 서로 같아요.

100_000_000_000
100000000000
1_00_00_00_00_000

밑줄은 숫자 사이에서만 나타날 수 있어요. 예를 들어 _123, 321_, 123__321유효하지 않은 리터럴이에요.

정수는 접두사 0b, 0o, 0x를 써서 각각 2진수(base 2)·8진수(base 8)·16진수(base 16)로 지정할 수 있어요. 16진수 숫자 1015는 문자 AF로 나타내며 대소문자를 구분하지 않아요. 예를 들면요.

0b100110111
0b_1110_0101
0o177
0o377
0xdeadbeef
0xDead_Beef

밑줄은 기수 지정자 뒤에 올 수 있어요. 예를 들어 0x_1f는 유효한 리터럴이지만 0_x1f0x__1f는 아니에요. 0이 아닌 십진수에서 앞의 0은 허용되지 않아요. 예를 들어 0123은 유효한 리터럴이 아니에요. 파이썬이 버전 3.0 이전에 쓰던 C 스타일 8진수 리터럴과의 혼동을 피하기 위해서예요.

형식적으로 정수 리터럴은 다음 어휘 정의로 설명돼요.

integer:      decinteger | bininteger | octinteger | hexinteger | zerointeger
decinteger:   nonzerodigit (["_"] digit)*
bininteger:   "0" ("b" | "B") (["_"] bindigit)+
octinteger:   "0" ("o" | "O") (["_"] octdigit)+
hexinteger:   "0" ("x" | "X") (["_"] hexdigit)+
zerointeger:  "0"+ (["_"] "0")*
nonzerodigit: "1"..."9"
digit:        "0"..."9"
bindigit:     "0" | "1"
octdigit:     "0"..."7"
hexdigit:     digit | "a"..."f" | "A"..."F"

버전 3.6에서 변경: 리터럴에서 그룹화 목적으로 밑줄이 이제 허용돼요.

2.6.2. 부동소수점 리터럴 (Floating-point literals)

3.141.5 같은 부동소수점(float) 리터럴은 실수의 근사값을 나타내요. 각각 십진 숫자들로 이루어진 *정수 부분과 *소수 부분으로 구성되고, 두 부분은 소수점 .으로 구분돼요.

2.71828
4.0

정수 리터럴과 달리 앞의 0이 허용돼요. 예를 들어 077.010은 합법이고 77.01과 같은 수를 나타내요. 정수 리터럴처럼 가독성을 위해 숫자 사이에 밑줄 하나가 올 수 있어요.

96_485.332_123
3.14_15_93

두 부분 중 하나는 비어 있을 수 있지만 둘 다 비면 안 돼요. 예를 들면요.

10.  # (equivalent to 10.0)
.001  # (equivalent to 0.001)

선택적으로 정수와 소수 부분 뒤에 *지수(exponent)가 올 수 있어요: eE 문자, 그다음 선택적 부호 +/-, 그리고 정수·소수 부분과 같은 형식의 숫자예요. eE는 "곱하기 10의 거듭제곱"을 뜻해요.

1.0e3  # (represents 1.0×10³, or 1000.0)
1.166e-5  # (represents 1.166×10⁻⁵, or 0.00001166)
6.02214076e+23  # (represents 6.02214076×10²³, or 602214076000000000000000.)

정수와 지수 부분만 있는 float에서는 소수점을 생략할 수 있어요.

1e3  # (equivalent to 1.e3 and 1.0e3)
0e0  # (equivalent to 0.)

형식적으로 부동소수점 리터럴은 다음 어휘 정의로 설명돼요.

floatnumber:
   | digitpart "." [digitpart] [exponent]
   | "." digitpart [exponent]
   | digitpart exponent
digitpart: digit (["_"] digit)*
exponent:  ("e" | "E") ["+" | "-"] digitpart

버전 3.6에서 변경: 리터럴에서 그룹화 목적으로 밑줄이 이제 허용돼요.

2.6.3. 허수 리터럴 (Imaginary literals)

파이썬에는 복소수 객체가 있지만 복소수 리터럴은 없어요. 대신 허수 리터럴이 실수부가 0인 복소수를 나타내요. 예를 들어 수학에서 복소수 3+4.2i 는 실수 3에 허수 4.2i를 더한 것으로 쓰지만, 파이썬은 비슷한 구문을 쓰되 허수 단위가 *i 대신 j로 적혀요.

3+4.2j

이것은 정수 리터럴 3, 연산자 '+', 허수 리터럴 4.2j로 이루어진 표현식이에요. 이 셋은 별개의 토큰이므로 그 사이에 공백이 허용돼요.

3 + 4.2j

각 토큰 내부에는 공백이 허용되지 않아요. 특히 j 접미사는 앞의 숫자와 분리될 수 없어요. j 앞의 숫자는 부동소수점 리터럴과 같은 문법이에요. 따라서 다음은 모두 유효한 허수 리터럴이에요.

4.2j
3.14j
10.j
.001j
1e100j
3.14e-10j
3.14_15_93j

부동소수점 리터럴과 달리 허수에 정수 부분만 있으면 소수점을 생략할 수 있어요. 숫자는 여전히 정수가 아니라 부동소수점 수로 평가돼요.

10j
0j
1000000000000000000000000j   # equivalent to 1e+24j

j 접미사는 대소문자를 구분하지 않아요. J를 써도 된다는 뜻이에요.

3.14J   # equivalent to 3.14j

형식적으로 허수 리터럴은 다음 어휘 정의로 설명돼요.

imagnumber: (floatnumber | digitpart) ("j" | "J")

2.7. 연산자와 구분자 (Operators and delimiters)

다음 문법은 *연산자와 *구분자 토큰, 즉 일반적인 OP 토큰 타입을 정의해요. 이 토큰들의 목록과 이름은 token 모듈 문서에서도 볼 수 있어요.

OP:
   | assignment_operator
   | bitwise_operator
   | comparison_operator
   | enclosing_delimiter
   | other_delimiter
   | arithmetic_operator
   | "..."
   | other_op

assignment_operator:   "+=" | "-=" | "*=" | "**=" | "/="  | "//=" | "%=" |
                       "&=" | "|=" | "^=" | "<<=" | ">>=" | "@="  | ":="
bitwise_operator:      "&"  | "|"  | "^"  | "~"   | "<<"  | ">>"
comparison_operator:   "<=" | ">=" | "<"  | ">"   | "=="  | "!="
enclosing_delimiter:   "("  | ")"  | "["  | "]"   | "{"   | "}"
other_delimiter:       ","  | ":"  | "!"  | ";"   | "="   | "->"
arithmetic_operator:   "+"  | "-"  | "**" | "*"   | "//"  | "/"   | "%"
other_op:              "."  | "@"

참고: 일반적으로 *연산자는 표현식을 결합하는 데 쓰이고 *구분자는 다른 용도로 쓰여요. 다만 두 범주 사이에 명확하고 형식적인 구분은 없어요. 일부 토큰은 용도에 따라 연산자나 구분자 어느 쪽으로도 쓰일 수 있어요. 예를 들어 *는 곱셈 연산자인 동시에 시퀀스 언패킹에 쓰이는 구분자이고, @는 행렬 곱셈인 동시에 데코레이터를 도입하는 구분자예요. 어떤 토큰은 그 구분이 애매해요. 예를 들어 어떤 사람은 ., (, )를 구분자로 보는 반면, 다른 사람은 getattr() 연산자와 함수 호출 연산자로 봐요.

파이썬의 일부 연산자 — 예를 들어 and, or, not in — 는 "기호"(연산자 토큰)가 아니라 키워드 토큰을 사용해요. 연속된 마침표 세 개(...)는 Ellipsis 리터럴로서 특별한 의미를 가져요.

더 알아보기