파이썬 표현식
파이썬 표현식 (Expressions)
이 문서는 파이썬 언어 레퍼런스의 표현식(Expressions) 장이야. 파이썬에서 식(expression)이 어떻게 생겼고, 각 원소가 어떤 의미를 갖는지 전부 정리해 뒀어. 아톰(atom)과 리터럴, 리스트·딕셔너리·집합 표현, 제너레이터 표현, yield 표현식, 속성 접근·인덱싱·슬라이싱·함수 호출, 산술·비트·비교 연산자, 불리언 연산, 조건식·람다·할당 표현식까지 한 번에 다룰 거야. 특히 연산자 우선순위표와 결합 규칙, 그리고 각 구문의 **문법 생성 규칙(grammar)**과 예제를 그대로 보존해 둘 테니, 식을 해석하거나 직접 쓸 때 바로 참고하면 돼.
출처: Python 언어 레퍼런스
문법 표기법 (Syntax Notes)
이 장과 다음 장들에서는 문법을 설명할 때 **문법 표기법(grammar notation)**을 사용할 거야(어휘 분석(lexical analysis)이 아니라). 어떤 문법 규칙의 (대안 중 하나가) 이런 형태를 띠면:
name
: othername
그 name 형태의 의미는 othername과 같다는 뜻이야.
6.1. 산술 변환 (Arithmetic conversions)
아래 산술 연산자 설명에서 "수치 인자를 **공통 실수 타입(common real type)**으로 변환한다"는 표현이 나오면, 내장 수치 타입의 연산자 구현이 표준 라이브러리 문서의 Numeric Types 섹션에 설명된 대로 동작한다는 뜻이야. 특정 연산자와 비수치 피연산자에는 추가 규칙이 적용되기도 해(예: % 연산자의 왼쪽 피연산자로 문자열이 오는 경우). 확장(extension)은 자기만의 변환 동작을 정의해야 해.
6.2. 아톰 (Atoms)
아톰은 표현식의 가장 기본적인 요소야. 가장 단순한 아톰은 **이름(name)**이나 **리터럴(literal)**이지. 괄호, 대괄호, 중괄호로 감싼 형태도 문법상 아톰으로 분류돼.
아톰의 문법은 공식적으로 이렇게 생겼어:
atom
: | 'True' | 'False' | 'None' | '...'
| identifier
| literal
| enclosure
enclosure
: | parenth_form
| list_display
| dict_display
| set_display
| generator_expression
| yield_atom
6.2.1. 내장 상수 (Built-in constants)
키워드 True, False, None은 내장 상수(built-in constants)를 가리켜. 토큰 ...은 Ellipsis 상수를 가리키지. 이 아톰들을 평가하면 각각에 해당하는 값이 나와.
참고: 몇몇 내장 상수는 전역 변수로 더 존재하지만, 여기 언급된 것들만 키워드야. 특히 이 이름들은 재할당하거나 속성으로 쓸 수 없어:
>>> False = 123
File "<input>", line 1
False = 123
^^^^^
SyntaxError: cannot assign to False
6.2.2. 식별자 (이름, Identifiers)
아톰으로 등장하는 식별자는 **이름(name)**이야. 어휘 정의는 Names 섹션, 이름 짓기와 바인딩에 대한 설명은 Naming and binding 섹션을 참고해. 이름이 객체에 바인딩되어 있으면 아톰을 평가했을 때 그 객체가 나와. 이름이 바인딩되어 있지 않으면 평가를 시도할 때 NameError 예외가 발생해.
6.2.2.1. 비공개 이름 맹글링 (Private name mangling)
클래스 정의 안에 텍스트로 나타나는 식별자가 밑줄 2개 이상으로 시작하면서 밑줄 2개 이상으로 끝나지 않으면, 그 클래스의 **비공개 이름(private name)**으로 간주돼. 비공개 이름은 코드가 생성되기 전에 더 긴 형태로 **변환(transform)**되어. 변환된 이름이 255자보다 길면 구현에 따라 자를 수 있어.
변환은 식별자가 쓰이는 문맥과 무관하게 일어나지만, 맹글링되는 비공개 식별자는 다음뿐이야:
- 할당되거나 읽히는 변수 이름, 또는 접근되는 속성 이름으로 쓰인 모든 이름. 단, 중첩 함수·클래스·타입 별칭의
__name__속성은 맹글링되지 않아. - 가져온 모듈의 이름(예:
import __spam의__spam). 모듈이 패키지의 일부면(이름에 점이 포함되면) 맹글링되지 않아(예:import __foo.bar의__foo). - 가져온 멤버의 이름(예:
from spam import __f의__f).
변환 규칙은 다음과 같아:
- 클래스 이름에서 앞쪽 밑줄을 제거하고 밑줄 하나를 앞에 붙인 것을 식별자 앞에 삽입해. 예를 들어
Foo,_Foo,__Foo라는 클래스 안의__spam은_Foo__spam으로 변환돼. - 클래스 이름이 밑줄로만 이루어져 있으면 변환은 항등 변환(그대로)이야. 예를 들어
_나__라는 클래스 안의__spam은 그대로 남아.
6.2.3. 리터럴 (Literals)
리터럴은 값의 텍스트 표현이야. 파이썬은 숫자, 문자열, 바이트 리터럴을 지원해. 포맷 문자열과 템플릿 문자열은 문자열 리터럴로 취급돼.
- 숫자 리터럴은 단일
NUMBER토큰으로 이루어지며, 정수·부동소수점·허수를 나타내. - 문자열·바이트 리터럴은 여러 토큰으로 이루어질 수 있어.
참고로 -3이나 3+4.2j 같은 음수와 복소수는 문법상 리터럴이 아니라, -나 + 연산자가 관여하는 단항·이항 산술 연산이야. 리터럴을 평가하면 주어진 타입(int, float, complex, str, bytes, Template)과 주어진 값을 가진 객체가 나와(부동소수점·허수 리터럴은 근사값일 수 있어).
리터럴의 공식 문법:
literal
: strings
| NUMBER
6.2.3.1. 리터럴과 객체 정체성 (Literals and object identity)
모든 리터럴은 불변(immutable) 데이터 타입에 대응하므로, 객체의 정체성보다 값이 더 중요해. 같은 값을 가진 리터럴을 여러 번 평가하면(프로그램 텍스트에서 같은 위치든 다른 위치든) 같은 객체가 나올 수도 있고, 같은 값을 가진 다른 객체가 나올 수도 있어.
CPython 구현 세부사항: 예를 들어 CPython에서 같은 값을 가진 작은 정수는 같은 객체로 평가돼:
>>> x = 7
>>> y = 7
>>> x is y
True
하지만 큰 정수는 서로 다른 객체로 평가돼:
>>> x = 123456789
>>> y = 123456789
>>> x is y
False
이 동작은 미래 CPython에서 바뀔 수 있어. 특히 "작은" 정수와 "큰" 정수의 경계는 이미 과거에 바뀐 적이 있어. CPython은 리터럴을 is로 비교하면 SyntaxWarning을 내보내:
>>> x = 7
>>> x is 7
<input>:1: SyntaxWarning: "is" with 'int' literal. Did you mean "=="?
True
자세한 내용은 "When can I rely on identity tests with the is operator?"를 참고해.
템플릿 문자열은 불변이지만 보간(Interpolation) 값으로 가변 객체를 참조할 수 있어. 이 섹션에서 두 t-문자열의 "값이 같다"는 것은 구조와 값들의 정체성이 모두 일치한다는 뜻이야. CPython 구현 세부사항: 현재 템플릿 문자열을 평가할 때마다 서로 다른 객체가 생성돼.
6.2.3.2. 문자열 리터럴 연결 (String literal concatenation)
인접한 여러 문자열·바이트 리터럴(다른 따옴표 방식을 써도 됨)은 허용되며, 그 의미는 이어붙인 것과 같아:
>>> "hello" 'world'
'helloworld'
이 기능은 문법 수준에서 정의되므로 리터럴에만 동작해. 런타임에 문자열 식을 연결하려면 + 연산자를 쓰면 돼:
>>> greeting = "Hello"
>>> space = " "
>>> name = "Blaise"
>>> print(greeting + space + name) # not: print(greeting space name)
Hello Blaise
리터럴 연결은 raw 문자열, 삼중 따옴표 문자열, 포맷 문자열 리터럴을 자유롭게 섞을 수 있어:
>>> "Hello" r', ' f"{name}!"
'Hello, Blaise!'
이 기능은 백슬래시 수를 줄이거나, 긴 문자열을 여러 줄로 편하게 나누거나, 문자열 일부에 주석을 달 때 유용해. 예를 들어:
re.compile(
"[A-Za-z_]" # letter or underscore
"[A-Za-z0-9_]*" # letter, digit or underscore
)
다만 바이트 리터럴은 다른 바이트 리터럴과만 결합할 수 있고, 어떤 문자열 리터럴과도 결합할 수 없어. 마찬가지로 템플릿 문자열 리터럴은 다른 템플릿 문자열 리터럴과만 결합할 수 있어:
>>> t"Hello" t"{name}!"
Template(strings=('Hello', '!'), interpolations=(...))
공식 문법:
strings
: (STRING | fstring)+ | tstring+
6.2.4. 괄호로 묶인 형태 (Parenthesized forms)
괄호로 묶인 형태는 소괄호로 감싼 **선택적인 표현식 목록(expression list)**이야:
parenth_form
: "(" [ starred_expression ] ")"
괄호로 묶인 표현식 목록은 그 표현식 목록이 산출하는 값을 산출해: 목록에 쉼표가 하나 이상 있으면 튜플을, 그렇지 않으면 목록을 이루는 단일 표현식의 값을 산출해. 빈 괄호 쌍 ()은 빈 튜플 객체를 산출해. 튜플은 불변이므로 리터럴과 같은 규칙이 적용돼(빈 튜플이 두 번 등장하면 같은 객체일 수도, 아닐 수도 있어).
주의할 점: 튜플은 괄호가 아니라 쉼표로 만들어져. 예외는 빈 튜플인데, 이것은 괄호가 필요해 — 괄호 없는 "아무것도 없음"을 표현식에서 허용하면 모호해지고 흔한 오타가 걸러지지 않으니까.
6.2.5. 리스트·집합·딕셔너리 표시 (Displays)
파이썬은 리스트, 집합, 딕셔너리를 만들기 위해 "표시(display)"라는 특수 문법을 제공해. 각각 두 가지 방식이 있어:
- 컨테이너 내용을 명시적으로 나열하는 방식, 또는
- **이해식(comprehension)**이라고 불리는 루프·필터 지시문 집합으로 계산하는 방식.
이해식의 공통 문법 요소는:
comprehension
: assignment_expression comp_for
comp_for
: [ "async" ] "for" target_list "in" or_test [ comp_iter ]
comp_iter
: comp_for | comp_if
comp_if
: "if" or_test [ comp_iter ]
이해식은 단일 표현식 뒤에 하나 이상의 for 절과, 0개 이상의 for 또는 if 절로 구성돼. 이 경우 새 컨테이너의 요소는 각 for·if 절을 블록으로 간주해 왼쪽에서 오른쪽으로 중첩하고, 가장 안쪽 블록에 도달할 때마다 표현식을 평가해 요소를 하나 만드는 방식으로 생성돼.
단, **가장 왼쪽 for 절의 순회식(iterable expression)**을 제외하면, 이해식은 별도의 **암묵적으로 중첩된 스코프(scope)**에서 실행돼. 이로써 target 목록에 할당된 이름이 바깥 스코프로 "새지 않도록" 보장하지. 가장 왼쪽 for 절의 순회식은 바깥 스코프에서 직접 평가돼 암묵적 중첩 스코프에 인자로 전달돼. 이후의 for 절과 가장 왼쪽 for 절 안의 필터 조건은 가장 왼쪽 순회식에서 얻은 값에 의존할 수 있으므로 바깥 스코프에서 평가할 수 없어. 예: [x*y for x in range(10) for y in range(x, x+10)].
이해식이 항상 적절한 타입의 컨테이너가 되도록, yield와 yield from 표현식은 암묵적 중첩 스코프에서 금지돼.
파이썬 3.6부터 async def 함수 안에서 async for 절로 비동기 반복자를 순회할 수 있어. async def 함수의 이해식은 맨 앞 표현식 뒤에 for 또는 async for 절을 가질 수 있고, 추가 for/async for 절을 가질 수도 있으며 await 표현식도 사용할 수 있어.
이해식이 async for 절을 포함하거나, 가장 왼쪽 for 절의 순회식 말고 다른 곳에 await 표현식이나 다른 비동기 이해식을 포함하면 **비동기 이해식(asynchronous comprehension)**이라고 불러. 비동기 이해식은 자신이 나타난 코루틴 함수의 실행을 일시 중단할 수 있어. (PEP 530 참고)
- 버전 3.6에서 추가: 비동기 이해식 도입.
- 버전 3.8에서 변경: 암묵적 중첩 스코프에서
yield와yield from금지. - 버전 3.11에서 변경: 비동기 함수의 이해식 내부에 비동기 이해식이 허용됨. 바깥 이해식은 자동으로 비동기가 됨.
6.2.6. 리스트 표시 (List displays)
리스트 표시는 대괄호로 감싼, 비어 있을 수도 있는 일련의 표현식이야:
list_display
: "[" [ flexible_expression_list | comprehension ] "]"
리스트 표시는 새 리스트 객체를 산출해. 쉼표로 구분된 표현식 목록이 주어지면 요소를 왼쪽에서 오른쪽으로 평가해 그 순서대로 리스트에 넣어. 이해식이 주어지면 이해식 결과의 요소들로 리스트를 만들어.
6.2.7. 집합 표시 (Set displays)
집합 표시는 중괄호로 나타내며, 키와 값을 구분하는 콜론이 없는 점이 딕셔너리 표시와 구분돼:
set_display
: "{" ( flexible_expression_list | comprehension ) "}"
집합 표시는 새 가변 집합 객체를 산출해. 표현식 목록이 주어지면 요소를 왼쪽에서 오른쪽으로 평가해 집합에 추가해. 이해식이면 그 결과 요소로 집합을 만들어.
주의: 빈 집합은 {}로 만들 수 없어 — {} 리터럴은 빈 딕셔너리를 만들어.
6.2.8. 딕셔너리 표시 (Dictionary displays)
딕셔너리 표시는 중괄호로 감싼, 비어 있을 수도 있는 일련의 dict 항목(키/값 쌍)이야:
dict_display
: "{" [ dict_item_list | dict_comprehension ] "}"
dict_item_list
: dict_item ( "," dict_item )* [ "," ]
dict_item
: expression ":" expression
| "**" or_expr
dict_comprehension
: expression ":" expression comp_for
딕셔너리 표시는 새 딕셔너리 객체를 산출해. 쉼표로 구분된 dict 항목 시퀀스가 주어지면 왼쪽에서 오른쪽으로 평가해 딕셔너리 항목을 정의해: 각 키 객체를 키로 써서 해당 값을 저장하지. 즉 dict 항목 목록에서 같은 키를 여러 번 지정할 수 있고, 그 키의 최종 값은 마지막에 준 값이 돼.
이중 별표 **는 딕셔너리 언패킹을 나타내. 그 피연산자는 매핑(mapping)이어야 하고, 각 매핑 항목이 새 딕셔너리에 추가돼. 나중에 나온 값이 이전 dict 항목과 이전 딕셔너리 언패킹으로 설정된 값을 덮어써.
- 버전 3.5에서 추가: 딕셔너리 표시로의 언패킹(PEP 448).
리스트·집합 이해식과 달리 dict 이해식은 두 표현식을 콜론으로 구분하고 그 뒤에 평소의 for·if 절이 따라와. 이해식이 실행되면 만들어지는 키·값 요소가 생성된 순서대로 새 딕셔너리에 삽입돼. 키 타입의 제약은 이미 표준 타입 계층 섹션에 나열돼 있어(요약하면 키 타입은 해시 가능(hashable)해야 하며, 이는 모든 가변 객체를 제외해). 중복 키 충돌은 감지되지 않고, 주어진 키에 대해 저장되는 (표시에서 텍스트상 가장 오른쪽의) 마지막 값이 우선해.
- 버전 3.8에서 변경: 3.8 이전에는 dict 이해식에서 키와 값의 평가 순서가 잘 정의되지 않았어. CPython에서 값이 키보다 먼저 평가됐지. 3.8부터는 PEP 572 제안대로 키가 값보다 먼저 평가돼.
6.2.9. 제너레이터 표현식 (Generator expressions)
제너레이터 표현식의 문법은 리스트 이해식과 같지만, 대괄호 대신 소괄호로 감싼다는 점만 달라. 예를 들어:
>>> iterator = (x**2 for x in range(10))
>>> iterator
<generator object <genexpr> at ...>
런타임에 제너레이터 표현식은 해당 리스트 이해식과 같은 값을 산출하는 제너레이터 반복자(generator iterator) 로 평가돼:
>>> list(iterator)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
즉 위 예는 다음 제너레이터 함수를 정의하고 호출하는 것과 대략 동등해:
def make_generator_of_squares(iterator):
for x in iterator:
yield x**2
make_generator_of_squares(iter(range(10)))
호출에서 제너레이터 표현식이 유일한 위치 인자이고 키워드 인자가 없으면, 둘러싼 괄호를 생략할 수 있어:
# The parentheses after `sum` are part of the call syntax:
>>> sum(x**2 for x in range(10))
285
# The generator needs its own parentheses if it's not the only argument:
>>> sum((x**2 for x in range(10)), start=1000)
1285
가장 왼쪽 for 절의 순회식은 즉시 평가돼, 이 식에서 발생한 오류는 첫 값을 꺼낼 때가 아니라 제너레이터 표현식을 정의하는 지점에서 발생해:
>>> (x**2 for x in nonexistent_iterable)
Traceback (most recent call last):
...
NameError: name 'nonexistent_iterable' is not defined
표현식이 평가된 뒤 그 결과로부터 iter()를 호출한 것처럼 반복자가 만들어져. 반복자 생성 중 발생한 오류도 즉시 발생해:
>>> (x**2 for x in None)
Traceback (most recent call last):
...
TypeError: 'NoneType' object is not iterable
그 밖의 모든 표현식은 일반 제너레이터처럼 지연(lazily) 평가돼(즉 반복자가 값을 산출하라고 요청받을 때):
>>> iterator = (nonexistent_value for x in range(10))
>>> iterator
<generator object <genexpr> at ...>
>>> list(iterator)
Traceback (most recent call last):
...
NameError: name 'nonexistent_value' is not defined
제너레이터 표현식 자체의 기대 동작을 방해하지 않도록 yield와 yield from 표현식은 암묵적 중첩 스코프 안에서 금지돼. 제너레이터 표현식에 async for 절이나 await 표현식이 포함되면 비동기 제너레이터 표현식이라고 불러. 비동기 제너레이터 표현식은 비동기 반복자인 새 비동기 제너레이터 객체를 반환해(비동기 반복자 참고).
제너레이터 표현식의 공식 문법:
generator_expression
: "(" expression comp_for ")"
- 버전 3.6에서 추가: 비동기 제너레이터 표현식 도입.
- 버전 3.7에서 변경: 3.7 이전에는 비동기 제너레이터 표현식이
async def코루틴에서만 나타날 수 있었어. 3.7부터는 아무 함수에서나 쓸 수 있어. - 버전 3.8에서 변경: 암묵적 중첩 스코프에서
yield와yield from금지.
6.2.10. Yield 표현식 (Yield expressions)
yield_atom
: "(" yield_expression ")"
yield_from
: "yield" "from" expression
yield_expression
: "yield" yield_list
| yield_from
yield 표현식은 제너레이터 함수나 비동기 제너레이터 함수를 정의할 때 쓰이며, 그래서 오직 함수 정의의 본문에서만 쓸 수 있어. 함수 본문에 yield 표현식을 쓰면 그 함수는 제너레이터 함수가 되고, async def 함수의 본문에 쓰면 그 코루틴 함수는 비동기 제너레이터 함수가 돼. 예를 들어:
def gen(): # defines a generator function
yield 123
async def agen(): # defines an asynchronous generator function
yield 123
yield 표현식은 포함하는 스코프에 부수 효과를 주므로, 이해식과 제너레이터 표현식을 구현하는 데 쓰이는 암묵적 스코프의 일부로는 허용되지 않아.
- 버전 3.8에서 변경: 이해식·제너레이터 표현식에 쓰이는 암묵적 중첩 스코프에서 yield 표현식 금지.
제너레이터 함수가 호출되면 제너레이터라고 알려진 반복자를 반환해. 그 제너레이터가 제너레이터 함수의 실행을 제어하지. 실행은 제너레이터의 메서드 중 하나가 호출될 때 시작돼. 그 시점에 실행은 첫 yield 표현식까지 진행하고, 다시 일시 중단돼 yield_list의 값(또는 yield_list가 생략되면 None)을 제너레이터 호출자에게 반환해. "일시 중단되었다"는 것은 지역 변수의 현재 바인딩, 명령 포인터, 내부 평가 스택, 예외 처리 상태 등 모든 지역 상태가 유지된다는 뜻이야. 제너레이터의 메서드 중 하나를 호출해 실행이 재개되면, 함수는 yield 표현식이 그냥 또 하나의 외부 호출인 것처럼 진행할 수 있어. 재개 후 yield 표현식의 값은 실행을 재개한 메서드에 따라 달라져. __next__()를 사용하면(보통 for나 내장 next()를 통해) 결과는 None이고, send()를 사용하면 그 메서드에 전달된 값이 결과가 돼.
이 모든 것이 제너레이터 함수를 코루틴과 아주 비슷하게 만들어: 여러 번 yield 하고, 진입점이 둘 이상이며, 실행이 일시 중단될 수 있어. 유일한 차이는 제너레이터 함수는 yield 후 실행이 어디서 이어질지 제어할 수 없다는 것 — 제어는 항상 제너레이터의 호출자에게 넘어가.
yield 표현식은 try 구조 안 어디서나 허용돼. 제너레이터가 finalize되기(참조 카운트 0에 도달하거나 가비지 컬렉션되기) 전에 재개되지 않으면, 제너레이터-반복자의 close() 메서드가 호출되어 대기 중인 finally 절이 실행될 수 있게 해.
yield from <expr>을 쓰면 주어진 표현식은 **반복 가능(iterable)**해야 해. 그 반복자를 순회하며 만들어지는 값들은 현재 제너레이터 메서드의 호출자에게 직접 전달돼. send()로 전달된 값과 throw()로 전달된 예외는, 기저 반복자가 적절한 메서드를 갖고 있다면 그에게 전달돼. 그렇지 않으면 send()는 AttributeError나 TypeError를, throw()는 전달된 예외를 즉시 발생시켜. 기저 반복자가 완료되면 발생한 StopIteration 인스턴스의 value 속성이 yield 표현식의 값이 돼. 이 값은 StopIteration을 발생시킬 때 명시적으로 설정할 수도 있고, 하위 반복자가 제너레이터일 때(하위 제너레이터가 값을 반환함으로써) 자동으로 설정될 수도 있어.
- 버전 3.3에서 변경: 제어 흐름을 하위 반복자에게 위임하는
yield from <expr>추가.
할당 문의 오른쪽에 yield 표현식이 유일한 표현식이면 괄호를 생략할 수 있어.
참고(관련 PEP):
- PEP 255 – Simple Generators: 제너레이터와
yield문을 파이썬에 추가하는 제안. - PEP 342 – Coroutines via Enhanced Generators: 제너레이터를 단순 코루틴으로 쓸 수 있게 API와 문법을 향상시키는 제안.
- PEP 380 – Syntax for Delegating to a Subgenerator: 하위 제너레이터 위임을 쉽게 만드는
yield_from문법 도입 제안. - PEP 525 – Asynchronous Generators: PEP 492를 확장해 코루틴 함수에 제너레이터 기능을 추가한 제안.
6.2.10.1. 제너레이터-반복자 메서드 (Generator-iterator methods)
이 절은 제너레이터 함수의 실행을 제어하는 데 쓰이는 제너레이터 반복자의 메서드를 설명해. 제너레이터가 이미 실행 중일 때 아래 제너레이터 메서드 중 아무거나 호출하면 ValueError 예외가 발생해.
generator.__next__(): 제너레이터 함수의 실행을 시작하거나 마지막으로 실행된yield표현식에서 재개해.__next__()로 재개되면 현재yield표현식은 항상None으로 평가돼. 그러면 실행은 다음yield표현식으로 이어져 제너레이터가 다시 일시 중단되고,yield_list의 값이__next__()호출자에게 반환돼. 제너레이터가 값을 더 산출하지 않고 종료하면StopIteration예외가 발생해. 이 메서드는 보통 암묵적으로 호출돼(예:for루프나 내장next()함수).generator.send(value): 실행을 재개하고 제너레이터 함수에 값을 "보내".value인자가 현재yield표현식의 결과가 돼.send()는 제너레이터가 산출하는 다음 값을 반환하거나, 값을 더 산출하지 않고 종료하면StopIteration을 발생시켜. 제너레이터를 시작할 때send()를 호출하려면 인자로None을 줘야 해 — 값을 받을yield표현식이 아직 없으니까.generator.throw(value)/generator.throw(type[, value[, traceback]]): 제너레이터가 일시 중단된 지점에서 예외를 발생시키고, 제너레이터 함수가 산출하는 다음 값을 반환해. 제너레이터가 값을 더 산출하지 않고 종료하면StopIteration을 발생시켜. 제너레이터 함수가 전달된 예외를 잡지 않거나 다른 예외를 발생시키면 그 예외가 호출자에게 전파돼. 전형적으로는raise키워드처럼 단일 예외 인스턴스로 호출해. 하위 호환성을 위해 두 번째 시그니처도 지원돼(이전 파이썬 관례).type인자는 예외 클래스여야 하고,value는 예외 인스턴스여야 해.value를 주지 않으면type생성자를 호출해 인스턴스를 얻어.traceback을 주면 예외에 설정되고, 그렇지 않으면value에 저장된 기존__traceback__속성이 지워질 수 있어. — 버전 3.12에서 변경: 두 번째 시그니처(type[, value[, traceback]])는 deprecated 처리됐고 미래 파이썬 버전에서 제거될 수 있어.generator.close(): 제너레이터 함수가 일시 중단된 지점에서GeneratorExit예외를 발생시켜(throw(GeneratorExit)호출과 동등). 예외가 발생하는 곳은 제너레이터가 일시 중단된yield표현식이야. 제너레이터 함수가 예외를 잡고 값을 반환하면 그 값이close()에서 반환돼. 제너레이터가 이미 닫혀 있거나GeneratorExit를 발생시키면(예외를 잡지 않아)close()는None을 반환해. 제너레이터가 값을 산출하면RuntimeError가 발생해. 제너레이터가 다른 예외를 발생시키면 호출자에게 전파돼. 제너레이터가 예외나 정상 종료로 이미 종료됐다면close()는None을 반환하고 다른 효과가 없어. — 버전 3.13에서 변경: 닫을 때 제너레이터가 값을 반환하면 그 값이close()에서 반환돼.
6.2.10.2. 예제 (Examples)
제너레이터와 제너레이터 함수의 동작을 보여주는 간단한 예:
>>> def echo(value=None):
... print("Execution starts when 'next()' is called for the first time.")
... try:
... while True:
... try:
... value = (yield value)
... except Exception as e:
... value = e
... finally:
... print("Don't forget to clean up when 'close()' is called.")
...
>>> generator = echo(1)
>>> print(next(generator))
Execution starts when 'next()' is called for the first time.
1
>>> print(next(generator))
None
>>> print(generator.send(2))
2
>>> generator.throw(TypeError, "spam")
TypeError('spam',)
>>> generator.close()
Don't forget to clean up when 'close()' is called.
yield from을 쓰는 예는 PEP 380: Syntax for Delegating to a Subgenerator를 참고해.
6.2.10.3. 비동기 제너레이터 함수 (Asynchronous generator functions)
async def로 정의된 함수·메서드에 yield 표현식이 있으면 그 함수는 비동기 제너레이터 함수로 더 정의돼. 비동기 제너레이터 함수가 호출되면 비동기 제너레이터 객체라고 알려진 비동기 반복자를 반환해. 그 객체가 제너레이터 함수의 실행을 제어하지. 비동기 제너레이터 객체는 제너레이터 객체가 for 문에 쓰이는 것과 유사하게, 코루틴 함수의 async for 문에서 전형적으로 쓰여.
비동기 제너레이터의 메서드 중 하나를 호출하면 awaitable 객체가 반환되고, 그 객체를 await 하면 실행이 시작돼. 그 시점에 실행은 첫 yield 표현식까지 진행해 다시 일시 중단되며 yield_list의 값을 await 하는 코루틴에게 반환해. 재개 후 yield 표현식의 값은 실행을 재개한 메서드에 따라 달라져. __anext__()를 사용하면 결과는 None이고, asend()를 사용하면 그 메서드에 전달된 값이 결과야.
비동기 제너레이터가 break로 일찍 종료되거나, 호출자 태스크가 취소되거나, 다른 예외로 종료되면, 제너레이터의 비동기 정리 코드가 실행되며 예상치 못한 문맥에서 예외를 발생시키거나 컨텍스트 변수에 접근할 수 있어(의존하는 태스크의 수명 이후 또는 이벤트 루프 종료 시점일 수도 있어). 이를 막으려면 호출자가 aclose() 메서드를 호출해 비동기 제너레이터를 명시적으로 닫아 finalize하고 결국 이벤트 루프에서 분리해야 해.
비동기 제너레이터 함수에서 yield 표현식은 try 구조 안 어디서나 허용돼. 다만 finalize되기 전에 재개되지 않으면, try 구조 안의 yield 표현식이 대기 중인 finally 절의 실행을 실패하게 만들 수 있어. 이 경우 비동기 제너레이터를 실행하는 이벤트 루프나 스케줄러가 비동기 제너레이터-반복자의 aclose() 메서드를 호출하고 결과 코루틴 객체를 실행해 대기 중인 finally 절이 실행되게 할 책임이 있어.
이벤트 루프 종료 시 finalize를 처리하려면, 이벤트 루프는 비동기 제너레이터-반복자를 받아 (아마도) aclose()를 호출하고 코루틴을 실행하는 finalizer 함수를 정의해야 해. 이 finalizer는 sys.set_asyncgen_hooks()를 호출해 등록할 수 있어. 처음 순회하면 비동기 제너레이터-반복자는 등록된 finalizer를 저장해 finalize 시 호출해. (예제는 asyncio.Loop.shutdown_asyncgens 구현을 참고해.)
비동기 제너레이터 함수에서 yield from <expr> 표현식을 쓰면 문법 오류야.
6.2.10.4. 비동기 제너레이터-반복자 메서드 (Asynchronous generator-iterator methods)
async agen.__anext__(): 실행하면 비동기 제너레이터의 실행을 시작하거나 마지막yield표현식에서 재개하는 awaitable을 반환해.__anext__()로 재개되면 현재yield표현식은 반환된 awaitable에서 항상None으로 평가되고, 실행되면 다음yield표현식으로 이어져.yield표현식의yield_list값은 완료되는 코루틴이 발생시키는StopIteration예외의 값이야. 비동기 제너레이터가 값을 더 산출하지 않고 종료하면 awaitable은 대신StopAsyncIteration예외를 발생시켜 비동기 순회가 완료됐음을 알려. 이 메서드는 보통async for루프에서 암묵적으로 호출돼.async agen.asend(value): 실행하면 비동기 제너레이터의 실행을 재개하는 awaitable을 반환해. 제너레이터의send()처럼 비동기 제너레이터 함수에 값을 "보내"고,value인자가 현재yield표현식의 결과가 돼.asend()가 반환한 awaitable은 제너레이터가 산출하는 다음 값을 발생한StopIteration의 값으로 반환하거나, 값을 더 산출하지 않고 종료하면StopAsyncIteration을 발생시켜. 시작할 때asend()를 호출하려면 인자로None을 줘야 해.async agen.athrow(value)/async agen.athrow(type[, value[, traceback]]): 비동기 제너레이터가 일시 중단된 지점에서type타입의 예외를 발생시키고, 제너레이터 함수가 산출하는 다음 값을 발생한StopIteration예외의 값으로 반환하는 awaitable을 반환해. 값을 더 산출하지 않고 종료하면 awaitable이StopAsyncIteration을 발생시켜. — 버전 3.12에서 변경: 두 번째 시그니처 deprecated.async agen.aclose(): 실행하면 일시 중단 지점에서 비동기 제너레이터 함수에GeneratorExit를 던지는 awaitable을 반환해. 함수가 정상 종료하거나, 이미 닫혀 있거나,GeneratorExit를 발생시키면 반환된 awaitable은StopIteration예외를 발생시켜. 이후 비동기 제너레이터에 대한 호출이 반환하는 추가 awaitable은StopAsyncIteration을 발생시켜. 비동기 제너레이터가 값을 산출하면 awaitable이RuntimeError를 발생시켜. 다른 예외를 발생시키면 호출자에게 전파돼. 이미 예외나 정상 종료로 종료됐다면 이후aclose()호출은 아무것도 하지 않는 awaitable을 반환해.
6.3. 일차식 (Primaries)
일차식(primary)은 언어에서 가장 강하게 결합하는 연산들을 나타내. 그 문법은:
primary
: atom
| attributeref
| subscription
| call
6.3.1. 속성 참조 (Attribute references)
속성 참조는 일차식 뒤에 점과 이름이 오는 형태야:
attributeref
: primary "." identifier
일차식은 속성 참조를 지원하는 타입의 객체로 평가돼야 해(대부분의 객체가 그래). 그러면 그 객체에 이름이 identifier인 속성을 만들어 달라고 요청해. 만들어지는 타입과 값은 객체가 결정해. 같은 속성 참조를 여러 번 평가하면 서로 다른 객체가 나올 수 있어.
이 생성 규칙은 __getattribute__()나 __getattr__() 메서드를 오버라이드해서 커스터마이즈할 수 있어. __getattribute__()가 먼저 호출되고, 값을 반환하거나 속성이 없으면 AttributeError를 발생시켜. AttributeError가 발생하고 객체에 __getattr__() 메서드가 있으면 그 메서드가 대체(fallback)로 호출돼.
6.3.2. 인덱싱과 슬라이싱 (Subscriptions and slicings)
인덱싱(subscription) 문법은 보통 컨테이너에서 요소를 선택하는 데 쓰여 — 예를 들어 dict에서 값을 가져올 때:
>>> digits_by_name = {'one': 1, 'two': 2}
>>> digits_by_name['two'] # Subscripting a dictionary using the key 'two'
2
인덱싱 문법에서, 인덱싱되는 객체(일차식) 뒤에 대괄호 안의 subscript가 와. 가장 단순한 경우 subscript는 단일 표현식이야.
인덱싱되는 객체의 타입에 따라 subscript는 키(key, 매핑에서), 인덱스(index, 시퀀스에서), 또는 **타입 인자(type argument, 제네릭 타입에서)**라고 불려. 문법적으로는 모두 동등해:
>>> colors = ['red', 'blue', 'green', 'black']
>>> colors[3] # Subscripting a list using the index 3
'black'
>>> list[str] # Parameterizing the list type using the type argument str
list[str]
런타임에 인터프리터는 일차식과 subscript를 평가하고, subscript를 인자로 일차식의 __getitem__() 또는 __class_getitem__() 특수 메서드를 호출해. 어떤 메서드가 호출되는지 자세히는 __class_getitem__ vs __getitem__을 참고해.
인덱싱이 어떻게 동작하는지 보여주기 위해 __getitem__()을 구현해 subscript 값을 출력하는 커스텀 객체를 정의할 수 있어:
>>> class SubscriptionDemo:
... def __getitem__(self, key):
... print(f'subscripted with: {key!r}')
...
>>> demo = SubscriptionDemo()
>>> demo[1]
subscripted with: 1
>>> demo['a' * 3]
subscripted with: 'aaa'
인덱싱은 할당이나 삭제 문의 대상으로도 쓸 수 있어. 이 경우 인터프리터는 __getitem__() 대신 각각 인덱싱된 객체의 __setitem__() 또는 __delitem__() 특수 메서드를 호출해:
>>> colors = ['red', 'blue', 'green', 'black']
>>> colors[3] = 'white' # Setting item at index
>>> colors
['red', 'blue', 'green', 'white']
>>> del colors[3] # Deleting item at index 3
>>> colors
['red', 'blue', 'green']
다음 절들에서 설명하는 모든 고급 subscript 형태도 할당·삭제에 사용할 수 있어.
6.3.2.1. 슬라이싱 (Slicings)
더 고급 형태의 인덱싱인 **슬라이싱(slicing)**은 보통 시퀀스의 일부를 추출하는 데 쓰여. 이 형태에서 subscript는 **슬라이스(slice)**야 — 콜론으로 구분된 최대 세 개의 표현식이지. 표현식은 어느 것이나 생략할 수 있지만, 슬라이스는 콜론을 하나 이상 포함해야 해:
>>> number_names = ['zero', 'one', 'two', 'three', 'four', 'five']
>>> number_names[1:3]
['one', 'two']
>>> number_names[1:]
['one', 'two', 'three', 'four', 'five']
>>> number_names[:3]
['zero', 'one', 'two']
>>> number_names[:]
['zero', 'one', 'two', 'three', 'four', 'five']
>>> number_names[::2]
['zero', 'two', 'four']
>>> number_names[:-3]
['zero', 'one', 'two']
>>> del number_names[4:]
>>> number_names
['zero', 'one', 'two', 'three']
슬라이스를 평가하면 인터프리터는 콜론 사이 표현식들의 결과를 각각 start, stop, step 속성으로 갖는 slice 객체를 구성해. 생략된 표현식은 None으로 평가돼. 그 slice 객체는 위처럼 __getitem__() 또는 __class_getitem__() 특수 메서드에 전달돼:
# continuing with the SubscriptionDemo instance defined above:
>>> demo[2:3]
subscripted with: slice(2, 3, None)
>>> demo[::'spam']
subscripted with: slice(None, None, 'spam')
6.3.2.2. 쉼표로 구분된 subscript (Comma-separated subscripts)
subscript는 쉼표로 구분된 두 개 이상의 표현식이나 슬라이스로도 줄 수 있어:
# continuing with the SubscriptionDemo instance defined above:
>>> demo[1, 2, 3]
subscripted with: (1, 2, 3)
>>> demo[1:2, 3]
subscripted with: (slice(1, 2, None), 3)
이 형태는 다차원 데이터를 슬라이싱하는 수치 라이브러리에서 흔히 쓰여. 이 경우 인터프리터는 표현식·슬라이스 결과들의 튜플을 구성해 위처럼 __getitem__()이나 __class_getitem__()에 전달해. subscript는 단일 표현식이나 슬라이스 뒤에 쉼표를 붙여 한 요소 튜플을 지정할 수도 있어:
>>> demo['spam',]
subscripted with: ('spam',)
6.3.2.3. 별표(*) subscript (Starred subscriptions)
- 버전 3.11에서 추가: tuple_slices의 표현식은 별표(*)를 붙일 수 있어(PEP 646).
subscript는 **별표 표현식(starred expression)**도 포함할 수 있어. 이 경우 인터프리터는 결과를 튜플로 풀어서 그 튜플을 __getitem__()이나 __class_getitem__()에 전달해:
# continuing with the SubscriptionDemo instance defined above:
>>> demo[*range(10)]
subscripted with: (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
별표 표현식은 쉼표로 구분된 표현식·슬라이스와 결합할 수 있어:
>>> demo['a', 'b', *range(3), 'c']
subscripted with: ('a', 'b', 0, 1, 2, 'c')
6.3.2.4. 공식 인덱싱 문법 (Formal subscription grammar)
subscription
: primary "[" subscript "]"
subscript
: single_subscript
| tuple_subscript
single_subscript
: proper_slice
| assignment_expression
proper_slice
: [ expression ] ":" [ expression ] [ ":" [ expression ] ]
tuple_subscript
: "," . (single_subscript | starred_expression)+ [ "," ]
| 연산자는 **순서 있는 선택(ordered choice)**을 뜻한다는 걸 기억해. 구체적으로 subscript에서 두 대안이 모두 매치되면 첫 번째(single_subscript)가 우선해.
6.3.3. 호출 (Calls)
호출은 callable 객체(예: 함수)를 (아마도 빈) 인자 시리즈와 함께 호출해:
call
: primary "(" [ argument_list [ "," ] | comprehension ] ")"
argument_list
: positional_arguments [ "," starred_and_keywords ] [ "," keywords_arguments ]
| starred_and_keywords [ "," keywords_arguments ]
| keywords_arguments
positional_arguments
: positional_item ( "," positional_item )*
positional_item
: assignment_expression
| "*" expression
starred_and_keywords
: ( "*" expression | keyword_item ) ( "," "*" expression | "," keyword_item )*
keywords_arguments
: ( keyword_item | "**" expression ) ( "," keyword_item | "," "**" expression )*
keyword_item
: identifier "=" expression
위치·키워드 인자 뒤에는 선택적인 **후행 쉼표(trailing comma)**가 올 수 있지만 의미에는 영향을 주지 않아.
일차식은 callable 객체로 평가돼야 해(사용자 정의 함수, 내장 함수, 내장 객체의 메서드, 클래스 객체, 클래스 인스턴스의 메서드, 그리고 __call__() 메서드를 가진 모든 객체가 callable이야). 모든 인자 표현식은 호출이 시도되기 전에 평가돼. 형식 매개변수 목록의 문법은 Function definitions 섹션을 참고해.
키워드 인자가 있으면 다음과 같이 먼저 위치 인자로 변환돼. 먼저 형식 매개변수를 위한 비어 있는 슬롯 목록을 만들어. 위치 인자가 N개면 첫 N개 슬롯에 넣어. 다음으로 각 키워드 인자에 대해 그 identifier로 해당 슬롯을 결정해(identifier가 첫 형식 매개변수 이름과 같으면 첫 슬롯, 이런 식). 슬롯이 이미 채워져 있으면 TypeError 예외가 발생해. 그렇지 않으면 인자를 슬롯에 넣어 채워(None이어도 슬롯을 채워). 모든 인자를 처리한 뒤에도 비어 있는 슬롯은 함수 정의의 해당 기본값으로 채워져. (기본값은 함수 정의 시 한 번 계산돼; 그래서 기본값으로 쓰인 리스트·딕셔너리 같은 가변 객체는 해당 슬롯에 인자 값을 지정하지 않는 모든 호출에서 공유돼 — 보통 피해야 해.) 기본값이 지정되지 않은 빈 슬롯이 남으면 TypeError 예외가 발생해. 그 외에는 채워진 슬롯 목록을 호출의 인자 목록으로 써.
CPython 구현 세부사항: 구현은 위치 매개변수에 이름이 없는 내장 함수를 제공할 수 있어(문서화 목적으로 '이름'이 있더라도), 그래서 키워드로 줄 수 없어. CPython에서는 C로 구현되어 PyArg_ParseTuple()로 인자를 파싱하는 함수가 그렇지.
위치 인자가 형식 매개변수 슬롯보다 많으면 TypeError 예외가 발생해. 단, *identifier 문법을 쓰는 형식 매개변수가 있으면 그 매개변수가 초과 위치 인자를 담은 튜플을 받아(초과 위치 인자가 없으면 빈 튜플). 어떤 키워드 인자도 형식 매개변수 이름과 일치하지 않으면 TypeError 예외가 발생해. 단, **identifier 문법의 형식 매개변수가 있으면 그 매개변수가 초과 키워드 인자를 담은 딕셔너리를 받아(키워드를 키로, 인자 값을 값으로), 초과 인자가 없으면 (새) 빈 딕셔너리를 받아.
함수 호출에 *expression 문법이 나오면 그 expression은 **반복 가능(iterable)**하다고 평가돼야 해. 그 순회식의 요소들은 추가 위치 인자처럼 취급돼. f(x1, x2, *y, x3, x4)에서 y가 시퀀스 y1, …, yM으로 평가되면, 이는 M+4개의 위치 인자 x1, x2, y1, …, yM, x3, x4를 쓰는 호출과 동등해.
이 결과로, *expression 문법이 명시적 키워드 인자 뒤에 나타날 수는 있지만 키워드 인자보다 먼저 처리돼(아래 **expression 인자도 마찬가지). 그래서:
>>> def f(a, b):
... print(a, b)
...
>>> f(b=1, *(2,))
2 1
>>> f(a=1, *(2,))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: f() got multiple values for keyword argument 'a'
>>> f(1, *(2,))
1 2
같은 호출에서 키워드 인자와 *expression 문법을 모두 쓰는 것은 드물어서, 실제로 이런 혼동은 자주 생기지 않아.
함수 호출에 **expression 문법이 나오면 그 expression은 **매핑(mapping)**으로 평가돼야 하고, 그 내용은 추가 키워드 인자로 취급돼. 키가 일치하는 매개변수에 이미 값이 주어졌으면(명시적 키워드 인자나 다른 언패킹으로) TypeError 예외가 발생해. **expression을 쓸 때 그 매핑의 각 키는 문자열이어야 해. 매핑의 각 값은 이름이 키와 같은, 키워드 할당 자격이 있는 첫 형식 매개변수에 할당돼. 키가 반드시 파이썬 식별자일 필요는 없어("max-temp °F"처럼 선언 가능한 형식 매개변수와 매치되지 않는 것도 허용) — 형식 매개변수와 매치되는 게 없으면, ** 매개변수가 있으면 그 키-값 쌍이 거기 모이고, 없으면 TypeError 예외가 발생해.
*identifier나 **identifier 문법을 쓰는 형식 매개변수는 위치 인자 슬롯이나 키워드 인자 이름으로 사용할 수 없어.
- 버전 3.5에서 변경: 함수 호출이 임의 개수의
*·**언패킹을 받아들임. 위치 인자가 반복 가능 언패킹(*) 뒤에 올 수 있고, 키워드 인자가 딕셔너리 언패킹(**) 뒤에 올 수 있어(PEP 448).
호출은 예외를 발생시키지 않는 한 항상 어떤 값을 반환해(아마 None). 이 값이 계산되는 방식은 callable 객체의 타입에 따라 달라져:
- 사용자 정의 함수: 함수의 코드 블록이 인자 목록을 받아 실행돼. 코드 블록이 가장 먼저 하는 일은 형식 매개변수를 인자에 바인딩하는 것이야(Function definitions 섹션 참고). 코드 블록이
return문을 실행하면 그게 함수 호출의 반환 값을 지정해. 코드 블록을 끝까지return없이 실행하면 반환 값은None이야. - 내장 함수·메서드: 결과는 인터프리터에 달려 있어(Built-in Functions 참고).
- 클래스 객체: 그 클래스의 새 인스턴스가 반환돼.
- 클래스 인스턴스 메서드: 대응하는 사용자 정의 함수가, 호출의 인자 목록보다 하나 긴 인자 목록으로 호출돼 — 인스턴스가 첫 인자가 돼.
- 클래스 인스턴스: 클래스가
__call__()메서드를 정의해야 해; 그러면 그 메서드를 호출한 것과 같은 효과가 나.
6.4. Await 표현식 (Await expression)
코루틴의 실행을 awaitable 객체에서 일시 중단시켜. 오직 코루틴 함수 안에서만 쓸 수 있어.
await_expr
: "await" primary
- 버전 3.5에서 추가.
6.5. 거듭제곱 연산자 (The power operator)
거듭제곱 연산자는 왼쪽의 단항 연산자보다는 강하게, 오른쪽의 단항 연산자보다는 약하게 결합해. 문법은:
power
: ( await_expr | primary ) [ "**" u_expr ]
따라서 괄호 없는 거듭제곱·단항 연산자 시퀀스에서 연산자는 오른쪽에서 왼쪽으로 평가돼(이것은 피연산자의 평가 순서를 제약하지 않아): -1**2는 -1이 돼.
거듭제곱 연산자는 두 인자로 호출한 내장 pow() 함수와 같은 의미를 가져: 왼쪽 인자를 오른쪽 인자만큼 거듭제곱한 결과를 산출해. 수치 인자는 먼저 공통 타입으로 변환되고 결과도 그 타입이야. int 피연산자의 경우 두 번째 인자가 음수가 아니면 결과는 피연산자와 같은 타입이야; 음수면 모든 인자가 float으로 변환되고 float 결과가 나와. 예를 들어 10**2는 100이지만 10**-2는 0.01이야. 0.0을 음의 거듭제곱으로 올리면 ZeroDivisionError가 발생해. 음수를 분수 거듭제곱으로 올리면 복소수가 돼(이전 버전에서는 ValueError를 발생시켰어). 이 연산은 __pow__()와 __rpow__() 특수 메서드로 커스터마이즈할 수 있어.
6.6. 단항 산술·비트 연산 (Unary arithmetic and bitwise operations)
모든 단항 산술·비트 연산은 같은 우선순위를 가져:
u_expr
: power
| "-" u_expr
| "+" u_expr
| "~" u_expr
- 단항
-(마이너스) 연산자는 수치 인자의 부정을 산출해;__neg__()특수 메서드로 오버라이드할 수 있어. - 단항
+(플러스) 연산자는 수치 인자를 그대로 산출해;__pos__()로 오버라이드할 수 있어. - 단항
~(invert) 연산자는 정수 인자의 비트 반전을 산출해.x의 비트 반전은-(x+1)로 정의돼. 정수 타입이나__invert__()특수 메서드를 오버라이드한 커스텀 객체에만 적용돼.
세 경우 모두 인자가 적절한 타입이 아니면 TypeError 예외가 발생해.
6.7. 이항 산술 연산 (Binary arithmetic operations)
이항 산술 연산은 관례적인 우선순위 수준을 가져. 이 연산 중 일부는 특정 비수치 타입에도 적용된다는 점에 주의해. 거듭제곱 연산자를 제외하면 수준이 두 개뿐이야 — 하나는 곱셈 계열 연산자, 하나는 덧셈 계열 연산자:
m_expr
: u_expr
| m_expr "*" u_expr
| m_expr "@" m_expr
| m_expr "//" u_expr
| m_expr "/" u_expr
| m_expr "%" u_expr
a_expr
: m_expr
| a_expr "+" m_expr
| a_expr "-" m_expr
*(곱셈) 연산자는 인자들의 곱을 산출해. 인자는 둘 다 숫자이거나, 하나는 정수이고 다른 하나는 시퀀스여야 해. 전자의 경우 숫자를 공통 실수 타입으로 변환해 곱해. 후자의 경우 시퀀스 반복이 수행돼; 음의 반복 계수는 빈 시퀀스를 산출해.__mul__()·__rmul__()로 커스터마이즈할 수 있어. — 버전 3.14에서 변경: 한 피연산자만 복소수면 다른 쪽은 부동소수점으로 변환됨.@(at) 연산자는 행렬 곱셈에 쓰려는 의도야. 내장 파이썬 타입 중 이 연산자를 구현하는 것은 없어.__matmul__()·__rmatmul__()로 커스터마이즈할 수 있어. — 버전 3.5에서 추가./(나눗셈)와//(바닥 나눗셈) 연산자는 인자들의 몫을 산출해. 수치 인자는 먼저 공통 타입으로 변환돼. 정수 나눗셈은 float을, 정수 바닥 나눗셈은 정수를 산출해; 결과는 수학적 나눗셈에 '바닥(floor)' 함수를 적용한 결과야. 0으로 나누면ZeroDivisionError가 발생해./는__truediv__()·__rtruediv__(),//는__floordiv__()·__rfloordiv__()로 커스터마이즈할 수 있어.%(나머지) 연산자는 첫 인자를 두 번째 인자로 나눈 나머지를 산출해. 수치 인자는 먼저 공통 타입으로 변환돼. 오른쪽 인자가 0이면ZeroDivisionError가 발생해. 인자는 부동소수점일 수 있어(예:3.14 % 0.7은0.34, 3.14 = 4*0.7 + 0.34이니까). 나머지 연산자는 항상 두 번째 피연산자와 같은 부호의 결과를 산출하고(또는 0), 결과의 절댓값은 두 번째 피연산자의 절댓값보다 엄격히 작아.[^1]- 바닥 나눗셈과 나머지는 다음 항등식으로 연결돼:
x == (x//y)*y + (x%y). 바닥 나눗셈과 나머지는 내장divmod()와도 연결돼:divmod(x, y) == (x//y, x%y).[^2] - 숫자에 대한 나머지 연산 외에도
%연산자는 문자열 객체에서 구식 문자열 포맷팅(일명 보간)으로도 오버로드돼. 문자열 포맷팅 문법은 printf-style String Formatting 섹션을 참고해.__mod__()·__rmod__()로 커스터마이즈할 수 있어. - 바닥 나눗셈·나머지·
divmod()는 복소수에 대해 정의되지 않아. 대신 적절하다면abs()로 부동소수점으로 변환해. +(덧셈) 연산자는 인자들의 합을 산출해. 인자는 둘 다 숫자이거나 둘 다 같은 타입의 시퀀스여야 해. 전자의 경우 공통 실수 타입으로 변환해 더하고, 후자의 경우 시퀀스를 연결해.__add__()·__radd__()로 커스터마이즈할 수 있어. — 버전 3.14에서 변경: 한 피연산자만 복소수면 다른 쪽은 부동소수점으로 변환됨.-(뺄셈) 연산자는 인자들의 차를 산출해. 수치 인자는 먼저 공통 실수 타입으로 변환돼.__sub__()·__rsub__()로 커스터마이즈할 수 있어. — 버전 3.14에서 변경: 위와 동일.
6.8. 시프트 연산 (Shifting operations)
시프트 연산은 산술 연산보다 낮은 우선순위를 가져:
shift_expr
: a_expr
| shift_expr ( "<<" | ">>" ) a_expr
이 연산자는 정수를 인자로 받아. 첫 인자를 두 번째 인자가 주는 비트 수만큼 왼쪽이나 오른쪽으로 시프트해. 왼쪽 시프트는 __lshift__()·__rlshift__(), 오른쪽 시프트는 __rshift__()·__rrshift__()로 커스터마이즈할 수 있어. n비트 오른쪽 시프트는 pow(2,n)으로 바닥 나눗셈한 것으로, n비트 왼쪽 시프트는 pow(2,n)으로 곱한 것으로 정의돼.
6.9. 이항 비트 연산 (Binary bitwise operations)
세 비트 연산 각각은 다른 우선순위 수준을 가져:
and_expr
: shift_expr
| and_expr "&" shift_expr
xor_expr
: and_expr
| xor_expr "^" and_expr
or_expr
: xor_expr
| or_expr "|" xor_expr
&연산자는 인자들의 비트 AND를 산출해. 인자는 정수이거나 그중 하나가__and__()·__rand__()를 오버라이드한 커스텀 객체여야 해.^연산자는 인자들의 비트 XOR(배타적 OR)를 산출해. 인자는 정수이거나 그중 하나가__xor__()·__rxor__()를 오버라이드한 커스텀 객체여야 해.|연산자는 인자들의 비트 (포함) OR를 산출해. 인자는 정수이거나 그중 하나가__or__()·__ror__()를 오버라이드한 커스텀 객체여야 해.
6.10. 비교 (Comparisons)
C와 달리, 파이썬의 모든 비교 연산은 같은 우선순위를 가지며, 이는 어떤 산술·시프트·비트 연산보다 낮아. 또 C와 달리 a < b < c 같은 표현식은 수학에서 관례적인 해석을 가져:
comparison
: or_expr ( comp_operator or_expr )*
comp_operator
: "<" | ">" | "==" | ">=" | "<=" | "!="
| "is" [ "not" ]
| [ "not" ] "in"
비교는 **불리언 값 True 또는 False**를 산출해. 커스텀 확장 비교(rich comparison) 메서드는 비불리언 값을 반환할 수 있는데, 이 경우 파이썬은 불리언 문맥에서 그 값에 bool()을 호출해.
비교는 **임의로 연결(chaining)**할 수 있어. 즉 x < y <= z는 x < y and y <= z와 동등한데, y는 한 번만 평가된다는 점이 달라(그리고 두 경우 모두 x < y가 거짓으로 판명되면 z는 전혀 평가되지 않아). 공식적으로, a, b, c, …, y, z가 표현식이고 op1, op2, …, opN이 비교 연산자라면, a op1 b op2 c ... y opN z는 a op1 b and b op2 c and ... y opN z와 동등하고, 각 표현식은 최대 한 번 평가된다는 점만 달라. a op1 b op2 c가 a와 c 사이의 어떤 비교를 암시하지는 않는다는 점에 주의해 — 그래서 x < y > z은 완전히 합법이야(보기 좋진 않아도).
6.10.1. 값 비교 (Value comparisons)
연산자 <, >, ==, >=, <=, !=는 두 객체의 값을 비교해. 객체는 같은 타입일 필요가 없어.
Objects, values and types 장이 객체가 (타입과 정체성 외에) 값을 가진다고 말하지. 객체의 값은 파이썬에서 꽤 추상적인 개념이야: 예를 들어 객체 값에 대한 정식 접근 메서드가 없어. 또 객체의 값이 특정 방식으로 구성되어야 한다는 요구도 없어(예: 모든 데이터 속성으로 이루어져야 한다든가). 비교 연산자는 객체의 값이 무엇인지에 대한 특정한 관념을 구현해. 비교 구현을 통해 간접적으로 객체의 값을 정의한다고 생각할 수 있어.
모든 타입은 (직접·간접적으로) object의 하위 타입이므로 object의 기본 비교 동작을 상속해. 타입은 __lt__() 같은 확장 비교 메서드를 구현해 비교 동작을 커스터마이즈할 수 있어(Basic customization 참고).
등호 비교(==, !=)의 기본 동작은 객체의 정체성에 기반해. 그래서 같은 정체성의 인스턴스끼리는 같게, 다른 정체성의 인스턴스끼리는 다르게 비교돼. 이 기본 동작의 동기는 모든 객체가 반사적(reflexive)이어야 한다는 것, 즉 x is y는 x == y를 암시한다는 바람이야. 기본 순서 비교(<, >, <=, >=)는 제공되지 않아; 시도하면 TypeError가 발생해. 이 기본 동작의 동기는 등호 같은 불변식이 없기 때문이야.
기본 등호 비교 동작(정체성이 다르면 항상 같지 않다)은, 객체 값과 값 기반 등호에 대한 합리적인 정의가 필요한 타입의 요구와 대비될 수 있어. 그런 타입은 비교 동작을 커스터마이즈해야 하는데, 실제로 많은 내장 타입이 그렇게 했어.
다음은 가장 중요한 내장 타입들의 비교 동작이야:
- 내장 수치 타입(Numeric Types — int, float, complex)과 표준 라이브러리 타입
fractions.Fraction,decimal.Decimal의 숫자는 타입 내부 및 타입 간 비교가 가능해. 단, 복소수는 순서 비교를 지원하지 않아. 연관된 타입 한도 내에서 정밀도 손실 없이 수학적으로(알고리즘적으로) 올바르게 비교해. NaN 값(float('NaN'),decimal.Decimal('NaN'))은 특별해. 숫자와 NaN 값의 어떤 순서 비교도 거짓이야. 반직관적인 결과로, NaN 값은 자기 자신과 같지 않아. 예를 들어x = float('NaN')이면3 < x,x < 3,x == x는 모두 거짓이고x != x는 참이야. 이 동작은 IEEE 754를 따른 거야. None과NotImplemented는 싱글턴이야. PEP 8은 싱글턴 비교는 항상is나is not으로 하라고 권고하지(등호 연산자는 절대 아님).- 이진 시퀀스(
bytes·bytearray인스턴스)는 타입 안에서·타입 간에 비교할 수 있어. 요소의 수치 값을 사용해 사전식(lexicographically) 비교해. - 문자열(
str인스턴스)은 문자들의 수치 유니코드 코드 포인트(내장ord()함수의 결과)를 사용해 사전식으로 비교해.[^3] 문자열과 이진 시퀀스는 직접 비교할 수 없어. - 시퀀스(
tuple·list·range인스턴스)는 각 타입 안에서만 비교할 수 있어. 단range는 순서 비교를 지원하지 않아. 타입 간 등호 비교는 같지 않음으로, 타입 간 순서 비교는TypeError로 이어져. 시퀀스는 대응 요소의 비교를 사용해 사전식으로 비교해. 내장 컨테이너는 보통 동일한 객체는 자기 자신과 같다고 가정해. 그 덕에 동일 객체에 대한 등호 테스트를 건너뛰어 성능을 높이고 내부 불변식을 유지할 수 있어. 내장 컬렉션 간 사전식 비교는 다음과 같이 동작해: (1) 두 컬렉션이 같으려면 같은 타입, 같은 길이, 그리고 각 대응 요소 쌍이 같아야 해(예:[1,2] == (1,2)는 타입이 달라 거짓). (2) 순서 비교를 지원하는 컬렉션은 첫 번째로 같지 않은 요소의 순서와 같게 정렬돼(예:[1,2,x] <= [1,2,y]는x <= y와 같은 값). (3) 대응 요소가 없으면 더 짧은 컬렉션이 먼저 정렬돼(예:[1,2] < [1,2,3]은 참). - 매핑(
dict인스턴스)은 (키, 값) 쌍이 같을 때만 같게 비교돼. 키·값의 등호 비교는 반사성을 강제해. 순서 비교(<,>,<=,>=)는TypeError를 발생시켜. - 집합(
set·frozenset인스턴스)은 타입 안에서·타입 간에 비교할 수 있어. 순서 비교 연산자를 부분집합·상위집합 테스트로 정의해. 이런 관계는 완전 순서(total ordering)를 정의하지 않아(예: 두 집합{1,2}와{2,3}은 같지도, 서로의 부분집합도, 상위집합도 아니야). 따라서 집합은 완전 순서에 의존하는 함수(min(),max(),sorted())에 부적절한 인자야. 집합 비교는 요소의 반사성을 강제해. - 대부분의 다른 내장 타입은 비교 메서드를 구현하지 않아 기본 비교 동작을 상속해.
사용자 정의 클래스가 비교 동작을 커스터마이즈할 때 가능하면 일관성 규칙을 따라야 해:
- 등호 비교는 반사적이어야 해. 즉 동일 객체는 같게 비교돼야 해:
x is y는x == y를 암시. - 비교는 대칭이어야 해. 즉 다음 표현식들의 결과가 같아야 해:
x == y와y == x,x != y와y != x,x < y와y > x,x <= y와y >= x. - **비교는 추이적(transitive)**이어야 해. 예:
x > y and y > z는x > z를 암시,x < y and y <= z는x < z를 암시. - 역 비교는 불리언 부정이어야 해. 즉 다음 결과가 같아야 해:
x == y와not (x != y),x < y와not (x >= y)(완전 순서에서),x > y와not (x <= y)(완전 순서에서). 마지막 두 표현식은 완전 정렬 컬렉션(예: 시퀀스, 집합·매핑은 아님)에 적용돼.@functools.total_ordering데코레이터 참고. hash()결과는 등호와 일치해야 해. 같은 객체는 같은 해시 값을 가지거나, 해시 불가능으로 표시되어야 해.
파이썬은 이런 일관성 규칙을 강제하지 않아. 사실 NaN 값은 이 규칙을 따르지 않는 예야.
6.10.2. 멤버십 테스트 연산 (Membership test operations)
연산자 in과 not in은 멤버십을 테스트해. x in s는 x가 s의 멤버면 True, 그렇지 않으면 False로 평가돼. x not in s는 x in s의 부정을 반환해. 모든 내장 시퀀스와 집합 타입이 이를 지원하고, 딕셔너리도 지원하는데 딕셔너리에서 in은 주어진 키가 있는지 테스트해. list, tuple, set, frozenset, dict, collections.deque 같은 컨테이너 타입에서 x in y는 any(x is e or x == e for e in y)와 동등해.
문자열·바이트 타입에서 x in y는 x가 y의 부분 문자열일 때만 True야. 동등한 테스트는 y.find(x) != -1이야. 빈 문자열은 항상 그 어떤 문자열의 부분 문자열로 간주되므로 "" in "abc"는 True를 반환해.
__contains__() 메서드를 정의한 사용자 정의 클래스에서 x in y는 y.__contains__(x)가 참 값을 반환하면 True, 그렇지 않으면 False야. __contains__()는 없지만 __iter__()를 정의한 클래스에서 x in y는 y를 순회하면서 x is z or x == z가 참인 어떤 값 z가 만들어지면 True야. 순회 중 예외가 발생하면 in이 그 예외를 발생시킨 것처럼 돼. 마지막으로 구식 순회 프로토콜이 시도돼: __getitem__()을 정의한 클래스에서 x in y는 어떤 음이 아닌 정수 인덱스 i에 대해 x is y[i] or x == y[i]이고, 그보다 낮은 정수 인덱스가 IndexError를 발생시키지 않을 때만 True야(다른 예외가 발생하면 in이 그 예외를 발생시킨 것처럼). 연산자 not in은 in의 역 진리값을 갖도록 정의돼.
6.10.3. 정체성 비교 (Identity comparisons)
연산자 is와 is not은 객체의 정체성을 테스트해: x is y는 x와 y가 같은 객체일 때만 참이야. 객체의 정체성은 id() 함수로 결정돼. x is not y는 역 진리값을 산출해.[^4]
6.11. 불리언 연산 (Boolean operations)
or_test
: and_test
| or_test "or" and_test
and_test
: not_test
| and_test "and" not_test
not_test
: comparison
| "not" not_test
불리언 연산의 맥락에서, 그리고 표현식이 제어 흐름 문에 쓰일 때, 다음 값들은 거짓으로 해석돼: False, None, 모든 타입의 수치 0, 그리고 빈 문자열·빈 컨테이너(문자열, 튜플, 리스트, 딕셔너리, 집합, frozenset 포함). 그 밖의 모든 값은 참으로 해석돼. 사용자 정의 객체는 __bool__() 메서드를 제공해 진리값을 커스터마이즈할 수 있어.
- 연산자 **
not**은 인자가 거짓이면True, 그렇지 않으면False를 산출해. - 표현식 **
x and y**는 먼저x를 평가해;x가 거짓이면 그 값을 반환하고, 그렇지 않으면y를 평가해 그 결과 값을 반환해. - 표현식 **
x or y**는 먼저x를 평가해;x가 참이면 그 값을 반환하고, 그렇지 않으면y를 평가해 그 결과 값을 반환해.
and와 or 모두 반환 값과 타입을 False/True로 제한하지 않고 마지막으로 평가된 인자를 반환한다는 점에 주의해. 이는 때로 유용해 — 예를 들어 s가 빈 경우 기본값으로 대체해야 하는 문자열이라면, s or 'foo'가 원하는 값을 산출하지. not은 새 값을 만들어야 하므로 인자의 타입과 무관하게 불리언 값을 반환해(예: not 'foo'는 ''가 아니라 False를 만들어).
6.12. 할당 표현식 (Assignment expressions)
assignment_expression
: [ identifier ":=" ] expression
할당 표현식(가끔 "named expression"이나 "walrus(해마)"라고도 불려)은 표현식을 identifier에 할당하면서, 동시에 그 표현식의 값도 반환해.
흔한 사용처 중 하나는 정규식 매칭을 처리할 때야:
if matching := pattern.search(data):
do_something(matching)
또는 파일 스트림을 청크 단위로 처리할 때:
while chunk := file.read(9000):
process(chunk)
할당 표현식은 표현식 문으로 쓰일 때, 그리고 슬라이싱, 조건식, 람다, 키워드 인자, 이해식-if 표현식 및 assert, with, 할당 문의 하위 표현식으로 쓰일 때는 반드시 괄호로 둘러싸야 해. 그 외에 사용할 수 있는 모든 곳에서는 괄호가 필요 없어 — if·while 문 포함.
- 버전 3.8에서 추가: 할당 표현식에 대한 자세한 내용은 PEP 572 참고.
6.13. 조건 표현식 (Conditional expressions)
conditional_expression
: or_test [ "if" or_test "else" expression ]
expression
: conditional_expression
| lambda_expr
조건 표현식(가끔 "삼항 연산자(ternary operator)"라고도 불려)은 if-else 문의 대안이야. 표현식이므로 값을 반환하고 하위 표현식으로 나타날 수 있어.
표현식 **x if C else y**는 먼저 x가 아니라 조건 C를 평가해. C가 참이면 x를 평가해 그 값을 반환하고, 그렇지 않으면 y를 평가해 그 값을 반환해. 조건 표현식에 대한 자세한 내용은 PEP 308 참고.
6.14. 람다 (Lambdas)
lambda_expr
: "lambda" [ parameter_list ] ":" expression
람다 표현식(가끔 lambda form이라고도 불려)은 익명 함수를 만드는 데 쓰여. 표현식 lambda parameters: expression은 함수 객체를 산출해. 그 이름 없는 객체는 다음과 같이 정의된 함수 객체처럼 동작해:
def <lambda>(parameters):
return expression
매개변수 목록의 문법은 Function definitions 섹션을 참고해. 람다 표현식으로 만든 함수에는 문(statement)이나 주석(annotation)을 담을 수 없다는 점에 주의해.
6.15. 표현식 목록 (Expression lists)
starred_expression
: "*" or_expr
| expression
flexible_expression
: assignment_expression
| starred_expression
flexible_expression_list
: flexible_expression ( "," flexible_expression )* [ "," ]
starred_expression_list
: starred_expression ( "," starred_expression )* [ "," ]
expression_list
: expression ( "," expression )* [ "," ]
yield_list
: expression_list
| starred_expression "," [ starred_expression_list ]
리스트·집합 표시의 일부가 아닐 때, 쉼표를 하나 이상 포함한 표현식 목록은 튜플을 산출해. 튜플의 길이는 목록의 표현식 개수야. 표현식들은 왼쪽에서 오른쪽으로 평가돼.
별표 *****는 **반복 가능 언패킹(iterable unpacking)**을 나타내. 그 피연산자는 반복 가능해야 해. 순회식은 항목 시퀀스로 확장되고, 언패킹되는 지점에서 새 튜플·리스트·집합에 포함돼.
- 버전 3.5에서 추가: 표현식 목록에서의 반복 가능 언패킹(PEP 448).
- 버전 3.11에서 추가: 표현식 목록의 모든 항목에 별표를 붙일 수 있음(PEP 646).
후행 쉼표는 한 항목 튜플을 만들 때만 필요해(예: 1,); 그 외 모든 경우에는 선택이야. 후행 쉼표 없는 단일 표현식은 튜플을 만들지 않고 그 표현식의 값을 산출해. (빈 튜플을 만들려면 빈 괄호 쌍 ()을 사용해.)
6.16. 평가 순서 (Evaluation order)
파이썬은 표현식을 왼쪽에서 오른쪽으로 평가해. 할당을 평가할 때는 오른쪽이 왼쪽보다 먼저 평가된다는 점에 주의해.
다음 줄들에서 표현식들은 접미사(suffix)의 산술 순서로 평가돼:
expr1, expr2, expr3, expr4
(expr1, expr2, expr3, expr4)
{expr1: expr2, expr3: expr4}
expr1 + expr2 * (expr3 - expr4)
expr1(expr2, expr3, *expr4, **expr5)
expr3, expr4 = expr1, expr2
6.17. 연산자 우선순위 (Operator precedence)
다음 표는 파이썬의 연산자 우선순위를 요약한 것으로, 높은 우선순위(가장 강한 결합)에서 낮은 우선순위(가장 약한 결합) 순서야. 같은 칸의 연산자는 같은 우선순위를 가져. 문법이 명시적으로 주어지지 않으면 연산자는 이항(binary)이야. 같은 칸의 연산자는 왼쪽에서 오른쪽으로 그룹 지어(단, 거듭제곱과 조건 표현식은 오른쪽에서 왼쪽으로 그룹).
비교, 멤버십 테스트, 정체성 테스트는 모두 같은 우선순위를 가지며 Comparisons 섹션에서 설명한 왼쪽에서 오른쪽 체이닝 기능을 가진다는 점에 주의해.
| 연산자 | 설명 |
|---|---|
(expressions...) , [expressions...] , {key: value...} , {expressions...} |
바인딩 또는 괄호로 묶인 표현식, 리스트 표시, 딕셔너리 표시, 집합 표시 |
x[index] , x[index:index] , x(arguments...) , x.attribute |
인덱싱(슬라이싱 포함), 호출, 속성 참조 |
await x |
Await 표현식 |
** |
거듭제곱 [^5] |
+x , -x , ~x |
양수, 음수, 비트 NOT |
* , @ , / , // , % |
곱셈, 행렬 곱셈, 나눗셈, 바닥 나눗셈, 나머지 [^6] |
+ , - |
덧셈과 뺄셈 |
<< , >> |
시프트 |
& |
비트 AND |
^ |
비트 XOR |
| |
비트 OR |
in , not in , is , is not , < , <= , > , >= , != , == |
비교(멤버십 테스트, 정체성 테스트 포함) |
not x |
불리언 NOT |
and |
불리언 AND |
or |
불리언 OR |
if – else |
조건 표현식 |
lambda |
람다 표현식 |
:= |
할당 표현식 |
각주:
- [^1]
abs(x%y) < abs(y)는 수학적으로는 참이지만, 반올림 때문에 float에서는 수치적으로 참이 아닐 수 있어. 예를 들어 파이썬 float이 IEEE 754 배정밀도 숫자인 플랫폼에서,-1e-100 % 1e100이1e100과 같은 부호가 되려면 계산 결과는-1e-100 + 1e100인데, 이는 수치적으로 정확히1e100과 같아.math.fmod()는 첫 인자의 부호와 일치하는 부호의 결과를 대신 반환하므로 이 경우-1e-100을 반환해. 어느 접근이 더 적절한지는 애플리케이션에 달려 있어. - [^2]
x가y의 정확한 정수 배수에 매우 가까우면, 반올림 때문에x//y가(x-x%y)//y보다 하나 클 수 있어. 그런 경우 파이썬은divmod(x,y)[0] * y + x % y가x에 아주 가깝게 유지되도록 후자 결과를 반환해. - [^3] 유니코드 표준은 코드 포인트(예: U+0041)와 추상 문자(예: "LATIN CAPITAL LETTER A")를 구분해. 대부분의 추상 문자는 하나의 코드 포인트로만 표현되지만, 둘 이상의 코드 포인트 시퀀스로도 표현될 수 있는 추상 문자도 있어. 예를 들어 "LATIN CAPITAL LETTER C WITH CEDILLA"는 결합 문자 앞의 기본 문자(결합 세딜라 U+0327) 시퀀스로도 표현될 수 있어. 문자열의 비교 연산자는 유니코드 코드 포인트 수준에서 비교해. 이는 인간에게 반직관적일 수 있어 —
"\u00C7" == "\u0043\u0327"는 두 문자열이 같은 추상 문자를 나타내더라도False야. 추상 문자 수준(즉 인간에게 직관적인 방식)에서 문자열을 비교하려면unicodedata.normalize()를 사용해. - [^4] 자동 가비지 컬렉션, 자유 목록, 디스크립터의 동적 특성 때문에, 인스턴스 메서드나 상수 간 비교처럼
is연산자의 특정 사용에서 이상해 보이는 동작을 볼 수 있어. 자세한 내용은 그들의 문서를 확인해. - [^5] 거듭제곱 연산자
**는 오른쪽의 산술·비트 단항 연산자보다 약하게 결합해. 즉2**-1은0.5야. - [^6]
%연산자는 문자열 포맷팅에도 쓰이는데, 같은 우선순위가 적용돼.
더 알아보기
- 표현식 문서 원문 — 예제와 문법 세부가 더 자세히 있어.
- 파이썬 연산자 우선순위 자습서 — 위 표의 원문.
- 관련 표준 라이브러리 문서: Numeric Types, Built-in Functions, printf-style String Formatting.