파이썬 데이터 모델
파이썬 데이터 모델 (Data Model)
이 문서는 파이썬의 가장 근본적인 개념인 **객체 모델(object model)**을 다루는 언어 레퍼런스야. 객체가 뭔지, 객체의 정체성·타입·값이 어떻게 정의되는지, 그리고 객체가 특수 메서드(special method)를 통해 어떻게 연산자와 문법에 참여하는지 전부 살펴볼 거야. 특히 __init__, __getitem__, __iter__, __enter__ 같은 특수 메서드들의 시그니처와 동작 방식을 정확하게 정리해 뒀으니, 직접 클래스나 프로토콜을 정의할 때 꼭 참고해.
출처: Python 언어 레퍼런스
객체, 값, 타입 (Objects, values and types)
*객체(Object)*는 파이썬이 데이터를 추상화한 것이야. 파이썬 프로그램의 모든 데이터는 객체 또는 객체 사이의 관계로 표현돼. 심지어 코드조차 객체로 표현되지.
모든 객체는 정체성(identity), 타입(type), **값(value)**을 가져. 객체의 정체성은 생성된 뒤에는 절대 변하지 않아. 메모리에서의 객체 주소라고 생각하면 돼. is 연산자는 두 객체의 정체성을 비교하고, id() 함수는 정체성을 나타내는 정수를 반환해.
CPython 구현 세부사항: CPython에서 id(x)는 x가 저장된 메모리 주소야.
객체의 타입은 그 객체가 지원하는 연산(예: "길이를 가질 수 있나?")을 결정하고, 그 타입의 객체가 가질 수 있는 가능한 값도 정의해. type() 함수는 객체의 타입을 반환해(타입도 그 자체로 객체지). 정체성과 마찬가지로 객체의 타입도 바꿀 수 없어. [1]
몇몇 객체는 값이 바뀔 수 있어. 값이 바뀔 수 있는 객체를 가변(mutable), 생성된 뒤에 값이 바뀌지 않는 객체를 *불변(immutable)*이라고 불러. (가변 객체에 대한 참조를 담고 있는 불변 컨테이너 객체는, 그 가변 객체의 값이 바뀌면 컨테이너의 값도 바뀔 수 있어. 그래도 컨테이너 자신은 여전히 불변으로 간주돼. 그 이유는 컨테이너가 담고 있는 객체들의 집합 자체는 바뀔 수 없기 때문이야. 즉 불변성은 "값이 절대 안 바뀐다"는 것과 완전히 같지 않고, 좀 더 미묘해.) 객체의 가변성은 타입이 결정해. 예를 들어 숫자, 문자열, 튜플은 불변이고, 딕셔너리와 리스트는 가변이야.
객체는 명시적으로 파괴되지 않아. 다만 도달할 수 없게(unreachable) 되면 가비지 컬렉션(garbage collection)의 대상이 될 수 있지. 구현은 가비지 컬렉션을 미루거나 아예 생략할 수도 있어. 도달 가능한 객체를 수집하지 않는 한, 어떻게 구현하느냐는 구현 품질의 문제야.
CPython 구현 세부사항: CPython은 현재 참조 카운팅(reference counting) 방식에 (선택적) 순환 참조 가비지의 지연 탐지가 결합된 방식을 써. 대부분의 객체는 도달할 수 없게 되는 즉시 수집되지만, 순환 참조를 포함한 가비지는 수집이 보장되지는 않아. 순환 가비지 수집을 제어하는 방법은 gc 모듈 문서를 참고해. 다른 구현은 다르게 동작할 수 있고 CPython도 바뀔 수 있어. 객체가 도달 불가능해졌다고 해서 즉시 finalize(종료 처리)된다고 의존하면 안 돼(그래서 파일은 항상 명시적으로 닫아야 해).
주의할 점으로, 구현의 트레이싱(tracing)이나 디버깅 기능을 사용하면 평소라면 수집될 객체들이 살아남을 수 있어. 또 try…except 문으로 예외를 잡는 것도 객체를 살려둘 수 있지.
몇몇 객체는 열린 파일이나 창 같은 "외부" 리소스에 대한 참조를 담고 있어. 이런 리소스는 객체가 가비지 컬렉션될 때 해제된다고 이해되지만, 가비지 컬렉션이 보장되는 건 아니므로 그런 객체들은 외부 리소스를 해제하는 명시적인 방법(보통 close() 메서드)도 제공해. 프로그램은 이런 객체를 명시적으로 닫기를 강력히 권장해. try…finally 문과 with 문이 이를 편리하게 해 주는 방법이야.
어떤 객체들은 다른 객체에 대한 참조를 담는데, 이를 *컨테이너(container)*라고 불러. 컨테이너의 예로 튜플, 리스트, 딕셔너리가 있어. 그 참조들은 컨테이너의 값의 일부야. 대부분의 경우 컨테이너의 값을 이야기할 때는 담겨 있는 객체들의 값(정체성이 아니라)을 뜻해. 다만 컨테이너의 가변성을 이야기할 때는 바로 담긴 객체들의 정체성만을 의미해. 그래서 불변 컨테이너(튜플처럼)가 가변 객체에 대한 참조를 담고 있다면, 그 가변 객체가 바뀔 때 컨테이너의 값도 바뀌는 거야.
타입은 객체 동작의 거의 모든 측면에 영향을 미쳐. 심지어 객체 정체성의 중요성에도 어느 정도 영향을 줘. 불변 타입의 경우 새 값을 계산하는 연산은 같은 타입·값을 가진 기존 객체에 대한 참조를 반환할 수도 있지만, 가변 객체는 그렇게 허용되지 않아. 예를 들어 a = 1; b = 1 이후에 a와 b는 값이 1인 같은 객체를 가리킬 수도 있고 아닐 수도 있어(구현에 따라 달라). 이는 int가 불변 타입이라 1에 대한 참조를 재사용할 수 있기 때문이야. 이 동작은 사용하는 구현에 의존하므로 믿으면 안 되지만, 객체 정체성 테스트를 사용할 때는 알아두면 좋은 사실이야. 반면 c = []; d = [] 이후에는 c와 d가 서로 다른, 고유한, 새로 생성된 두 개의 빈 리스트를 가리킨다고 보장돼. (참고로 e = f = []는 e와 f에 같은 객체를 할당해.)
표준 타입 계층 (The standard type hierarchy)
아래는 파이썬에 내장된 타입들의 목록이야. 확장 모듈(구현에 따라 C, Java 또는 다른 언어로 작성된)은 추가 타입을 정의할 수 있어. 미래의 파이썬 버전은 타입 계층에 타입을 추가할 수도 있어(예: 유리수, 정수를 효율적으로 저장한 배열 등). 다만 그런 추가는 보통 표준 라이브러리를 통해 제공되곤 해.
아래 타입 설명 중 일부에는 '특수 속성(special attributes)'을 나열한 문단이 포함돼 있어. 이 속성들은 구현에 대한 접근을 제공하는 속성이라 일반적인 용도로 쓰게 만든 게 아니야. 정의는 미래에 바뀔 수 있어.
None
이 타입은 단 하나의 값만 가져. 이 값을 가진 단일 객체가 존재하는데, 내장 이름 None으로 접근해. 값이 없음을 나타내는 데 여러 상황에서 쓰여. 예를 들어 명시적으로 아무것도 반환하지 않는 함수에서 반환되곤 하지. 참값(truth value)은 거짓이야.
NotImplemented
이 타입은 단 하나의 값만 가져. 이 값을 가진 단일 객체가 존재하는데, 내장 이름 NotImplemented로 접근해. 숫자 메서드와 확장 비교(rich comparison) 메서드는 제공된 피연산자에 대해 연산을 구현하지 않을 때 이 값을 반환해야 해. (그러면 인터프리터가 연산자에 따라 반영 연산(reflected operation)이나 다른 대체 동작을 시도해.) 이 값은 불리언 문맥에서 평가하면 안 돼.
더 자세한 내용은 산술 연산 구현하기를 참고해.
버전 3.9에서 변경: 불리언 문맥에서 NotImplemented를 평가하는 것이 deprecated 처리됐어.
버전 3.14에서 변경: 불리언 문맥에서 NotImplemented를 평가하면 이제 TypeError가 발생해. 파이썬 3.9부터는 이전에 True로 평가되고 DeprecationWarning을 발생시켰어.
Ellipsis
이 타입은 단 하나의 값만 가져. 이 값을 가진 단일 객체가 존재하는데, 리터럴 ... 또는 내장 이름 Ellipsis로 접근해. 참값은 참이야.
numbers.Number
이 타입의 객체는 숫자 리터럴로 생성되고, 산술 연산자와 산술 내장 함수의 결과로 반환돼. 숫자 객체는 불변이야. 생성된 뒤에는 값이 절대 바뀌지 않아. 파이썬의 숫자는 당연히 수학적 숫자와 밀접하게 관련돼 있지만, 컴퓨터에서의 숫자 표현 한계의 영향을 받아.
숫자 클래스의 문자열 표현(__repr__()과 __str__()로 계산된 것)은 다음과 같은 속성들을 가져:
-
클래스 생성자에 전달하면 원래 숫자와 같은 값을 가진 객체를 만드는 유효한 숫자 리터럴이야.
-
가능하면 10진법으로 표현돼.
-
소수점 앞의 단일 0 하나를 제외하면, 앞에 붙는 0은 표시되지 않아.
-
소수점 뒤의 단일 0 하나를 제외하면, 뒤에 붙는 0은 표시되지 않아.
-
숫자가 음수일 때만 부호가 표시돼.
파이썬은 정수, 부동소수점 숫자, 복소수를 구분해:
numbers.Integral
이 타입은 수학적 정수 집합(양수와 음수)의 원소를 나타내.
참고
정수 표현 규칙은 음수 정수와 관련된 시프트 및 마스크 연산에 가장 의미 있는 해석을 제공하도록 의도된 거야.
정수에는 두 가지 타입이 있어:
정수 (int)
이 타입은 무한한 범위의 숫자를 나타내며, 오직 사용 가능한 (가상) 메모리에 의해서만 제한돼. 시프트와 마스크 연산을 위해 2진 표현을 가정하고, 음수는 2의 보수(2's complement)의 변형으로 표현돼. 이때 왼쪽으로 무한히 확장되는 부호 비트의 시퀀스처럼 보이게 돼.
불리언 (bool)
이 타입은 참값 False와 True를 나타내. 각각 False와 True 값을 나타내는 두 객체가 불리언 객체의 전부야. 불리언 타입은 정수 타입의 하위 타입이며, 불리언 값은 거의 모든 문맥에서 각각 0과 1처럼 동작해. 단, 문자열로 변환할 때는 각각 "False"또는 "True" 문자열이 반환된다는 예외가 있어.
numbers.Real (float)
이 타입은 머신 레벨의 배정밀도(double precision) 부동소수점 숫자를 나타내. 허용되는 범위와 오버플로 처리 측면에서 기본 머신 아키텍처(및 C나 Java 구현)의 영향을 받아. 파이썬은 단정밀도 부동소수점 숫자를 지원하지 않아. 보통 그걸 쓰는 이유인 프로세서·메모리 절약은 파이썬에서 객체를 쓰는 오버헤드에 비해 압도적으로 작으므로, 두 종류의 부동소수점 숫자로 언어를 복잡하게 만들 이유가 없어.
numbers.Complex (complex)
이 타입은 복소수를 머신 레벨 배정밀도 부동소수점 숫자 쌍으로 나타내. 부동소수점 숫자와 같은 주의사항이 적용돼. 복소수 z의 실수부와 허수부는 읽기 전용 속성 z.real과 z.imag를 통해 얻을 수 있어.
시퀀스 (Sequences)
이 타입은 음수가 아닌 숫자로 인덱싱되는 유한한 순서가 있는 집합을 나타내. 내장 함수 len()은 시퀀스의 항목 수를 반환해. 시퀀스의 길이가 n이면 인덱스 집합은 0, 1, …, n-1을 담아. 시퀀스 a의 항목 i는 a[i]로 선택돼. 내장 시퀀스를 포함한 일부 시퀀스는 음수 첨자를 해석할 때 시퀀스의 길이를 더해. 예를 들어 a[-2]는 a[n-2]와 같고, 길이 n인 시퀀스 a의 뒤에서 두 번째 항목이야.
결과 값은 시퀀스의 항목 수보다 작은 음이 아닌 정수여야 해. 그렇지 않으면 IndexError가 발생해.
시퀀스는 슬라이싱도 지원해. a[start:stop]는 start <= k < stop인 인덱스 k를 가진 모든 항목을 선택해. 표현식으로 쓰일 때 슬라이스는 같은 타입의 시퀀스야. 음수 첨자에 대한 위의 설명은 음수 슬라이스 위치에도 적용돼. 슬라이스 위치가 0보다 작거나 시퀀스의 길이보다 커도 오류가 발생하지 않는다는 점을 참고해.
start가 없거나 None이면 슬라이싱은 start가 0인 것처럼 동작해. stop이 없거나 None이면 슬라이싱은 stop이 시퀀스의 길이와 같은 것처럼 동작해.
일부 시퀀스는 세 번째 "step" 매개변수를 사용하는 "확장 슬라이싱(extended slicing)"도 지원해. a[i:j:k]는 x = i + n*k, n >= 0, i <= x < j인 인덱스 x를 가진 a의 모든 항목을 선택해.
시퀀스는 가변성에 따라 구분돼:
불변 시퀀스 (Immutable sequences)
불변 시퀀스 타입의 객체는 생성된 뒤에 바꿀 수 없어. (객체가 다른 객체에 대한 참조를 담고 있다면 그 다른 객체는 가변일 수 있고 바뀔 수는 있어. 하지만 불변 객체가 직접 참조하는 객체들의 집합은 바뀔 수 없어.)
다음 타입들은 불변 시퀀스야:
문자열
문자열(str)은 문자(characters), 더 형식적으로는 *유니코드 코드 포인트(Unicode code points)*를 나타내는 값들의 시퀀스야. 0부터 0x10FFFF 범위의 모든 코드 포인트를 문자열로 표현할 수 있어. 파이썬에는 전용 문자 타입이 없어. 대신 문자열의 모든 코드 포인트가 길이 1인 문자열 객체로 표현돼. 내장 함수 ord()는 코드 포인트를 문자열 형태에서 00x10FFFF 범위의 정수로 변환하고, chr()는 00x10FFFF 범위의 정수를 해당 길이 1 문자열 객체로 변환해. str.encode()는 주어진 텍스트 인코딩을 사용해 str을 bytes로 변환하고, bytes.decode()는 그 반대를 달성할 수 있어.
튜플
tuple의 항목은 임의의 파이썬 객체야. 두 개 이상의 항목을 가진 튜플은 쉼표로 구분된 표현식 목록으로 형성돼. 한 항목을 가진 튜플('싱글턴')은 표현식에 쉼표 하나를 붙여서 만들 수 있어(괄호는 표현식의 그룹화에 사용될 수 있어야 하므로 표현식 하나만으로는 튜플을 만들지 못해). 빈 튜플은 빈 괄호 쌍으로 만들 수 있어.
바이트
bytes 객체는 불변 배열이야. 항목은 8비트 바이트로, 0 <= x < 256 범위의 정수로 표현돼. 바이트 리터럴(b'abc'처럼)과 내장 bytes() 생성자로 바이트 객체를 만들 수 있어. 또한 바이트 객체는 decode() 메서드로 문자열로 디코딩될 수 있어.
가변 시퀀스 (Mutable sequences)
가변 시퀀스는 생성된 뒤에 바꿀 수 있어. 첨자(subscription) 및 슬라이싱 표기법을 할당과 del(삭제) 문의 대상으로 사용할 수 있어.
참고
collections 모듈과 array 모듈은 가변 시퀀스 타입의 추가 예를 제공해.
현재 두 가지 고유한(intrinsic) 가변 시퀀스 타입이 있어:
리스트
리스트의 항목은 임의의 파이썬 객체야. 리스트는 대괄호 안에 쉼표로 구분된 표현식 목록을 배치하여 형성돼. (길이 0 또는 1의 리스트를 형성하는 데 특별한 경우가 필요 없다는 점을 참고해.)
바이트 배열
bytearray 객체는 가변 배열이야. 내장 bytearray() 생성자로 생성돼. 가변(따라서 해시 불가능)이라는 점을 제외하면, 바이트 배열은 불변 bytes 객체와 동일한 인터페이스와 기능을 제공해.
집합 타입 (Set types)
이 타입은 고유한 불변 객체들의 순서가 없는 유한 집합을 나타내. 그렇기에 어떤 첨자로도 인덱싱할 수 없어. 하지만 반복할 수 있고, 내장 함수 len()은 집합의 항목 수를 반환해. 집합의 일반적인 용도는 빠른 멤버십 테스트, 시퀀스에서 중복 제거, 그리고 교집합·합집합·차집합·대칭차집합 같은 수학적 연산 계산이야.
집합 요소에는 딕셔너리 키와 동일한 불변성 규칙이 적용돼. 숫자 타입은 숫자 비교에 대한 일반적인 규칙을 따르므로, 두 숫자가 같게 비교되면(예: 1과 1.0) 둘 중 하나만 집합에 담길 수 있다는 점을 참고해.
현재 두 가지 고유한 집합 타입이 있어:
집합
이 타입은 가변 집합을 나타내. 내장 set() 생성자로 생성되고, 나중에 add() 같은 여러 메서드로 수정할 수 있어.
프로즌셋
이 타입은 불변 집합을 나타내. 내장 frozenset() 생성자로 생성돼. frozenset은 불변이고 해시 가능하므로, 다른 집합의 요소나 딕셔너리 키로 다시 사용될 수 있어.
매핑 (Mappings)
이 타입은 임의 인덱스 집합으로 인덱싱되는 객체들의 유한 집합을 나타내. 첨자 표기법 a[k]는 매핑 a에서 k로 인덱싱된 항목을 선택해. 이것은 표현식에서 사용될 수 있고 할당이나 del 문의 대상으로 사용될 수 있어. 내장 함수 len()은 매핑의 항목 수를 반환해.
현재 단일 고유 매핑 타입이 있어:
딕셔너리 (Dictionaries)
이 타입은 거의 임의의 값으로 인덱싱되는 객체들의 유한 집합을 나타내. 키로 허용되지 않는 유일한 값 타입은 리스트, 딕셔너리 또는 값으로 비교되는(객체 정체성이 아닌) 다른 가변 타입을 포함하는 값들이야. 그 이유는 딕셔너리의 효율적인 구현이 키의 해시 값이 일정하게 유지되도록 요구하기 때문이야. 키에 사용되는 숫자 타입은 숫자 비교에 대한 일반적인 규칙을 따르므로, 두 숫자가 같게 비교되면(예: 1과 1.0) 같은 딕셔너리 항목을 인덱싱하는 데 서로 바꿔 사용할 수 있어.
딕셔너리는 삽입 순서를 보존해. 즉 키는 딕셔너리에 순차적으로 추가된 것과 같은 순서로 생성돼. 기존 키를 교체해도 순서는 바뀌지 않지만, 키를 제거하고 다시 삽입하면 이전 위치 대신 끝에 추가돼.
딕셔너리는 가변이며, {} 표기법으로 생성할 수 있어(딕셔너리 표시 섹션 참고).
확장 모듈 dbm.ndbm과 dbm.gnu는 매핑 타입의 추가 예를 제공하며, collections 모듈도 그래.
버전 3.7에서 변경: 파이썬 3.6 이전 버전에서는 딕셔너리가 삽입 순서를 보존하지 않았어. CPython 3.6에서는 삽입 순서가 보존됐지만, 당시에는 언어 보장이 아닌 구현 세부사항으로 간주됐어.
호출 가능 타입 (Callable types)
함수 호출 연산(Calls 섹션 참고)을 적용할 수 있는 타입들이야:
사용자 정의 함수 (User-defined functions)
사용자 정의 함수 객체는 함수 정의(함수 정의 섹션 참고)로 생성돼. 함수의 형식적 매개변수 목록과 같은 수의 항목을 가진 인자 목록으로 호출해야 해.
특수 읽기 전용 속성 (Special read-only attributes)
| 속성 | 의미 |
|---|---|
function.__builtins__ |
함수의 내장 네임스페이스(builtins namespace)를 담고 있는 dictionary에 대한 참조. 버전 3.10에서 추가됨. |
function.__globals__ |
함수의 전역 변수를 담고 있는 dictionary에 대한 참조. 즉 함수가 정의된 모듈의 전역 네임스페이스. |
function.__closure__ |
함수의 code object의 co_freevars 속성에 지정된 이름에 대한 바인딩을 담은 셀(cell)들의 tuple 또는 None. 셀 객체는 cell_contents 속성을 가져. 이걸로 셀의 값을 가져오거나 설정할 수 있어. |
특수 쓰기 가능 속성 (Special writable attributes)
이 속성들 대부분은 할당된 값의 타입을 검사해:
| 속성 | 의미 |
|---|---|
function.__doc__ |
함수의 문서 문자열, 또는 사용할 수 없으면 None. |
function.__name__ |
함수의 이름. 참고: __name__ attributes. |
function.__qualname__ |
함수의 정규화된 이름. 참고: __qualname__ attributes. 버전 3.3에서 추가됨. |
function.__module__ |
함수가 정의된 모듈의 이름, 또는 사용할 수 없으면 None. |
function.__defaults__ |
기본값이 있는 매개변수들에 대한 기본 매개변수 값을 담은 tuple, 또는 기본값을 가진 매개변수가 없으면 None. |
function.__code__ |
컴파일된 함수 본문을 나타내는 code object. |
function.__dict__ |
임의의 함수 속성을 지원하는 네임스페이스. 참고: __dict__ attributes. |
function.__annotations__ |
매개변수들의 애너테이션을 담은 dictionary. 딕셔너리의 키는 매개변수 이름이고, 반환 애너테이션이 제공되면 'return'이야. 참고: object.__annotations__. 버전 3.14에서 변경: 애너테이션은 이제 지연 평가(lazily evaluated)됨. PEP 649 참고. |
function.__annotate__ |
이 함수의 애너테이트 함수, 또는 함수에 애너테이션이 없으면 None. object.__annotate__ 참고. 버전 3.14에서 추가됨. |
function.__kwdefaults__ |
키워드 전용 매개변수들의 기본값을 담은 dictionary. |
function.__type_params__ |
제네릭 함수의 타입 매개변수를 담은 tuple. 버전 3.12에서 추가됨. |
함수 객체는 임의의 속성을 가져오고 설정하는 것도 지원해. 예를 들어 함수에 메타데이터를 붙이는 데 쓸 수 있지. 그런 속성을 가져오고 설정하는 데는 일반적인 속성 점 표기법(dot-notation)을 사용해.
CPython 구현 세부사항: CPython의 현재 구현은 사용자 정의 함수에서만 함수 속성을 지원해. 내장 함수의 함수 속성은 미래에 지원될 수 있어.
함수 정의에 대한 추가 정보는 (__code__ 속성으로 접근할 수 있는) code object에서 얻을 수 있어.
인스턴스 메서드 (Instance methods)
인스턴스 메서드 객체는 클래스, 클래스 인스턴스, 그리고 임의의 호출 가능 객체(보통 사용자 정의 함수)를 결합해.
특수 읽기 전용 속성:
| 속성 | 의미 |
|---|---|
method.__self__ |
메서드가 바인딩된 클래스 인스턴스 객체를 가리켜. |
method.__func__ |
원래의 함수 객체를 가리켜. |
method.__doc__ |
메서드의 문서(원래 함수의 method.__func__.__doc__와 같음). 원래 함수에 docstring이 있으면 string, 그렇지 않으면 None. |
method.__name__ |
메서드의 이름(method.__func__.__name__와 같음). |
method.__module__ |
메서드가 정의된 모듈의 이름, 또는 사용할 수 없으면 None. |
메서드는 기반 함수 객체의 임의 함수 속성에 대한 접근(설정은 아님)도 지원해.
클래스의 속성(아마도 그 클래스의 인스턴스를 통해)을 가져올 때, 그 속성이 사용자 정의 함수 객체나 classmethod 객체라면 사용자 정의 메서드 객체가 생성될 수 있어.
인스턴스 중 하나를 통해 클래스에서 사용자 정의 함수 객체를 가져와서 인스턴스 메서드 객체가 생성되면, 그 __self__ 속성은 인스턴스가 되고 메서드 객체는 *바인딩(bound)*되었다고 말해. 새 메서드의 __func__ 속성은 원래 함수 객체야.
클래스나 인스턴스에서 classmethod 객체를 가져와서 인스턴스 메서드 객체가 생성되면, 그 __self__ 속성은 클래스 자체가 되고, __func__ 속성은 클래스 메서드의 기반이 되는 함수 객체야.
인스턴스 메서드 객체가 호출되면, 기반 함수(__func__)가 호출되는데, 이때 클래스 인스턴스(__self__)가 인자 목록의 앞에 삽입돼. 예를 들어 C가 함수 f()의 정의를 담고 있는 클래스이고 x가 C의 인스턴스일 때, x.f(1)을 호출하는 것은 C.f(x, 1)을 호출하는 것과 같아.
인스턴스 메서드 객체가 classmethod 객체에서 파생되면, __self__에 저장된 "클래스 인스턴스"는 실제로 클래스 자체가 돼. 그래서 x.f(1)이나 C.f(1) 중 어느 것을 호출해도 기반 함수가 f일 때 f(C,1)을 호출하는 것과 같아.
중요한 점은, 클래스 인스턴스의 속성인 사용자 정의 함수는 바인딩 메서드로 변환되지 않는다는 거야. 이 변환은 오직 함수가 클래스의 속성일 때만 일어나.
제너레이터 함수 (Generator functions)
yield 표현식(Yield expressions 섹션 참고)을 포함하는 함수나 메서드를 제너레이터 함수라고 불러. 그런 함수는 호출되면 항상 함수의 본문을 실행하는 데 사용할 수 있는 이터레이터 객체를 반환해. 이터레이터의 iterator.__next__() 메서드를 호출하면 함수가 yield 표현식으로 값을 제공할 때까지 실행돼. 함수가 return 문을 실행하거나 끝까지 실행되면 StopIteration 예외가 발생하고, 이터레이터는 반환할 값 집합의 끝에 도달한 것이야.
코루틴 함수 (Coroutine functions)
async def로 정의된 함수나 메서드를 코루틴 함수라고 불러. 그런 함수는 호출되면 코루틴 객체를 반환해. await 표현식뿐만 아니라 async with와 async for 문도 포함할 수 있어. 코루틴 객체 섹션도 참고해.
비동기 제너레이터 함수 (Asynchronous generator functions)
async def로 정의되고 yield 표현식을 포함하는 함수나 메서드를 비동기 제너레이터 함수라고 불러. 그런 함수는 호출되면 함수의 본문을 실행하기 위해 async for 문에서 사용할 수 있는 비동기 이터레이터 객체를 반환해.
비동기 이터레이터의 aiterator.__anext__ 메서드를 호출하면 awaitable을 반환하는데, 이를 await하면 yield 표현식으로 값을 제공할 때까지 실행돼. 함수가 빈 return 문을 실행하거나 끝까지 실행되면 StopAsyncIteration 예외가 발생하고, 비동기 이터레이터는 yield할 값 집합의 끝에 도달한 것이야.
내장 함수 (Built-in functions)
내장 함수 객체는 C 함수를 감싼 래퍼(wrapper)야. 내장 함수의 예로 len()과 math.sin()(math는 표준 내장 모듈)이 있어. 인자의 수와 타입은 C 함수가 결정해. 특수 읽기 전용 속성:
__doc__는 함수의 문서 문자열, 또는 사용할 수 없으면None.function.__doc__참고.__name__은 함수의 이름.function.__name__참고.__self__는None으로 설정돼(하지만 다음 항목 참고).__module__은 함수가 정의된 모듈의 이름, 또는 사용할 수 없으면None.function.__module__참고.
내장 메서드 (Built-in methods)
이것은 사실상 내장 함수의 다른 모습이야. 이번에는 C 함수에 암시적 추가 인자로 전달되는 객체를 담고 있어. 내장 메서드의 예로 alist가 리스트 객체라고 가정할 때 alist.append()가 있어. 이 경우 특수 읽기 전용 속성 __self__는 alist가 가리키는 객체로 설정돼. (속성은 다른 인스턴스 메서드와 동일한 의미론을 가져.)
클래스 (Classes)
클래스는 호출 가능해. 이 객체들은 보통 자기 자신의 새 인스턴스를 위한 팩토리(factory)로 동작하지만, __new__()를 오버라이드하는 클래스 타입에서는 변형이 가능해. 호출의 인자는 __new__()에 전달되고, 전형적인 경우에는 새 인스턴스를 초기화하기 위해 __init__()에도 전달돼.
클래스 인스턴스 (Class Instances)
임의 클래스의 인스턴스는 그 클래스에 __call__() 메서드를 정의하면 호출 가능하게 만들 수 있어.
모듈 (Modules)
모듈은 파이썬 코드의 기본적인 구성 단위이며, import 문으로 호출되든 importlib.import_module() 같은 함수와 내장 __import__()를 호출하든 가져오기 시스템에 의해 생성돼. 모듈 객체는 dictionary 객체로 구현된 네임스페이스를 가져(이 딕셔너리는 모듈에 정의된 함수의 __globals__ 속성이 참조하는 바로 그 딕셔너리야). 속성 참조는 이 딕셔너리에서의 조회로 변환돼. 예를 들어 m.x는 m.__dict__["x"]와 같아. 모듈 객체는 모듈을 초기화하는 데 사용된 code object는 담고 있지 않아(초기화가 끝나면 필요 없기 때문이야).
속성 할당은 모듈의 네임스페이스 딕셔너리를 갱신해. 예를 들어 m.x = 1은 m.__dict__["x"] = 1과 같아.
모듈 객체의 가져오기 관련 속성 (Import-related attributes on module objects)
모듈 객체는 가져오기 시스템과 관련된 다음 속성들을 가져. 모듈이 가져오기 시스템과 연결된 메커니즘으로 생성되면, loader가 모듈을 실행하고 로드하기 전에 모듈의 spec을 기반으로 이 속성들이 채워져.
가져오기 시스템 대신 모듈을 동적으로 생성하려면, 다양한 가져오기 제어 속성을 적절한 값으로 설정해 줄 importlib.util.module_from_spec()을 사용하는 걸 권장해. types.ModuleType 생성자로 모듈을 직접 생성하는 것도 가능하지만, 이 방법은 대부분의 속성을 모듈 생성 후 수동으로 설정해야 하므로 오류가 발생하기 더 쉬워.
주의
__name__을 제외하고, 이 하위 섹션에 나열된 다른 개별 속성 대신 __spec__과 그 속성들에 의존하는 것을 강력히 권장해. __spec__의 속성을 갱신해도 모듈 자체의 해당 속성은 갱신되지 않는다는 점을 참고해:
>>> import typing
>>> typing.__name__, typing.__spec__.name
('typing', 'typing')
>>> typing.__spec__.name = 'spelling'
>>> typing.__name__, typing.__spec__.name
('typing', 'spelling')
>>> typing.__name__ = 'keyboard_smashing'
>>> typing.__name__, typing.__spec__.name
('keyboard_smashing', 'spelling')
module.__name__
가져오기 시스템에서 모듈을 고유하게 식별하는 데 사용되는 이름. 직접 실행된 모듈은 "__main__"으로 설정돼. 이 속성은 모듈의 정규화된 이름(full qualified name)으로 설정해야 해. module.__spec__.name의 값과 일치할 것으로 기대돼.
module.__spec__
모듈의 가져오기 시스템 관련 상태 기록. 모듈을 가져올 때 사용된 module spec으로 설정돼. 자세한 내용은 Module specs 참고. 버전 3.4에서 추가됨.
module.__package__
모듈이 속한 패키지. 모듈이 최상위(즉 어떤 특정 패키지의 일부도 아닌)라면 속성은 ''(빈 문자열)로 설정해야 해. 그렇지 않으면 모듈의 패키지 이름으로 설정해야 해(모듈 자체가 패키지라면 module.__name__과 같을 수 있어). 자세한 내용은 PEP 366 참고. 이 속성은 main 모듈의 명시적 상대 가져오기를 계산할 때 __name__ 대신 사용돼. types.ModuleType 생성자로 동적으로 생성된 모듈에서는 기본값 None이야. 속성이 str로 설정되도록 하려면 importlib.util.module_from_spec()를 사용해. module.__package__ 대신 module.__spec__.parent를 사용하는 것을 강력히 권장해. __package__는 이제 __spec__.parent가 설정되지 않았을 때의 폴백으로만 사용되며, 이 폴백 경로는 deprecated야.
버전 3.4에서 변경: 이 속성은 types.ModuleType 생성자로 동적으로 생성된 모듈에서 이제 기본값 None이야. 이전에는 선택적 속성이었어.
버전 3.6에서 변경: __package__의 값은 __spec__.parent와 같을 것으로 기대돼. __package__는 이제 가져오기 해석 중 __spec__.parent가 정의되지 않았을 때의 폴백으로만 사용돼.
버전 3.10에서 변경: 가져오기 해석이 __spec__.parent 대신 __package__로 폴백하면 ImportWarning이 발생해.
버전 3.12에서 변경: 가져오기 해석 중 __package__로 폴백할 때 ImportWarning 대신 DeprecationWarning이 발생해.
버전 3.13부터 deprecated, 버전 3.15에서 제거 예정: __package__는 가져오기 시스템이나 표준 라이브러리에서 더 이상 설정되거나 고려되지 않을 거야.
module.__loader__
가져오기 메커니즘이 모듈을 로드하는 데 사용한 loader 객체. 이 속성은 주로 인트로스펙션에 유용하지만, 예를 들어 loader와 연결된 데이터를 가져오는 것 같은 추가 loader 특화 기능에도 사용될 수 있어. types.ModuleType 생성자로 동적으로 생성된 모듈에서는 __loader__ 기본값이 None이야. 속성이 loader 객체로 설정되도록 하려면 importlib.util.module_from_spec()를 사용해. module.__loader__ 대신 module.__spec__.loader를 사용하는 것을 강력히 권장해.
버전 3.4에서 변경: 이 속성은 types.ModuleType 생성자로 동적으로 생성된 모듈에서 이제 기본값 None이야. 이전에는 선택적 속성이었어.
버전 3.12부터 deprecated, 버전 3.16에서 제거 예정: 모듈에 __loader__를 설정하면서
그리고 __spec__.loader도 설정하지 않으면 deprecated야. 파이썬 3.16에서 __loader__는 가져오기 시스템이나 표준 라이브러리에서 더 이상 설정되거나 고려되지 않을 거야.
module.__path__
패키지의 하위 모듈이 위치하는 곳을 나열하는 (아마도 빈) 문자열 sequence. 패키지가 아닌 모듈은 __path__ 속성을 가지면 안 돼. 자세한 내용은 모듈의 path 속성 참고. module.__path__ 대신 module.__spec__.submodule_search_locations를 사용하는 것을 강력히 권장해.
module.__file__, module.__cached__
__file__과 __cached__는 모두 설정될 수도 있고 아닐 수도 있는 선택적 속성이야. 두 속성 모두 사용 가능하면 str이어야 해. __file__은 모듈이 로드된 파일의 경로명(파일에서 로드된 경우), 또는 공유 라이브러리에서 동적으로 로드된 확장 모듈의 공유 라이브러리 파일 경로명을 나타내. 인터프리터에 정적으로 링크된 C 모듈 같은 특정 유형의 모듈에서는 없을 수 있고, 가져오기 시스템은 의미론적 의미가 없으면(예: 데이터베이스에서 로드된 모듈) 설정을 생략할 수 있어. __file__이 설정되면 __cached__ 속성도 설정될 수 있는데, 이는 코드의 컴파일된 버전(예: 바이트 컴파일된 파일)의 경로야. 이 속성을 설정하는 데 파일이 존재할 필요는 없어. 경로는 컴파일된 파일이 있을 위치를 가리키기만 하면 돼(PEP 3147 참고). __file__이 설정되지 않아도 __cached__는 설정될 수 있다는 점을 참고해. 다만 그 시나리오는 꽤 비전형적이야. 궁극적으로 loader가 (__file__과 __cached__가 파생되는) finder가 제공하는 모듈 spec을 사용하는 것이야. 그래서 loader가 캐시된 모듈에서는 로드할 수 있지만 그 외에는 파일에서 로드하지 않는다면, 그 비전형적 시나리오가 적절할 수 있어. module.__cached__ 대신 module.__spec__.cached를 사용하는 것을 강력히 권장해.
버전 3.13부터 deprecated, 버전 3.15에서 제거 예정: 모듈에 __cached__를 설정하면서 __spec__.cached도 설정하지 않으면 deprecated야. 파이썬 3.15에서 __cached__는 가져오기 시스템이나 표준 라이브러리에서 더 이상 설정되거나 고려되지 않을 거야.
모듈 객체의 기타 쓰기 가능 속성 (Other writable attributes on module objects)
위에 나열된 가져오기 관련 속성 외에도 모듈 객체는 다음 쓰기 가능 속성들을 가져:
module.__doc__
모듈의 문서 문자열, 또는 사용할 수 없으면 None. 참고: __doc__ attributes.
module.__annotations__
모듈 본문 실행 중 수집된 변수 애너테이션을 담고 있는 딕셔너리. __annotations__를 다루는 모범 사례는 annotationlib를 참고해.
버전 3.14에서 변경: 애너테이션은 이제 지연 평가됨. PEP 649 참고.
module.__annotate__
이 모듈의 애너테이트 함수, 또는 모듈에 애너테이션이 없으면 None. 참고: __annotate__ 속성. 버전 3.14에서 추가됨.
모듈 딕셔너리 (Module dictionaries)
모듈 객체는 다음 특수 읽기 전용 속성도 가져:
module.__dict__
딕셔너리 객체로서의 모듈의 네임스페이스. 여기에 나열된 속성들 중 유일하게 __dict__는 모듈 내부에서 전역 변수로 접근할 수 없고, 모듈 객체의 속성으로만 접근할 수 있어.
CPython 구현 세부사항: CPython이 모듈 딕셔너리를 지우는 방식 때문에, 딕셔너리에 아직 살아있는 참조가 있어도 모듈이 범위를 벗어나면 모듈 딕셔너리는 지워져. 이를 피하려면 딕셔너리를 복사하거나, 그 딕셔너리를 직접 사용하는 동안 모듈을 유지해.
사용자 정의 클래스 (Custom classes)
사용자 정의 클래스 타입은 보통 클래스 정의(Class definitions 섹션 참고)로 생성돼. 클래스는 딕셔너리 객체로 구현된 네임스페이스를 가져. 클래스 속성 참조는 이 딕셔너리에서의 조회로 변환돼. 예를 들어 C.x는 C.__dict__["x"]로 변환돼(속성을 찾는 다른 수단을 허용하는 많은 훅이 있긴 해). 속성 이름이 거기서 발견되지 않으면 속성 검색은 기본 클래스들로 계속돼. 이 기본 클래스 검색은 C3 메서드 결정 순서(method resolution order, MRO)를 사용하며, 이는 공통 조상으로 돌아가는 여러 상속 경로가 있는 '다이아몬드' 상속 구조에서도 올바르게 동작해. 파이썬이 사용하는 C3 MRO에 대한 추가 세부사항은 The Python 2.3 Method Resolution Order에서 찾을 수 있어.
클래스 속성 참조(예를 들어 클래스 C에 대한)가 클래스 메서드 객체를 산출한다면, 그 참조는 __self__ 속성이 C인 인스턴스 메서드 객체로 변환돼. 그것이 staticmethod 객체를 산출한다면, 정적 메서드 객체로 감싸진 객체로 변환돼. 클래스에서 가져온 속성이 실제로 그 __dict__에 담긴 것과 다를 수 있는 또 다른 방식은 Descriptor 구현하기 섹션을 참고해.
클래스 속성 할당은 클래스의 딕셔너리를 갱신하지, 기본 클래스의 딕셔너리를 절대 갱신하지 않아.
클래스 객체는 호출되어(위 참고) 클래스 인스턴스를 산출할 수 있어(아래 참고).
특수 속성 (Special attributes)
| 속성 | 의미 |
|---|---|
type.__name__ |
클래스의 이름. 참고: __name__ attributes. |
type.__qualname__ |
클래스의 정규화된 이름. 참고: __qualname__ attributes. |
type.__module__ |
클래스가 정의된 모듈의 이름. |
type.__dict__ |
클래스의 네임스페이스에 대한 읽기 전용 뷰를 제공하는 mapping proxy. 참고: __dict__ attributes. |
type.__bases__ |
클래스의 기반(base)들을 담고 있는 tuple. 대부분의 경우 class X(A, B, C)로 정의된 클래스에 대해 X.__bases__는 정확히 (A, B, C)와 같아. |
type.__base__ |
CPython 구현 세부사항: 인스턴스의 메모리 레이아웃을 담당하는 상속 체인의 단일 기본 클래스. 이 속성은 C 레벨의 tp_base에 해당해. |
type.__doc__ |
클래스의 문서 문자열, 또는 정의되지 않았으면 None. 하위 클래스에 상속되지 않아. |
type.__annotations__ |
클래스 본문 실행 중 수집된 변수 애너테이션을 담고 있는 딕셔너리. 참고: __annotations__ attributes. __annotations__를 다루는 모범 사례는 annotationlib를 참고해. 이 속성에 직접 접근하는 대신 annotationlib.get_annotations()를 사용해. 경고: 클래스 객체의 __annotations__ 속성에 직접 접근하면 특정 경우, 특히 클래스나 그 기본 클래스 또는 메타클래스가 from __future__ import annotations 아래에서 정의된 경우 잘못된 클래스의 애너테이션을 반환할 수 있어. 자세한 내용은 PEP 749 참고. 이 속성은 특정 내장 클래스에는 존재하지 않아. __annotations__가 없는 사용자 정의 클래스에서는 빈 딕셔너리야. 버전 3.14에서 변경: 애너테이션은 이제 지연 평가됨. PEP 649 참고. |
type.__annotate__() |
이 클래스의 애너테이트 함수, 또는 클래스에 애너테이션이 없으면 None. 참고: __annotate__ attributes. 버전 3.14에서 추가됨. |
type.__type_params__ |
제네릭 클래스의 타입 매개변수를 담고 있는 tuple. 버전 3.12에서 추가됨. |
type.__static_attributes__ |
본문의 어떤 함수에서 self.X를 통해 할당되는 이 클래스 속성들의 이름을 담고 있는 tuple. 버전 3.13에서 추가됨. |
type.__firstlineno__ |
데코레이터를 포함한 클래스 정의의 첫 줄의 줄 번호. __module__ 속성을 설정하면 타입의 딕셔너리에서 __firstlineno__ 항목이 제거돼. 버전 3.13에서 추가됨. |
type.__mro__ |
메서드 결정 중 기본 클래스를 찾을 때 고려되는 클래스들의 tuple. |
특수 메서드 (Special methods)
위에서 설명한 특수 속성 외에도 모든 파이썬 클래스는 다음 두 메서드를 사용할 수 있어:
type.mro()
이 메서드는 메타클래스가 오버라이드하여 인스턴스의 메서드 결정 순서를 커스터마이즈할 수 있어. 클래스 인스턴스화 시 호출되고, 그 결과는 __mro__에 저장돼.
type.__subclasses__()
각 클래스는 직접 하위 클래스들에 대한 약한 참조(weak reference) 목록을 유지해. 이 메서드는 아직 살아있는 모든 참조의 목록을 반환해. 목록은 정의 순서야. 예:
>>> class A: pass
>>> class B(A): pass
>>> A.__subclasses__()
[<class 'B'>]
클래스 인스턴스 (Class instances)
클래스 인스턴스는 클래스 객체를 호출하여(위 참고) 생성돼. 클래스 인스턴스는 딕셔너리로 구현된 네임스페이스를 가지며, 속성 참조가 가장 먼저 검색되는 곳이야. 속성이 거기서 발견되지 않고 인스턴스의 클래스에 그 이름의 속성이 있다면, 검색은 클래스 속성으로 계속돼. 사용자 정의 함수 객체인 클래스 속성이 발견되면, 그 속성은 __self__ 속성이 인스턴스인 인스턴스 메서드 객체로 변환돼. 정적 메서드 및 클래스 메서드 객체도 변환돼. 위 "클래스" 부분 참고. 인스턴스를 통해 가져온 클래스의 속성이 실제로 클래스의 __dict__에 저장된 객체와 다를 수 있는 또 다른 방식은 Descriptor 구현하기 섹션을 참고해. 클래스 속성이 발견되지 않고 그 객체의 클래스에 __getattr__() 메서드가 있다면, 그것이 호출되어 조회를 충족시켜.
속성 할당과 삭제는 인스턴스의 딕셔너리를 갱신하지, 클래스의 딕셔너리를 절대 갱신하지 않아. 클래스에 __setattr__() 또는 __delattr__() 메서드가 있으면, 인스턴스 딕셔너리를 직접 갱신하는 대신 그것이 호출돼.
클래스 인스턴스는 특정 특수 이름을 가진 메서드를 가지면 숫자, 시퀀스, 매핑인 척할 수 있어. 특수 메서드 이름 섹션 참고.
특수 속성 (Special attributes)
object.__class__
클래스 인스턴스가 속한 클래스.
object.__dict__
객체의 (쓰기 가능한) 속성을 저장하는 데 사용되는 딕셔너리 또는 다른 매핑 객체. 모든 인스턴스가 __dict__ 속성을 가지는 건 아냐. 자세한 내용은 slots 섹션 참고.
I/O 객체 (파일 객체라고도 함)
파일 객체는 열린 파일을 나타내. 파일 객체를 만드는 다양한 지름길이 있어. open() 내장 함수, 그리고 os.popen(), os.fdopen(), 소켓 객체의 makefile() 메서드(그리고 확장 모듈이 제공하는 다른 함수나 메서드)가 그것이야.
파일 객체는 아래에 나열된 공통 메서드를 구현해 일반 코드에서의 사용을 단순화해. 그것들은 With 문 컨텍스트 관리자가 될 것으로 기대돼.
sys.stdin, sys.stdout, sys.stderr 객체는 인터프리터의 표준 입력·출력·오류 스트림에 해당하는 파일 객체로 초기화돼. 이들은 모두 텍스트 모드로 열리므로 io.TextIOBase 추상 클래스가 정의한 인터페이스를 따르지.
file.read(size=-1, /)
파일에서 최대 size 데이터를 검색해. size가 지정되지 않거나 -1이면 사용 가능한 모든 데이터를 검색하는 편의를 제공해.
file.write(data, /)
data를 파일에 저장해.
file.close()
버퍼를 모두 flush하고 기반 파일을 닫아.
내부 타입 (Internal types)
인터프리터가 내부적으로 사용하는 몇 가지 타입이 사용자에게 노출돼. 그 정의는 인터프리터의 미래 버전에서 바뀔 수 있지만, 완전성을 위해 여기에 언급해.
코드 객체 (Code objects)
Code object는 바이트 컴파일된(byte-compiled) 실행 가능한 파이썬 코드, 즉 바이트코드를 나타내. 코드 객체와 함수 객체의 차이는, 함수 객체는 함수의 전역(정의된 모듈)에 대한 명시적 참조를 가지지만 코드 객체는 어떤 컨텍스트도 가지지 않는다는 점이야. 또한 기본 인자 값은 코드 객체가 아니라 함수 객체에 저장돼(런타임에 계산된 값을 나타내기 때문이야). 함수 객체와 달리 코드 객체는 불변이고 (직접적 또는 간접적으로) 가변 객체에 대한 참조를 일절 담지 않아.
특수 읽기 전용 속성 (Special read-only attributes)
| 속성 | 의미 |
|---|---|
codeobject.co_name |
함수 이름 |
codeobject.co_qualname |
완전히 정규화된 함수 이름. 버전 3.11에서 추가됨. |
codeobject.co_argcount |
함수가 가진 위치 매개변수(positional-only 매개변수와 기본값이 있는 매개변수 포함)의 총 수 |
codeobject.co_posonlyargcount |
함수가 가진 positional-only 매개변수(기본값이 있는 인자 포함)의 수 |
codeobject.co_kwonlyargcount |
함수가 가진 keyword-only 매개변수(기본값이 있는 인자 포함)의 수 |
codeobject.co_nlocals |
함수가 사용하는 지역 변수(매개변수 포함)의 수 |
codeobject.co_varnames |
함수의 지역 변수 이름(매개변수 이름으로 시작)을 담고 있는 tuple |
| codeobject.co_cellvars | 함수 내부의 중첩 스코프 중 적어도 하나에서 참조되는 지역 변수 이름을 담고 있는 tuple |
| codeobject.co_freevars | 중첩 스코프가 바깥 스코프에서 참조하는 자유(클로저) 변수 이름을 담고 있는 tuple. function.__closure__ 참고. 전역 및 내장 이름에 대한 참조는 포함되지 않아. |
| codeobject.co_code | 함수의 바이트코드 명령어 시퀀스를 나타내는 문자열 |
| codeobject.co_consts | 함수의 바이트코드가 사용하는 리터럴을 담고 있는 tuple |
| codeobject.co_names | 함수의 바이트코드가 사용하는 이름을 담고 있는 tuple |
| codeobject.co_filename | 코드가 컴파일된 파일의 이름 |
| codeobject.co_firstlineno | 함수의 첫 줄의 줄 번호 |
| codeobject.co_lnotab | 바이트코드 오프셋에서 줄 번호로의 매핑을 인코딩하는 문자열. 자세한 내용은 인터프리터의 소스 코드를 참고해. 버전 3.12부터 deprecated, 파이썬 3.15에서 제거될 수 있음. |
| codeobject.co_stacksize | 코드 객체의 요구 스택 크기 |
| codeobject.co_flags | 인터프리터를 위한 여러 플래그를 인코딩한 integer. |
co_flags에 대해 다음 플래그 비트가 정의돼: 함수가 임의 개수의 위치 인자를 받기 위해 *arguments 문법을 사용하면 비트 0x04가 설정되고, 임의 개수의 키워드 인자를 받기 위해 **keywords 문법을 사용하면 비트 0x08이 설정되며, 함수가 제너레이터이면 비트 0x20이 설정돼. 존재할 수 있는 각 플래그의 의미에 대한 자세한 내용은 Code Objects Bit Flags를 참고해.
미래 기능 선언(from __future__ import division 같은)도 co_flags의 비트를 사용해 코드 객체가 특정 기능을 활성화한 채 컴파일됐는지 나타내. compiler_flag 참고.
co_flags의 다른 비트는 내부 사용을 위해 예약돼 있어.
코드 객체가 함수를 나타내고 docstring이 있다면, co_flags에 CO_HAS_DOCSTRING 비트가 설정되고 co_consts의 첫 항목이 함수의 docstring이야.
코드 객체의 메서드 (Methods on code objects)
codeobject.co_positions()
코드 객체의 각 바이트코드 명령어의 소스 코드 위치에 대한 iterable을 반환해. 이터레이터는 (start_line, end_line, start_column, end_column)을 담고 있는 tuple을 반환해. i-번째 튜플은 i-번째 코드 단위로 컴파일된 소스 코드의 위치에 해당해. 열 정보는 주어진 소스 줄의 0-인덱스 utf-8 바이트 오프셋이야. 이 위치 정보는 없을 수 있어. 그럴 수 있는 경우의 (완전하지 않은) 목록:
- 인터프리터를
-Xno_debug_ranges로 실행하는 경우. -Xno_debug_ranges를 사용하면서 컴파일된 pyc 파일을 로드하는 경우.- 인공 명령어(artificial instructions)에 해당하는 위치 튜플.
- 구현 특정 한계로 표현할 수 없는 줄·열 번호.
이런 경우 일부 또는 모든 튜플 요소가 None일 수 있어.
버전 3.11에서 추가됨.
참고
이 기능은 코드 객체에 열 위치를 저장해야 하므로 컴파일된 파이썬 파일의 디스크 사용량이나 인터프리터 메모리 사용량이 약간 늘어날 수 있어. 추가 정보 저장을 피하고/또는 추가 traceback 정보 출력을 비활성화하려면 -X no_debug_ranges 명령줄 플래그 또는 PYTHONNODEBUGRANGES 환경 변수를 사용할 수 있어.
codeobject.co_lines()
연속된 바이트코드 범위에 대한 정보를 생성하는 이터레이터를 반환해. 생성되는 각 항목은 (start, end, lineno) tuple이야:
start(anint)는 바이트코드 범위의 시작(포함) 오프셋을 나타내.end(anint)는 바이트코드 범위의 끝(제외) 오프셋을 나타내.lineno는 바이트코드 범위의 줄 번호를 나타내는int, 또는 주어진 범위의 바이트코드에 줄 번호가 없으면None.
생성되는 항목은 다음 속성들을 가져:
- 생성되는 첫 번째 범위는
start가 0이야. (start, end)범위는 감소하지 않고 연속적이야. 즉 두tuple쌍에 대해 두 번째의start는 첫 번째의end와 같아.- 어떤 범위도 뒤로 가지 않아. 모든 트리플에 대해
end >= start야. - 생성되는 마지막
tuple은end가 바이트코드의 크기와 같아.
start == end인 너비 0 범위는 허용돼. 너비 0 범위는 소스 코드에 존재하지만 바이트코드 컴파일러에 의해 제거된 줄에 사용돼.
버전 3.10에서 추가됨.
참고: PEP 626 - 디버깅 및 기타 도구를 위한 정밀한 줄 번호. co_lines() 메서드를 도입한 PEP야.
codeobject.replace(**kwargs)
지정된 필드에 새 값을 적용한 코드 객체의 복사본을 반환해. 코드 객체는 제네릭 함수 copy.replace()도 지원해. 버전 3.8에서 추가됨.
프레임 객체 (Frame objects)
프레임 객체는 실행 프레임을 나타내. traceback 객체에 나타날 수 있고, 등록된 트레이스 함수에도 전달돼.
특수 읽기 전용 속성 (Special read-only attributes)
| 속성 | 의미 |
|---|---|
frame.f_back |
이전 스택 프레임(호출자 쪽으로)을 가리켜, 또는 이게 맨 아래 스택 프레임이면 None |
frame.f_code |
이 프레임에서 실행 중인 code object. 이 속성에 접근하면 인자 obj와 "f_code"를 가진 auditing event object.__getattr__이 발생해. |
frame.f_locals |
프레임이 지역 변수를 찾는 데 사용하는 매핑. 프레임이 최적화된 스코프를 가리키면 write-through 프록시 객체를 반환할 수 있어. 버전 3.13에서 변경: 최적화된 스코프에 대한 프록시를 반환. |
frame.f_globals |
프레임이 전역 변수를 찾는 데 사용하는 딕셔너리 |
frame.f_builtins |
프레임이 내장(고유) 이름을 찾는 데 사용하는 딕셔너리 |
frame.f_lasti |
프레임 객체의 "정밀 명령어"(이것은 code object의 바이트코드 문자열에 대한 인덱스) |
frame.f_generator |
이 프레임을 소유하는 제너레이터 또는 코루틴 객체, 또는 프레임이 일반 함수이면 None. 버전 3.14에서 추가됨. |
특수 쓰기 가능 속성 (Special writable attributes)
| 속성 | 의미 |
|---|---|
frame.f_trace |
None이 아니면 코드 실행 중 다양한 이벤트에 대해 호출되는 함수(디버거가 사용함). 보통 각 새 소스 줄마다 이벤트가 트리거돼(f_trace_lines 참고). |
frame.f_trace_lines |
각 소스 줄마다 트레이싱 이벤트를 트리거하는 것을 비활성화하려면 이 속성을 False로 설정해. |
frame.f_trace_opcodes |
per-opcode 이벤트를 요청하도록 허용하려면 이 속성을 True로 설정해. 트레이스 함수가 발생시킨 예외가 트레이스되는 함수로 탈출하면 정의되지 않은 인터프리터 동작이 발생할 수 있다는 점에 유의해. |
frame.f_lineno |
프레임의 현재 줄 번호. 트레이스 함수 내부에서 이 값을 쓰면 주어진 줄로 점프해(맨 아래 프레임에서만). 디버거는 이 속성에 쓰는 것으로 Jump 명령(일명 Set Next Statement)을 구현할 수 있어. |
프레임 객체 메서드 (Frame object methods)
프레임 객체는 하나의 메서드를 지원해:
frame.clear()
이 메서드는 프레임이 보유한 지역 변수에 대한 모든 참조를 지워. 또한 프레임이 제너레이터에 속했다면 제너레이터는 finalize돼. 이는 프레임 객체와 관련된 참조 순환을 끊는 데 도움이 돼(예: 예외를 잡고 그 traceback을 나중에 사용하기 위해 저장할 때). 프레임이 현재 실행 중이거나 일시 중지(suspended) 상태면 RuntimeError가 발생해. 버전 3.4에서 추가됨.
버전 3.13에서 변경: 일시 중지된 프레임을 지우려 시도하면 RuntimeError가 발생해(실행 중 프레임에 대해서는 항상 그랬음).
Traceback 객체 (Traceback objects)
Traceback 객체는 예외의 스택 추적(stack trace)을 나타내. traceback 객체는 예외가 발생할 때 암시적으로 생성되고, types.TracebackType를 호출하여 명시적으로 생성될 수도 있어.
버전 3.7에서 변경: traceback 객체를 이제 파이썬 코드에서 명시적으로 인스턴스화할 수 있어.
암시적으로 생성된 traceback의 경우, 예외 핸들러를 찾는 검색이 실행 스택을 풀어낼 때 각 풀어낸 레벨에서 traceback 객체가 현재 traceback의 앞에 삽입돼. 예외 핸들러에 진입하면 스택 추적이 프로그램에서 사용 가능해져. (try 문 섹션 참고.) sys.exc_info()가 반환하는 튜플의 세 번째 항목으로, 그리고 잡은 예외의 __traceback__ 속성으로 접근할 수 있어.
프로그램에 적절한 핸들러가 없으면 스택 추적은 (예쁘게 포맷되어) 표준 오류 스트림에 기록돼. 인터프리터가 대화형이면 sys.last_traceback으로 사용자에게도 제공돼.
명시적으로 생성된 traceback의 경우, 완전한 스택 추적을 형성하기 위해 tb_next 속성을 어떻게 연결할지 결정하는 것은 traceback 생성자에게 달려 있어.
특수 읽기 전용 속성:
| 속성 | 의미 |
|---|---|
traceback.tb_frame |
현재 레벨의 실행 프레임을 가리켜. 이 속성에 접근하면 인자 obj와 "tb_frame"을 가진 auditing event object.__getattr__이 발생해. |
traceback.tb_lineno |
예외가 발생한 줄 번호를 제공해 |
traceback.tb_lasti |
"정밀 명령어"를 나타내. |
예외가 일치하는 except 절이 없거나 finally 절이 있는 try 문에서 발생했다면, traceback의 줄 번호와 마지막 명령어가 그 프레임 객체의 줄 번호와 다를 수 있어.
traceback.tb_next
특수 쓰기 가능 속성 tb_next는 스택 추적의 다음 레벨(예외가 발생한 프레임 쪽으로)이야, 또는 다음 레벨이 없으면 None. 버전 3.7에서 변경: 이 속성은 이제 쓰기 가능.
슬라이스 객체 (Slice objects)
슬라이스 객체는 __getitem__() 메서드를 위한 슬라이스를 나타내는 데 사용돼. 내장 slice() 함수로도 생성돼.
특수 읽기 전용 속성: start는 하한, stop은 상한, step은 스텝 값이야. 각각 생략되면 None이야. 이 속성들은 어떤 타입이든 될 수 있어.
슬라이스 객체는 하나의 메서드를 지원해:
slice.indices(self, length)
이 메서드는 단일 정수 인자 length를 받아, 길이 length인 시퀀스에 적용했을 때 슬라이스 객체가 설명할 슬라이스에 대한 정보를 계산해. 세 개의 정수로 이루어진 튜플을 반환하는데, 각각 슬라이스의 start 및 stop 인덱스와 step(또는 보폭) 길이야. 없거나 범위를 벗어난 인덱스는 일반 슬라이스와 일관된 방식으로 처리돼.
정적 메서드 객체 (Static method objects)
정적 메서드 객체는 위에서 설명한 함수 객체가 메서드 객체로 변환되는 것을 막는 방법을 제공해. 정적 메서드 객체는 다른 어떤 객체, 보통 사용자 정의 메서드 객체를 감싼 래퍼야. 정적 메서드 객체가 클래스나 클래스 인스턴스에서 가져와지면, 실제로 반환되는 객체는 감싸진 객체이며 더 이상의 변환을 받지 않아. 정적 메서드 객체는 호출도 가능해. 정적 메서드 객체는 내장 staticmethod() 생성자로 생성돼.
클래스 메서드 객체 (Class method objects)
클래스 메서드 객체는 정적 메서드 객체처럼 다른 객체를 감싸서, 그 객체가 클래스와 클래스 인스턴스에서 가져와지는 방식을 바꾸는 래퍼야. 클래스 메서드 객체가 그러한 가져오기에서 보이는 동작은 위 "인스턴스 메서드"에서 설명돼. 클래스 메서드 객체는 내장 classmethod() 생성자로 생성돼.
특수 메서드 이름 (Special method names)
클래스는 특수 이름을 가진 메서드를 정의하여 특수 문법(산술 연산이나 첨자·슬라이싱 같은)으로 호출되는 특정 연산을 구현할 수 있어. 이것이 파이썬의 연산자 오버로딩(operator overloading) 접근 방식이야. 클래스가 언어 연산자에 대해 자기 자신의 동작을 정의할 수 있게 해 주지. 예를 들어 클래스가 __getitem__()이라는 메서드를 정의하고 x가 이 클래스의 인스턴스라면, x[i]는 대략 type(x).__getitem__(x, i)와 같아. 언급된 경우를 제외하고, 적절한 메서드가 정의되지 않았을 때 연산을 실행하려 하면 예외(보통 AttributeError 또는 TypeError)가 발생해.
특수 메서드를 None으로 설정하는 것은 해당 연산을 사용할 수 없음을 나타내. 예를 들어 클래스가 __iter__()를 None으로 설정하면 그 클래스는 이터러블이 아니므로, 그 인스턴스에 iter()를 호출하면 (__getitem__()으로 폴백하지 않고) TypeError가 발생해. [2]
내장 타입을 흉내 내는 클래스를 구현할 때는, 모델링하는 객체에 의미가 있는 정도로만 흉내를 구현하는 것이 중요해. 예를 들어 일부 시퀀스는 개별 요소 검색에는 잘 동작하지만 슬라이스 추출이 의미가 없을 수 있어. (W3C의 Document Object Model에 있는 NodeList 인터페이스가 그 예야.)
기본 커스터마이즈 (Basic customization)
object.__new__(cls[, ...])
클래스 cls의 새 인스턴스를 만들기 위해 호출돼. __new__()는 정적 메서드야(그렇게 선언할 필요가 없도록 특별 취급됨). 인스턴스가 요청된 클래스를 첫 번째 인자로 받아. 나머지 인자는 객체 생성자 표현식(클래스 호출)에 전달된 것들이야. __new__()의 반환 값은 새 객체 인스턴스여야 해(보통 cls의 인스턴스).
전형적인 구현은 super().__new__(cls[, ...])로 적절한 인자와 함께 수퍼클래스의 __new__() 메서드를 호출해 클래스의 새 인스턴스를 만든 다음, 반환하기 전에 필요에 따라 새로 만든 인스턴스를 수정해.
객체 생성 중 __new__()가 호출되어 cls의 인스턴스를 반환하면, 그 새 인스턴스의 __init__() 메서드가 __init__(self[, ...])처럼 호출돼. 여기서 self는 새 인스턴스이고 나머지 인자는 객체 생성자에 전달된 것과 같아. __new__()가 cls의 인스턴스를 반환하지 않으면 새 인스턴스의 __init__() 메서드는 호출되지 않아.
__new__()는 주로 불변 타입(int, str, tuple 같은)의 하위 클래스가 인스턴스 생성을 커스터마이즈하도록 하기 위한 것이야. 또한 클래스 생성을 커스터마이즈하기 위해 사용자 정의 메타클래스에서 흔히 오버라이드되곤 해.
object.__init__(self[, ...])
인스턴스가 (__new__()로) 생성된 후, 호출자에게 반환되기 전에 호출돼. 인자는 클래스 생성자 표현식에 전달된 것들이야. 기본 클래스에 __init__() 메서드가 있으면, 파생 클래스의 __init__() 메서드(있다면)는 인스턴스의 기본 클래스 부분이 제대로 초기화되도록 명시적으로 호출해야 해. 예: super().__init__([args...]).
__new__()와 __init__()은 객체를 구성하면서 함께 작동하므로(__new__()는 생성하고, __init__()은 커스터마이즈), __init__()은 None이 아닌 값을 반환하면 안 돼. 그렇게 하면 런타임에 TypeError가 발생해.
object.__del__(self)
인스턴스가 파괴되려 할 때 호출돼. 이를 finalizer 또는 (부적절하게) 소멸자(destructor)라고도 불러. 기본 클래스에 __del__() 메서드가 있으면, 파생 클래스의 __del__() 메서드(있다면)는 인스턴스의 기본 클래스 부분이 제대로 삭제되도록 명시적으로 호출해야 해.
__del__() 메서드가 인스턴스에 대한 새 참조를 만들어 파괴를 연기하는 것도 (권장되진 않지만!) 가능해. 이를 객체 *부활(resurrection)*이라고 불러. 부활한 객체가 파괴되려 할 때 __del__()이 두 번째로 호출될지는 구현에 따라 달라. 현재 CPython 구현은 한 번만 호출해.
인터프리터가 종료될 때 여전히 존재하는 객체에 대해 __del__() 메서드가 호출된다는 보장은 없어. weakref.finalize는 객체가 가비지 컬렉션될 때 호출될 정리 함수를 등록하는 간단한 방법을 제공해.
참고
del x는 x.__del__()을 직접 호출하지 않아. 전자는 x의 참조 카운트를 1 줄이고, 후자는 x의 참조 카운트가 0에 도달했을 때만 호출돼.
CPython 구현 세부사항: 참조 순환(reference cycle)이 객체의 참조 카운트가 0이 되는 것을 막을 수 있어. 이 경우 순환은 나중에 순환 가비지 컬렉터에 의해 감지되고 삭제돼. 참조 순환의 일반적인 원인은 예외가 지역 변수에 잡혔을 때야. 그러면 프레임의 지역 변수가 예외를 참조하고, 예외는 자신의 traceback을 참조하고, traceback은 그 안에 잡힌 모든 프레임의 지역 변수를 참조해.
참고: gc 모듈에 대한 문서.
경고
__del__() 메서드가 호출되는 위태로운 상황 때문에, 실행 중 발생하는 예외는 무시되고 대신 경고가 sys.stderr에 출력돼. 특히:
__del__()은 임의의 코드가 실행되는 동안, 임의의 스레드에서 호출될 수 있어.__del__()이 락을 잡거나 다른 차단 자원을 호출해야 한다면,__del__()을 실행하기 위해 중단된 코드가 그 자원을 이미 잡고 있을 수 있으므로 교착 상태(deadlock)에 빠질 수 있어.__del__()은 인터프리터 종료 중에 실행될 수 있어. 그 결과, 접근해야 하는 전역 변수(다른 모듈 포함)가 이미 삭제되었거나None으로 설정됐을 수 있어. 파이썬은 단일 밑줄로 시작하는 이름의 전역 변수가 다른 전역 변수보다 먼저 자신의 모듈에서 삭제되도록 보장해. 그런 전역 변수에 대한 다른 참조가 없다면, 이는__del__()메서드가 호출될 때 가져온 모듈이 여전히 사용 가능하도록 하는 데 도움이 될 수 있어.
object.__repr__(self)
객체의 "공식(official)" 문자열 표현을 계산하기 위해 repr() 내장 함수가 호출해. 가능하다면 적절한 환경 하에서 같은 값을 가진 객체를 재현할 수 있는 유효한 파이썬 표현식처럼 보여야 해. 그게 가능하지 않다면 <...유용한 설명...> 형태의 문자열을 반환해야 해. 반환 값은 문자열 객체여야 해. 클래스가 __repr__()을 정의하지만 __str__()은 정의하지 않으면, 그 클래스 인스턴스의 "비공식(informal)" 문자열 표현이 필요할 때도 __repr__()이 사용돼. 이는 보통 디버깅에 사용되므로, 표현이 정보가 풍부하고 모호함이 없어야 하는 것이 중요해. 기본 구현은 object 클래스 자체가 제공해.
object.__str__(self)
str(object), 기본 __format__() 구현, 그리고 내장 함수 print()가 객체의 "비공식" 또는 보기 좋게 출력할 수 있는 문자열 표현을 계산하기 위해 호출해. 반환 값은 str 객체여야 해.
이 메서드는 __str__()이 유효한 파이썬 표현식을 반환해야 한다는 기대가 없다는 점에서 object.__repr__()과 달라. 더 편리하거나 간결한 표현을 사용할 수 있지.
내장 타입 object가 정의한 기본 구현은 object.__repr__()을 호출해.
object.__bytes__(self)
객체의 바이트 문자열 표현을 계산하기 위해 bytes가 호출해. 이는 bytes 객체를 반환해야 해. object 클래스 자체는 이 메서드를 제공하지 않아.
object.__format__(self, format_spec)
객체의 "포맷된" 문자열 표현을 만들기 위해 format() 내장 함수가, 그리고 확장해서 포맷된 문자열 리터럴의 평가와 str.format() 메서드가 호출해. format_spec 인자는 원하는 포맷 옵션에 대한 설명을 담은 문자열이야. format_spec 인자의 해석은 __format__()을 구현하는 타입에게 달려 있어. 하지만 대부분의 클래스는 내장 타입 중 하나에 포맷을 위임하거나 유사한 포맷 옵션 문법을 사용할 거야. 표준 포맷 문법에 대한 설명은 Format specification mini-language를 참고해. 반환 값은 문자열 객체여야 해.
object 클래스의 기본 구현에는 빈 format_spec 문자열이 주어져야 해. 그것은 __str__()에 위임해.
버전 3.4에서 변경: object의 format 메서드는 비어 있지 않은 문자열을 전달받으면 TypeError를 발생시켜.
버전 3.7에서 변경: object.__format__(x, '')는 이제 format(str(x), '')가 아니라 str(x)와 같아.
object.__lt__(self, other), object.__le__(self, other), object.__eq__(self, other), object.__ne__(self, other), object.__gt__(self, other), object.__ge__(self, other)
이것들이 이른바 "확장 비교(rich comparison)" 메서드야. 연산자 기호와 메서드 이름의 대응은 다음과 같아: x<y는 x.__lt__(y), x<=y는 x.__le__(y), x==y는 x.__eq__(y), x!=y는 x.__ne__(y), x>y는 x.__gt__(y), x>=y는 x.__ge__(y)를 호출해.
확장 비교 메서드는 주어진 인자 쌍에 대해 연산을 구현하지 않는다면 싱글턴 NotImplemented를 반환할 수 있어. 관례상 성공적인 비교에는 False와 True가 반환돼. 하지만 이 메서드들은 어떤 값이든 반환할 수 있으므로, 비교 연산자가 불리언 문맥(예: if 문의 조건)에서 사용되면 파이썬은 결과가 참인지 거짓인지 판단하기 위해 그 값에 bool()을 호출해.
기본적으로 object는 is를 사용해 __eq__()을 구현하고, 거짓 비교의 경우 NotImplemented를 반환해: True if x is y else NotImplemented. __ne__()의 경우 기본적으로 __eq__()에 위임하고, 결과가 NotImplemented가 아니면 결과를 뒤집어. 비교 연산자나 기본 구현 사이에 다른 암시적 관계는 없어. 예를 들어 (x<y or x==y)의 참이 x<=y를 의미하지 않아. 단일 루트 연산에서 순서 연산을 자동으로 생성하려면 @functools.total_ordering을 참고해.
기본적으로 object 클래스는 Value comparisons와 일관된 구현을 제공해. 동등 비교는 객체 정체성에 따라 비교하고, 순서 비교는 TypeError를 발생시켜. 각 기본 메서드는 이 결과들을 직접 생성할 수 있지만 NotImplemented를 반환할 수도 있어.
사용자 지정 비교 연산을 지원하고 딕셔너리 키로 사용할 수 있는 해시 가능 객체를 만들 때 중요한 몇 가지 참고 사항은 __hash__() 문단을 참고해.
이 메서드들에는 인자 순서가 바뀐(swapped-argument) 버전이 없어(왼쪽 인자가 연산을 지원하지 않지만 오른쪽 인자는 지원할 때 사용되는 것). 대신 __lt__()와 __gt__()는 서로의 반영(reflection)이고, __le__()와 __ge__()는 서로의 반영이며, __eq__()와 __ne__()은 자신의 반영이야.
피연산자가 서로 다른 타입이고 오른쪽 피연산자의 타입이 왼쪽 피연산자 타입의 직접적 또는 간접적 하위 클래스라면, 오른쪽 피연산자의 반영 메서드가 우선권을 갖고, 그렇지 않으면 왼쪽 피연산자의 메서드가 우선권을 가져. 가상 하위 클래스화는 고려되지 않아.
적절한 메서드가 NotImplemented가 아닌 어떤 값도 반환하지 않으면, ==와 != 연산자는 각각 is와 is not으로 폴백해.
object.__hash__(self)
내장 함수 hash()와 set, frozenset, dict를 포함한 해시 컬렉션의 멤버에 대한 연산에서 호출돼. __hash__() 메서드는 정수를 반환해야 해. 유일한 요구 속성은 같게 비교되는 객체가 같은 해시 값을 가진다는 것이야. 객체 비교에 관여하는 구성 요소들의 해시 값을 튜플로 묶어 그 튜플을 해싱하는 방식으로 섞는 것을 권장해. 예:
def __hash__(self):
return hash((self.name, self.nick, self.color))
참고
hash()는 객체의 사용자 지정 __hash__() 메서드가 반환한 값을 Py_ssize_t의 크기로 잘라내. 이는 보통 64비트 빌드에서 8바이트, 32비트 빌드에서 4바이트야. 객체의 __hash__()가 다른 비트 크기의 빌드에서 상호 운용되어야 한다면, 지원하는 모든 빌드에서 너비를 확인해야 해. 간단한 방법은 python -c "import sys; print(sys.hash_info.width)"로 하는 거야.
클래스가 __eq__() 메서드를 정의하지 않으면 __hash__() 연산도 정의하면 안 돼. __eq__()은 정의하지만 __hash__()은 정의하지 않으면, 그 인스턴스는 해시 컬렉션의 항목으로 사용할 수 없어. 클래스가 가변 객체를 정의하고 __eq__() 메서드를 구현한다면 __hash__()을 구현하면 안 돼. 해시 가능 컬렉션의 구현은 키의 해시 값이 불변이어야 하기 때문이야(객체의 해시 값이 바뀌면 잘못된 해시 버킷에 있게 돼).
사용자 정의 클래스는 기본적으로 (object 클래스에서 상속된) __eq__()과 __hash__() 메서드를 가져. 이를 통해 모든 객체는 (자기 자신을 제외하고) 서로 같지 않게 비교되고, x.__hash__()는 x == y가 x is y와 hash(x) == hash(y)를 모두 의미하도록 하는 적절한 값을 반환해.
__eq__()을 오버라이드하고 __hash__()을 정의하지 않은 클래스는 __hash__()이 암시적으로 None으로 설정돼. 클래스의 __hash__() 메서드가 None이면, 프로그램이 그 인스턴스의 해시 값을 검색하려 할 때 적절한 TypeError가 발생하고, isinstance(obj, collections.abc.Hashable)을 확인할 때도 해시 불가능으로 올바르게 식별돼.
__eq__()을 오버라이드하는 클래스가 부모 클래스의 __hash__() 구현을 유지해야 한다면, __hash__ = <ParentClass>.__hash__로 설정하여 인터프리터에 이를 명시적으로 알려야 해.
__eq__()을 오버라이드하지 않는 클래스가 해시 지원을 억제하려면 클래스 정의에 __hash__ = None을 포함해야 해.
자신만의 __hash__()을 정의하면서 명시적으로 TypeError를 발생시키는 클래스는 isinstance(obj, collections.abc.Hashable) 호출로 해시 가능하다고 잘못 식별돼.
참고
기본적으로 str과 bytes 객체의 __hash__() 값은 예측할 수 없는 무작위 값으로 "염(salted)" 처리돼. 개별 파이썬 프로세스 내에서는 일정하지만, 파이썬을 반복 실행할 때마다 예측할 수는 없어. 이것은 dict 삽입의 최악의 경우 성능인 O(n²) 복잡성을 이용하는 신중하게 선택된 입력으로 인한 서비스 거부(denial-of-service)로부터 보호하기 위한 것이야. 자세한 내용은 https://ocert.org/advisories/ocert-2011-003.html 참고. 해시 값을 바꾸면 집합의 반복 순서에 영향을 미쳐. 파이썬은 이 순서에 대해 어떤 보장도 한 적이 없어(그리고 보통 32비트와 64비트 빌드 사이에 다르지). PYTHONHASHSEED도 참고해.
버전 3.3에서 변경: 해시 무작위화가 기본적으로 활성화됐어.
object.__bool__(self)
참값 테스트와 내장 연산 bool()을 구현하기 위해 호출돼. False 또는 True를 반환해야 해. 이 메서드가 정의되지 않으면, 정의되어 있다면 __len__()이 호출되고 그 결과가 0이 아니면 객체는 참으로 간주돼. 클래스가 __len__()도 __bool__()도 정의하지 않으면(object 클래스 자체가 그렇지) 모든 인스턴스가 참으로 간주돼.
속성 접근 커스터마이즈 (Customizing attribute access)
다음 메서드들은 클래스 인스턴스에 대한 속성 접근(x.name의 사용, 할당, 삭제)의 의미를 커스터마이즈하기 위해 정의할 수 있어.
object.__getattr__(self, name)
기본 속성 접근이 AttributeError로 실패할 때 호출돼(__getattribute__()가 name이 self에 대한 인스턴스 속성도 클래스 트리의 속성도 아니라서 AttributeError를 발생시키거나, name 속성의 __get__()이 AttributeError를 발생시키는 등). 이 메서드는 (계산된) 속성 값을 반환하거나 AttributeError 예외를 발생시켜야 해.
object 클래스 자체는 이 메서드를 제공하지 않아. 속성이 정상 메커니즘으로 발견되면 __getattr__()은 호출되지 않는다는 점을 참고해. (이것은 __getattr__()과 __setattr__() 사이의 의도된 비대칭성이야.) 이는 효율성 때문이기도 하고, 그렇지 않으면 __getattr__()이 인스턴스의 다른 속성에 접근할 방법이 없기 때문이기도 해. 최소한 인스턴스 변수에 대해서는, 인스턴스 속성 딕셔너리에 어떤 값도 넣지 않고(대신 다른 객체에 넣고) 완전한 제어를 할 수 있다는 점을 참고해. 속성 접근에 대한 완전한 제어를 실제로 얻는 방법은 아래 __getattribute__() 메서드를 참고해.
object.__getattribute__(self, name)
클래스 인스턴스에 대한 속성 접근을 구현하기 위해 무조건 호출돼. 클래스가 __getattr__()도 정의하면, __getattribute__()이 명시적으로 호출하거나 AttributeError를 발생시키지 않는 한 후자는 호출되지 않아. 이 메서드는 (계산된) 속성 값을 반환하거나 AttributeError 예외를 발생시켜야 해. 이 메서드에서 무한 재귀를 피하기 위해, 구현은 필요로 하는 속성에 접근할 때 항상 같은 이름의 기본 클래스 메서드를 호출해야 해. 예: object.__getattribute__(self, name).
참고
이 메서드는 언어 문법이나 내장 함수를 통한 암시적 호출의 결과로 특수 메서드를 찾을 때 여전히 우회될 수 있어. 특수 메서드 조회 참고.
특정 민감한 속성 접근의 경우 인자 obj와 name을 가진 auditing event object.__getattr__을 발생시켜.
object.__setattr__(self, name, value)
속성 할당이 시도될 때 호출돼. 이는 정상 메커니즘(즉 인스턴스 딕셔너리에 값 저장) 대신 호출돼. name은 속성 이름이고, value는 할당할 값이야. __setattr__()이 인스턴스 속성에 할당하려면 같은 이름의 기본 클래스 메서드를 호출해야 해. 예: object.__setattr__(self, name, value).
특정 민감한 속성 할당의 경우 인자 obj, name, value를 가진 auditing event object.__setattr__을 발생시켜.
object.__delattr__(self, name)
__setattr__()과 같지만 할당 대신 속성 삭제용이야. del obj.name이 객체에 의미가 있을 때만 구현해야 해. 특정 민감한 속성 삭제의 경우 인자 obj와 name을 가진 auditing event object.__delattr__을 발생시켜.
object.__dir__(self)
객체에 dir()이 호출될 때 호출돼. iterable을 반환해야 해. dir()은 반환된 iterable을 리스트로 변환하고 정렬해.
모듈 속성 접근 커스터마이즈 (Customizing module attribute access)
module.__getattr__(), module.__dir__()
특수 이름 __getattr__과 __dir__은 모듈 속성에 대한 접근을 커스터마이즈하는 데도 사용할 수 있어. 모듈 레벨의 __getattr__ 함수는 속성의 이름인 인자 하나를 받아 계산된 값을 반환하거나 AttributeError를 발생시켜야 해. 속성이 정상 조회, 즉 object.__getattribute__()로 모듈 객체에서 발견되지 않으면, AttributeError를 발생시키기 전에 모듈 __dict__에서 __getattr__을 검색해. 발견되면 속성 이름과 함께 호출되고 결과가 반환돼.
__dir__ 함수는 인자를 받지 않아야 하고, 모듈에서 접근 가능한 이름을 나타내는 문자열의 iterable을 반환해야 해. 존재하면 이 함수는 모듈의 표준 dir() 검색을 오버라이드해.
module.__class__
모듈 동작(속성 설정, 프로퍼티 등)을 더 세밀하게 커스터마이즈하려면 모듈 객체의 __class__ 속성을 types.ModuleType의 하위 클래스로 설정할 수 있어. 예:
import sys
from types import ModuleType
class VerboseModule(ModuleType):
def __repr__(self):
return f'Verbose {self.__name__}'
def __setattr__(self, attr, value):
print(f'Setting {attr}...')
super().__setattr__(attr, value)
sys.modules[__name__].__class__ = VerboseModule
참고
모듈 __getattr__을 정의하고 모듈 __class__를 설정하는 것은 속성 접근 문법으로 이루어진 조회에만 영향을 미쳐. 모듈 전역에 직접 접근하는 것(모듈 내부의 코드로든, 모듈의 전역 딕셔너리에 대한 참조를 통해서든)은 영향을 받지 않아.
버전 3.5에서 변경: __class__ 모듈 속성은 이제 쓰기 가능해.
버전 3.7에서 추가: __getattr__과 __dir__ 모듈 속성.
참고: PEP 562 - Module getattr and dir. 모듈의 __getattr__과 __dir__ 함수를 설명해.
Descriptor 구현하기 (Implementing Descriptors)
다음 메서드들은 메서드를 담고 있는 클래스(이른바 descriptor 클래스)의 인스턴스가 owner 클래스에 나타날 때만 적용돼(descriptor는 owner의 클래스 딕셔너리나 그 부모 중 하나의 클래스 딕셔너리에 있어야 해). 아래 예에서 "the attribute"는 owner 클래스의 __dict__에서 그 속성의 이름이 key인 속성을 가리켜. object 클래스 자체는 이 프로토콜들 중 어떤 것도 구현하지 않아.
object.__get__(self, instance, owner=None)
owner 클래스의 속성(클래스 속성 접근) 또는 그 클래스의 인스턴스의 속성(인스턴스 속성 접근)을 가져오기 위해 호출돼. 선택적 owner 인자는 owner 클래스이고, instance는 속성이 통해 접근된 인스턴스, 또는 속성이 owner를 통해 접근될 때 None이야. 이 메서드는 계산된 속성 값을 반환하거나 AttributeError 예외를 발생시켜야 해.
PEP 252는 __get__()이 인자 하나 또는 두 개로 호출 가능하다고 명시해. 파이썬 자체 내장 descriptor들은 이 명세를 지원해. 하지만 일부 서드파티 도구에는 두 인자를 모두 요구하는 descriptor들이 있을 가능성이 높아. 파이썬 자체 __getattribute__() 구현은 필요하든 아니든 항상 두 인자를 모두 전달해.
object.__set__(self, instance, value)
owner 클래스의 인스턴스 instance의 속성을 새 값 value로 설정하기 위해 호출돼. __set__() 또는 __delete__()을 추가하면 descriptor의 종류가 "data descriptor"로 바뀐다는 점에 유의해. 자세한 내용은 Invoking Descriptors 참고.
object.__delete__(self, instance)
owner 클래스의 인스턴스 instance의 속성을 삭제하기 위해 호출돼.
descriptor의 인스턴스는 __objclass__ 속성도 가질 수 있어:
object.__objclass__
__objclass__ 속성은 inspect 모듈이 이 객체가 정의된 클래스를 지정하는 것으로 해석해(적절히 설정하면 동적 클래스 속성의 런타임 인트로스펙션에 도움이 될 수 있어). 호출 가능 객체의 경우 주어진 타입(또는 하위 클래스)의 인스턴스가 첫 번째 위치 인자로 기대되거나 요구된다는 것을 나타낼 수 있어(예: CPython은 C로 구현된 unbound 메서드에 대해 이 속성을 설정해).
Descriptor 호출하기 (Invoking Descriptors)
일반적으로 descriptor는 "바인딩 동작"을 가진 객체 속성으로, 그 속성 접근이 descriptor 프로토콜의 메서드인 __get__(), __set__(), __delete__()에 의해 오버라이드된 것이야. 그 메서드 중 하나라도 객체에 정의되어 있으면 그 객체는 descriptor라고 말해.
속성 접근의 기본 동작은 객체의 딕셔너리에서 속성을 가져오거나, 설정하거나, 삭제하는 것이야. 예를 들어 a.x는 a.__dict__['x']로 시작하고, 그 다음 type(a).__dict__['x'], 그리고 메타클래스를 제외한 type(a)의 기본 클래스들을 거쳐 계속되는 조회 체인을 가져.
하지만 조회된 값이 descriptor 메서드 중 하나를 정의하는 객체라면, 파이썬은 기본 동작을 오버라이드하고 대신 descriptor 메서드를 호출할 수 있어. 이것이 우선순위 체인에서 어디에 발생하는지는 어떤 descriptor 메서드가 정의되었고 어떻게 호출되었는지에 따라 달라.
descriptor 호출의 시작점은 바인딩, 즉 a.x야. 인자가 어떻게 조립되는지는 a에 따라 달라:
직접 호출 (Direct Call)
가장 단순하고 가장 드문 호출은 사용자 코드가 descriptor 메서드를 직접 호출할 때야: x.__get__(a).
인스턴스 바인딩 (Instance Binding)
객체 인스턴스에 바인딩하면 a.x는 호출 type(a).__dict__['x'].__get__(a, type(a))로 변환돼.
클래스 바인딩 (Class Binding)
클래스에 바인딩하면 A.x는 호출 A.__dict__['x'].__get__(None, A)로 변환돼.
Super 바인딩 (Super Binding)
super(A, a).x 같은 점 표기 조회는 a.__class__.__mro__에서 A 다음에 오는 기본 클래스 B를 검색한 다음 B.__dict__['x'].__get__(a, A)를 반환해. x가 descriptor가 아니면 변경 없이 반환돼.
인스턴스 바인딩의 경우 descriptor 호출의 우선순위는 어떤 descriptor 메서드가 정의되었는지에 따라 달라. descriptor는 __get__(), __set__(), __delete__()의 어떤 조합이든 정의할 수 있어. __get__()을 정의하지 않으면, 객체의 인스턴스 딕셔너리에 값이 없지 않는 한 속성에 접근하면 descriptor 객체 자체가 반환돼. descriptor가 __set__() 및/또는 __delete__()을 정의하면 data descriptor이고, 둘 다 정의하지 않으면 non-data descriptor야. 보통 data descriptor는 __get__()과 __set__()을 모두 정의하고, non-data descriptor는 __get__() 메서드만 가져. __get__()과 __set__()(및/또는 __delete__())을 정의한 data descriptor는 항상 인스턴스 딕셔너리의 재정의를 오버라이드해. 반대로 non-data descriptor는 인스턴스가 오버라이드할 수 있어.
Python 메서드(@staticmethod와 @classmethod로 장식된 것 포함)는 non-data descriptor로 구현돼. 따라서 인스턴스가 메서드를 재정의하고 오버라이드할 수 있어. 이는 개별 인스턴스가 같은 클래스의 다른 인스턴스와 다른 동작을 획득하도록 허용해.
@property 데코레이터는 data descriptor로 구현돼. 따라서 인스턴스는 property의 동작을 오버라이드할 수 없어.
slots
*slots*을 사용하면 데이터 멤버(프로퍼티처럼)를 명시적으로 선언하고, __dict__와 *weakref의 생성을 거부할 수 있어(slots*에 명시적으로 선언하거나 부모에서 사용 가능하지 않으면).
__dict__를 사용하는 것에 비해 절약되는 공간은 상당할 수 있어. 속성 조회 속도도 크게 향상될 수 있지.
object.__slots__
이 클래스 변수에는 인스턴스가 사용하는 변수 이름의 문자열, iterable, 또는 문자열 시퀀스를 할당할 수 있어. *slots*은 선언된 변수들을 위한 공간을 예약하고 각 인스턴스에 대한 __dict__와 *weakref*의 자동 생성을 방지해.
slots 사용에 대한 참고 사항:
- *slots*이 없는 클래스에서 상속할 때는 인스턴스의
__dict__와 weakref 속성이 항상 접근 가능할 거야. __dict__변수 없이는 인스턴스에 slots 정의에 나열되지 않은 새 변수를 할당할 수 없어. 나열되지 않은 변수 이름에 할당하려 하면AttributeError가 발생해. 새 변수의 동적 할당이 필요하다면 slots 선언의 문자열 시퀀스에'__dict__'를 추가해.- 각 인스턴스에 대한 weakref 변수 없이는, *slots*을 정의하는 클래스는 그 인스턴스에 대한 약한 참조를 지원하지 않아. 약한 참조 지원이 필요하면 slots 선언의 문자열 시퀀스에
'__weakref__'를 추가해. - *slots*은 각 변수 이름에 대한 descriptor를 만들어 클래스 레벨에서 구현돼. 결과적으로 클래스 속성은 *slots*이 정의한 인스턴스 변수의 기본값을 설정하는 데 사용될 수 없어. 그렇게 하면 클래스 속성이 descriptor 할당을 덮어쓰게 되니까.
- slots 선언의 동작은 정의된 클래스에만 국한되지 않아. 부모에 선언된 *slots*은 자식 클래스에서도 사용 가능해. 하지만 하위 클래스가 *slots*을 정의하지 않으면(그것은 추가 슬롯의 이름만 담아야 해) 자식 하위 클래스의 인스턴스는
__dict__와 *weakref*를 얻게 돼. - 클래스가 기본 클래스에도 정의된 슬롯을 정의하면, 기본 클래스 슬롯이 정의한 인스턴스 변수는 접근할 수 없게 돼(그 descriptor를 기본 클래스에서 직접 검색하는 것 외에는). 이는 프로그램의 의미를 정의되지 않게 만들어. 미래에는 이를 방지하는 검사가 추가될 수 있어.
int,bytes,tuple같은"가변 길이" 내장 타입에서 파생된 클래스에 비어 있지 않은 *slots*이 정의되면TypeError가 발생해.- 비문자열 iterable은 *slots*에 할당될 수 있어.
dictionary를 사용해 *slots*을 할당하면 딕셔너리 키가 슬롯 이름으로 사용돼. 딕셔너리의 값은inspect.getdoc()이 인식하고help()의 출력에 표시되는 속성별 docstring을 제공하는 데 사용될 수 있어.__class__할당은 두 클래스가 같은 *slots*을 가질 때만 작동해.- 여러 개의 slotted 부모 클래스를 사용한 다중 상속은 사용될 수 있지만, 슬롯이 만든 속성을 가질 수 있는 부모는 하나뿐이야(다른 기반은 빈 슬롯 레이아웃을 가져야 해). 위반하면
TypeError가 발생해. - *slots*에 iterator가 사용되면 이터레이터의 각 값에 대해 descriptor가 생성돼. 하지만 slots 속성은 빈 이터레이터가 돼.
클래스 생성 커스터마이즈 (Customizing class creation)
클래스가 다른 클래스에서 상속할 때마다 부모 클래스에서 __init_subclass__()이 호출돼. 이렇게 하면 하위 클래스의 동작을 바꾸는 클래스를 작성할 수 있어. 이것은 클래스 데코레이터와 밀접하게 관련돼 있지만, 클래스 데코레이터는 적용된 특정 클래스에만 영향을 주는 반면 __init_subclass__은 그 메서드를 정의하는 클래스의 미래 하위 클래스에만 적용돼.
classmethod object.__init_subclass__(cls)
이 메서드는 포함하는 클래스가 하위 클래스화될 때마다 호출돼. cls는 그때의 새 하위 클래스야. 일반 인스턴스 메서드로 정의하면 이 메서드는 암시적으로 클래스 메서드로 변환돼. 새 클래스에 주어진 키워드 인자는 부모 클래스의 __init_subclass__에 전달돼. __init_subclass__을 사용하는 다른 클래스와의 호환성을 위해 필요한 키워드 인자를 빼내고 나머지를 기본 클래스에 넘겨야 해. 예:
class Philosopher:
def __init_subclass__(cls, /, default_name, **kwargs):
super().__init_subclass__(**kwargs)
cls.default_name = default_name
class AustralianPhilosopher(Philosopher, default_name="Bruce"):
pass
기본 구현 object.__init_subclass__은 아무것도 하지 않지만, 어떤 인자와 함께 호출되면 오류를 발생시켜.
참고
메타클래스 힌트 metaclass는 type 메커니즘의 나머지가 소비하고, __init_subclass__ 구현에 전달되지 않아. 실제 메타클래스(명시적 힌트가 아닌)는 type(cls)로 접근할 수 있어.
버전 3.6에서 추가됨.
클래스가 생성될 때 type.__new__()은 클래스 변수를 검사하고 __set_name__() 훅이 있는 것들에 콜백을 만들어.
object.__set_name__(self, owner, name)
소유 클래스 owner가 생성되는 시점에 자동으로 호출돼. 객체가 그 클래스에서 name에 할당된 상태야:
class A:
x = C() # Automatically calls: x.__set_name__(A, 'x')
클래스 변수가 클래스 생성 후에 할당되면 __set_name__()은 자동으로 호출되지 않아. 필요하면 __set_name__()을 직접 호출할 수 있어:
class A:
pass
c = C()
A.x = c # The hook is not called
c.__set_name__(A, 'x') # Manually invoke the hook
자세한 내용은 클래스 객체 생성 참고. 버전 3.6에서 추가됨.
메타클래스 (Metaclasses)
기본적으로 클래스는 type()으로 구성돼. 클래스 본문은 새 네임스페이스에서 실행되고 클래스 이름은 type(name, bases, namespace)의 결과에 로컬로 바인딩돼.
클래스 생성 과정은 클래스 정의 줄에 metaclass 키워드 인자를 전달하거나, 그런 인자를 포함한 기존 클래스에서 상속하여 커스터마이즈할 수 있어. 다음 예에서 MyClass와 MySubclass는 모두 Meta의 인스턴스야:
class Meta(type):
pass
class MyClass(metaclass=Meta):
pass
class MySubclass(MyClass):
pass
클래스 정의에 지정된 다른 모든 키워드 인자는 아래 설명된 모든 메타클래스 연산에 전달돼.
클래스 정의가 실행되면 다음 단계가 발생해:
- MRO 항목들이 해석됨;
- 적절한 메타클래스가 결정됨;
- 클래스 네임스페이스가 준비됨;
- 클래스 본문이 실행됨;
- 클래스 객체가 생성됨.
MRO 항목 해석 (Resolving MRO entries)
object.__mro_entries__(self, bases)
클래스 정의에 나타나는 기반(base)이 type의 인스턴스가 아니면, 그 기반에서 __mro_entries__() 메서드가 검색돼. __mro_entries__() 메서드가 발견되면, 클래스를 만들 때 그 기반은 __mro_entries__() 호출 결과로 대체돼. 이 메서드는 원래 기반 튜플을 bases 매개변수에 전달받아 호출되고, 기반 대신 사용할 클래스들의 튜플을 반환해야 해. 반환된 튜플은 비어 있을 수 있어. 그런 경우 원래 기반은 무시돼.
참고:
types.resolve_bases():type의 인스턴스가 아닌 기반을 동적으로 해석.types.get_original_bases():__mro_entries__()에 의한 수정 전의 클래스의 "원래 기반"을 검색.- PEP 560: typing 모듈과 제네릭 타입을 위한 핵심 지원.
적절한 메타클래스 결정 (Determining the appropriate metaclass)
클래스 정의에 대한 적절한 메타클래스는 다음과 같이 결정돼:
- 기반도 명시적 메타클래스도 없으면
type()이 사용됨; - 명시적 메타클래스가 주어졌고 그것이
type()의 인스턴스가 아니면, 그것이 직접 메타클래스로 사용됨; type()의 인스턴스가 명시적 메타클래스로 주어졌거나 기반이 정의되면, 가장 파생된 메타클래스가 사용됨.
가장 파생된 메타클래스는 명시적으로 지정된 메타클래스(있다면)와 모든 지정된 기반 클래스의 메타클래스(즉 type(cls)) 중에서 선택돼. 가장 파생된 메타클래스는 이 후보 메타클래스 모두의 하위 타입인 것이야. 후보 메타클래스 중 어느 것도 그 기준을 충족하지 않으면 클래스 정의는 TypeError로 실패해.
클래스 네임스페이스 준비 (Preparing the class namespace)
적절한 메타클래스가 식별되면 클래스 네임스페이스가 준비돼. 메타클래스에 __prepare__ 속성이 있으면 namespace = metaclass.__prepare__(name, bases, **kwds)로 호출돼(추가 키워드 인자는 있다면 클래스 정의에서 옴). __prepare__ 메서드는 classmethod로 구현해야 해. __prepare__가 반환한 네임스페이스는 __new__에 전달되지만, 최종 클래스 객체가 생성될 때 네임스페이스는 새 dict로 복사돼.
메타클래스에 __prepare__ 속성이 없으면 클래스 네임스페이스는 빈 순서 매핑으로 초기화돼.
참고: PEP 3115 - Metaclasses in Python 3000. __prepare__ 네임스페이스 훅을 도입했어.
클래스 본문 실행 (Executing the class body)
클래스 본문은 (대략) exec(body, globals(), namespace)로 실행돼. 일반적인 exec() 호출과의 핵심 차이는, 클래스 정의가 함수 안에서 일어날 때 어휘적 스코핑이 클래스 본문(메서드를 포함해)이 현재 및 바깥 스코프의 이름을 참조하도록 허용한다는 것이야.
하지만 클래스 정의가 함수 안에서 일어나도 클래스 안에서 정의된 메서드는 여전히 클래스 스코프에 정의된 이름을 볼 수 없어. 클래스 변수는 인스턴스 또는 클래스 메서드의 첫 번째 매개변수를 통해, 또는 다음 섹션에서 설명하는 암시적 어휘 스코프 __class__ 참조를 통해 접근해야 해.
클래스 객체 생성 (Creating the class object)
클래스 네임스페이스가 클래스 본문 실행으로 채워지면, metaclass(name, bases, namespace, **kwds)를 호출해 클래스 객체가 생성돼(여기에 전달된 추가 키워드는 __prepare__에 전달된 것과 같아).
이 클래스 객체가 super()의 0-인자 형태가 참조하는 객체야. __class__는 클래스 본문의 어떤 메서드가 __class__나 super를 참조하면 컴파일러가 만드는 암시적 클로저 참조야. 이는 super()의 0-인자 형태가 어휘적 스코핑을 기반으로 정의되는 클래스를 올바르게 식별하도록 허용하는 반면, 현재 호출을 만드는 데 사용된 클래스나 인스턴스는 메서드에 전달된 첫 번째 인자를 기반으로 식별돼.
CPython 구현 세부사항: CPython 3.6 이상에서 __class__ 셀은 __classcell__ 항목으로 클래스 네임스페이스의 메타클래스에 전달돼. 존재하면 클래스가 올바르게 초기화되도록 type.__new__ 호출까지 전파되어야 해. 그러지 않으면 파이썬 3.8에서 RuntimeError가 발생해.
기본 메타클래스 type이나 궁극적으로 type.__new__를 호출하는 어떤 메타클래스를 사용할 때, 클래스 객체 생성 후 다음 추가 커스터마이즈 단계가 호출돼:
type.__new__메서드는__set_name__()메서드를 정의하는 클래스 네임스페이스의 모든 속성을 수집해;- 그
__set_name__메서드들은 정의되는 클래스와 그 특정 속성의 할당된 이름으로 호출돼; __init_subclass__()훅이 새 클래스의 메서드 결정 순서에서 바로 위 부모에서 호출돼.
클래스 객체가 생성된 후, 클래스 정의에 포함된 클래스 데코레이터(있다면)에 전달되고 결과 객체는 정의된 클래스로서 지역 네임스페이스에 바인딩돼.
type.__new__가 새 클래스를 만들 때, namespace 매개변수로 제공된 객체는 새 순서 매핑으로 복사되고 원래 객체는 버려져. 새 복사본은 읽기 전용 프록시로 감싸지며, 그것이 클래스 객체의 __dict__ 속성이 돼.
참고: PEP 3135 - New super. 암시적 __class__ 클로저 참조를 설명해.
메타클래스의 용도 (Uses for metaclasses)
메타클래스의 잠재적 용도는 무궁무진해. 탐구된 몇 가지 아이디어에는 enum, 로깅, 인터페이스 검사, 자동 위임, 자동 프로퍼티 생성, 프록시, 프레임워크, 자동 리소스 잠금/동기화가 포함돼.
인스턴스 및 하위 클래스 검사 커스터마이즈 (Customizing instance and subclass checks)
다음 메서드들은 isinstance()와 issubclass() 내장 함수의 기본 동작을 오버라이드하는 데 사용돼.
특히 메타클래스 abc.ABCMeta는 추상 기본 클래스(ABC)를 어떤 클래스나 타입(내장 타입 포함)에, 그리고 다른 ABC에도 "가상 기본 클래스"로 추가할 수 있도록 이 메서드들을 구현해.
type.__instancecheck__(self, instance)
instance가 class의 (직접적 또는 간접적) 인스턴스로 간주되어야 하면 true를 반환해. 정의되면 isinstance(instance, class)를 구현하기 위해 호출돼.
type.__subclasscheck__(self, subclass)
subclass가 class의 (직접적 또는 간접적) 하위 클래스로 간주되어야 하면 true를 반환해. 정의되면 issubclass(subclass, class)를 구현하기 위해 호출돼.
이 메서드들은 클래스의 타입(메타클래스)에서 조회된다는 점에 유의해. 그것들은 실제 클래스에서 클래스 메서드로 정의될 수 없어. 이는 인스턴스에서 호출되는 특수 메서드의 조회와 일관되며, 이 경우에는 인스턴스가 그 자체로 클래스라는 점만 다르지.
참고: PEP 3119 - Introducing Abstract Base Classes. __instancecheck__()와 __subclasscheck__()를 통한 isinstance()와 issubclass() 동작 커스터마이즈 명세와, 언어에 추상 기본 클래스(abc 모듈)를 추가하는 맥락에서 이 기능의 동기를 포함해.
제네릭 타입 흉내 내기 (Emulating generic types)
타입 애너테이션을 사용할 때 파이썬의 대괄호 표기법으로 제네릭 타입을 *매개변수화(parameterize)*하는 것이 종종 유용해. 예를 들어 애너테이션 list[int]는 모든 요소가 int 타입인 list를 나타내는 데 사용될 수 있어.
참고:
- PEP 484 - Type Hints: 파이썬의 타입 애너테이션 프레임워크를 도입.
- Generic Alias Types: 매개변수화된 제네릭 클래스를 나타내는 객체에 대한 문서.
- Generics, user-defined generics 그리고
typing.Generic: 런타임에 매개변수화될 수 있고 정적 타입 검사기가 이해할 수 있는 제네릭 클래스를 구현하는 방법에 대한 문서.
클래스는 일반적으로 특수 클래스 메서드 __class_getitem__()을 정의하는 경우에만 매개변수화될 수 있어.
classmethod object.__class_getitem__(cls, key)
key에서 찾은 타입 인자들로 제네릭 클래스를 전문화하는 객체를 반환해. 클래스에 정의되면 __class_getitem__()은 자동으로 클래스 메서드야. 따라서 정의할 때 @classmethod로 장식할 필요가 없어.
__class_getitem__의 목적 (The purpose of class_getitem)
__class_getitem__()의 목적은 런타임에 표준 라이브러리 제네릭 클래스의 매개변수화를 허용하여 타입 힌트를 이 클래스들에 더 쉽게 적용하게 하는 것이야.
런타임에 매개변수화될 수 있고 정적 타입 검사기가 이해할 수 있는 맞춤형 제네릭 클래스를 구현하려면, 사용자는 이미 __class_getitem__()을 구현한 표준 라이브러리 클래스에서 상속하거나, 자체 __class_getitem__() 구현을 가진 typing.Generic에서 상속해야 해.
표준 라이브러리 외부에서 정의된 클래스의 맞춤형 __class_getitem__() 구현은 mypy 같은 서드파티 타입 검사기가 이해하지 못할 수 있어. 타입 힌팅 이외의 목적으로 어떤 클래스에 __class_getitem__()을 사용하는 것은 권장되지 않아.
class_getitem 대 getitem
보통 대괄호를 사용한 객체의 첨자(subscription)는 객체의 클래스에 정의된 __getitem__() 인스턴스 메서드를 호출해. 하지만 첨자로 접근되는 객체가 그 자체로 클래스라면, 클래스 메서드 __class_getitem__()이 대신 호출될 수 있어. __class_getitem__()은 제대로 정의되면 GenericAlias 객체를 반환해야 해.
표현식 obj[x]가 주어지면, 파이썬 인터프리터는 __getitem__() 또는 __class_getitem__() 중 무엇을 호출할지 결정하기 위해 대략 다음 같은 과정을 따라:
from inspect import isclass
def subscribe(obj, x):
"""Return the result of the expression 'obj[x]'"""
class_of_obj = type(obj)
# If the class of obj defines __getitem__,
# call class_of_obj.__getitem__(obj, x)
if hasattr(class_of_obj, '__getitem__'):
return class_of_obj.__getitem__(obj, x)
# Else, if obj is a class and defines __class_getitem__,
# call obj.__class_getitem__(x)
elif isclass(obj) and hasattr(obj, '__class_getitem__'):
return obj.__class_getitem__(x)
# Else, raise an exception
else:
raise TypeError(
f"'{class_of_obj.__name__}' object is not subscriptable"
)
파이썬에서 모든 클래스는 그 자체가 다른 클래스의 인스턴스야. 클래스의 클래스를 그 클래스의 메타클래스라고 부르고, 대부분의 클래스는 type 클래스를 메타클래스로 가져. type은 __getitem__()을 정의하지 않으므로, list[int], dict[str, float], tuple[str, bytes] 같은 표현식은 모두 __class_getitem__()이 호출되는 결과를 만들어:
>>> # list has class "type" as its metaclass, like most classes:
>>> type(list)
<class 'type'>
>>> type(dict) == type(list) == type(tuple) == type(str) == type(bytes)
True
>>> # "list[int]" calls "list.__class_getitem__(int)"
>>> list[int]
list[int]
>>> # list.__class_getitem__ returns a GenericAlias object:
>>> type(list[int])
<class 'types.GenericAlias'>
하지만 클래스가 __getitem__()을 정의하는 맞춤형 메타클래스를 가진다면, 클래스를 첨자 접근하는 것은 다른 동작을 초래할 수 있어. 그 예는 enum 모듈에서 찾을 수 있어:
>>> from enum import Enum
>>> class Menu(Enum):
... """A breakfast menu"""
... SPAM = 'spam'
... BACON = 'bacon'
...
>>> # Enum classes have a custom metaclass:
>>> type(Menu)
<class 'enum.EnumMeta'>
>>> # EnumMeta defines __getitem__,
>>> # so __class_getitem__ is not called,
>>> # and the result is not a GenericAlias object:
>>> Menu['SPAM']
<Menu.SPAM: 'spam'>
>>> type(Menu['SPAM'])
<enum 'Menu'>
참고: PEP 560 - Core Support for typing module and generic types. __class_getitem__()을 도입하고, 언제 첨자가 __getitem__() 대신 __class_getitem__()이 호출되는 결과를 만드는지 개략적으로 설명해.
호출 가능 객체 흉내 내기 (Emulating callable objects)
object.__call__(self[, args...])
인스턴스가 함수로 "호출"될 때 호출돼. 이 메서드가 정의되면 x(arg1, arg2, ...)는 대략 type(x).__call__(x, arg1, ...)로 변환돼. object 클래스 자체는 이 메서드를 제공하지 않아.
컨테이너 타입 흉내 내기 (Emulating container types)
다음 메서드들은 컨테이너 객체를 구현하기 위해 정의할 수 있어. 그것들 중 어떤 것도 object 클래스 자체가 제공하지 않아. 컨테이너는 보통 시퀀스(lists 또는 tuples 같은) 또는 매핑(dictionaries 같은)이지만, 다른 컨테이너를 나타낼 수도 있어. 첫 번째 메서드 집합은 시퀀스를 흉내 내거나 매핑을 흉내 내는 데 사용돼. 차이는 시퀀스의 경우 허용되는 키는 0 <= k < N인 정수 k(여기서 N은 시퀀스의 길이) 또는 항목 범위를 정의하는 slice 객체여야 한다는 거야. 또한 매핑은 파이썬의 표준 dictionary 객체와 유사하게 동작하는 keys(), values(), items(), get(), clear(), setdefault(), pop(), popitem(), copy(), update() 메서드를 제공하는 것이 권장돼. collections.abc 모듈은 기본적인 __getitem__(), __setitem__(), __delitem__(), keys() 집합에서 그 메서드들을 만드는 데 도움이 되는 MutableMapping 추상 기본 클래스를 제공해.
가변 시퀀스는 파이썬 표준 list 객체처럼 append(), clear(), count(), extend(), index(), insert(), pop(), remove(), reverse() 메서드를 제공해야 해. 마지막으로 시퀀스 타입은 아래 설명된 __add__(), __radd__(), __iadd__(), __mul__(), __rmul__(), __imul__() 메서드를 정의하여 덧셈(연결을 의미)과 곱셈(반복을 의미)을 구현해야 해. 다른 수치 연산자는 정의하면 안 돼.
매핑과 시퀀스 모두 in 연산자의 효율적 사용을 허용하도록 __contains__() 메서드를 구현하는 것이 권장돼. 매핑의 경우 in은 매핑의 키를 검색해야 하고, 시퀀스의 경우 값을 검색해야 해. 또한 매핑과 시퀀스 모두 컨테이너를 통한 효율적 반복을 허용하도록 __iter__() 메서드를 구현하는 것이 권장돼. 매핑의 경우 __iter__()은 객체의 키를 통해 반복해야 하고, 시퀀스의 경우 값을 통해 반복해야 해.
object.__len__(self)
내장 함수 len()을 구현하기 위해 호출돼. 객체의 길이, 즉 정수 >= 0을 반환해야 해. 또한 __bool__() 메서드를 정의하지 않고 __len__() 메서드가 0을 반환하는 객체는 불리언 문맥에서 거짓으로 간주돼.
CPython 구현 세부사항: CPython에서 길이는 최대 sys.maxsize여야 해. 길이가 sys.maxsize보다 크면 len() 같은 일부 기능이 OverflowError를 발생시킬 수 있어. 참값 테스트로 OverflowError가 발생하는 것을 막으려면 객체가 __bool__() 메서드를 정의해야 해.
object.__length_hint__(self)
operator.length_hint()을 구현하기 위해 호출돼. 객체에 대한 추정 길이(실제 길이보다 크거나 작을 수 있음)를 반환해야 해. 길이는 정수 >= 0이어야 해. 반환 값은 NotImplemented일 수도 있는데, 이는 __length_hint__ 메서드가 아예 존재하지 않는 것과 동일하게 취급돼. 이 메서드는 순전히 최적화를 위한 것이며 정확성을 위해 결코 필요하지 않아. 버전 3.4에서 추가됨.
object.__getitem__(self, subscript)
subscription, 즉 self[subscript]을 구현하기 위해 호출돼. 문법에 대한 자세한 내용은 Subscriptions and slicings 참고. __getitem__()을 통해 첨자를 지원하는 두 가지 유형의 내장 객체가 있어:
- 시퀀스, 여기서 subscript(index라고도 부름)는 정수나
slice객체여야 해.slice객체와 음수 인덱스 처리를 포함한 기대 동작은 시퀀스 문서 참고. - 매핑, 여기서 subscript는 key라고도 부름. 기대 동작은 매핑 문서 참고.
subscript가 부적절한 타입이면 __getitem__()은 TypeError를 발생시켜야 해. subscript가 부적절한 값이면 __getitem__()은 LookupError 또는 그 하위 클래스(시퀀스는 IndexError, 매핑은 KeyError)를 발생시켜야 해.
참고
슬라이싱은 __getitem__(), __setitem__(), __delitem__()이 처리해. a[1:2] = b 같은 호출은 a[slice(1, 2, None)] = b로 변환되고, 그런 식으로 이어져. 누락된 슬라이스 항목은 항상 None으로 채워져.
참고
시퀀스 반복 프로토콜(for 루프에서 사용되는)은 시퀀스의 끝을 올바르게 감지할 수 있도록 불법 인덱스에 대해 IndexError가 발생할 것을 기대해.
참고
클래스를 첨자 접근할 때 __getitem__() 대신 특수 클래스 메서드 __class_getitem__()이 호출될 수 있어. 자세한 내용은 class_getitem 대 getitem 참고.
object.__setitem__(self, key, value)
self[key]에 대한 할당을 구현하기 위해 호출돼. __getitem__()과 같은 참고 사항이 적용돼. 이 메서드는 매핑에서 키의 값 변경이나 새 키 추가를 지원하거나, 시퀀스에서 요소 교체를 지원하는 경우에만 구현해야 해. 부적절한 key 값에 대해 __getitem__() 메서드와 같은 예외를 발생시켜야 해.
object.__delitem__(self, key)
self[key]의 삭제를 구현하기 위해 호출돼. __getitem__()과 같은 참고 사항이 적용돼. 이 메서드는 매핑에서 키 제거를 지원하거나, 시퀀스에서 요소 제거를 지원하는 경우에만 구현해야 해. 부적절한 key 값에 대해 __getitem__() 메서드와 같은 예외를 발생시켜야 해.
object.__missing__(self, key)
key가 딕셔너리에 없을 때 dict 하위 클래스에 대해 self[key]을 구현하기 위해 dict.__getitem__()이 호출해.
object.__iter__(self)
컨테이너에 대한 이터레이터가 필요할 때 이 메서드가 호출돼. 이 메서드는 컨테이너의 모든 객체를 반복할 수 있는 새 이터레이터 객체를 반환해야 해. 매핑의 경우 컨테이너의 키를 통해 반복해야 해.
object.__reversed__(self)
역방향 반복을 구현하기 위해 (존재하면) reversed() 내장 함수가 호출해. 컨테이너의 모든 객체를 역순으로 반복하는 새 이터레이터 객체를 반환해야 해. __reversed__() 메서드가 제공되지 않으면 reversed() 내장 함수는 시퀀스 프로토콜(__len__()과 __getitem__())을 사용하는 것으로 폴백해. 시퀀스 프로토콜을 지원하는 객체는 reversed()가 제공하는 것보다 더 효율적인 구현을 제공할 수 있을 때만 __reversed__()을 제공해야 해.
멤버십 테스트 연산자(in과 not in)는 보통 컨테이너를 통한 반복으로 구현돼. 하지만 컨테이너 객체는 객체가 이터러블일 필요가 없는 더 효율적인 구현으로 다음 특수 메서드를 제공할 수 있어.
object.__contains__(self, item)
멤버십 테스트 연산자를 구현하기 위해 호출돼. item이 self에 있으면 true, 그렇지 않으면 false를 반환해야 해. 매핑 객체의 경우 값이나 키-항목 쌍이 아니라 매핑의 키를 고려해야 해. __contains__()을 정의하지 않는 객체의 경우 멤버십 테스트는 먼저 __iter__()로 반복을 시도한 다음, __getitem__()을 통한 이전 시퀀스 반복 프로토콜을 시도해. 언어 레퍼런스의 이 섹션 참고.
숫자 타입 흉내 내기 (Emulating numeric types)
숫자 객체를 흉내 내기 위해 다음 메서드들을 정의할 수 있어. 구현하는 특정 종류의 숫자가 지원하지 않는 연산(예: 정수가 아닌 숫자에 대한 비트 연산)에 해당하는 메서드는 정의하지 않은 채로 두어야 해.
object.__add__(self, other), object.__sub__(self, other), object.__mul__(self, other), object.__matmul__(self, other), object.__truediv__(self, other), object.__floordiv__(self, other), object.__mod__(self, other), object.__divmod__(self, other), object.__pow__(self, other[, modulo]), object.__lshift__(self, other), object.__rshift__(self, other), object.__and__(self, other), object.__xor__(self, other), object.__or__(self, other)
이 메서드들은 2진 산술 연산(+, -, *, @, /, //, %, divmod(), pow(), **, <<, >>, &, ^, |)을 구현하기 위해 호출돼. 예를 들어 __add__() 메서드를 가진 클래스의 인스턴스인 x로 표현식 x + y를 평가하기 위해 type(x).__add__(x, y)가 호출돼. __divmod__() 메서드는 __floordiv__()와 __mod__()를 사용하는 것과 동등해야 해. __truediv__()와 관련이 없어야 해. 내장 pow() 함수의 3-인자 버전을 지원하려면 __pow__()가 선택적 세 번째 인자를 받도록 정의해야 한다는 점에 유의해. 그 메서드 중 하나가 제공된 인자로 연산을 지원하지 않으면 NotImplemented를 반환해야 해.
object.__radd__(self, other), object.__rsub__(self, other), object.__rmul__(self, other), object.__rmatmul__(self, other), object.__rtruediv__(self, other), object.__rfloordiv__(self, other), object.__rmod__(self, other), object.__rdivmod__(self, other), object.__rpow__(self, other[, modulo]), object.__rlshift__(self, other), object.__rrshift__(self, other), object.__rand__(self, other), object.__rxor__(self, other), object.__ror__(self, other)
이 메서드들은 반영된(swapped) 피연산자인 2진 산술 연산(+, -, *, @, /, //, %, divmod(), pow(), **, <<, >>, &, ^, |)을 구현하기 위해 호출돼. 이 함수들은 피연산자가 서로 다른 타입일 때, 왼쪽 피연산자가 해당 연산을 지원하지 않을 때[3], 또는 오른쪽 피연산자의 클래스가 왼쪽 피연산자의 클래스에서 파생되었을 때[4]만 호출돼. 예를 들어 __rsub__() 메서드를 가진 클래스의 인스턴스인 y로 표현식 x - y를 평가하기 위해, type(x).__sub__(x, y)가 NotImplemented를 반환하거나 type(y)가 type(x)의 하위 클래스이면 type(y).__rsub__(y, x)가 호출돼. [5] 내장 pow() 함수의 3-인자 버전을 지원하려면 __rpow__()가 선택적 세 번째 인자를 받도록 정의해야 한다는 점에 유의해.
버전 3.14에서 변경: 3-인자 pow()는 이제 필요하면 __rpow__()를 호출하려 시도해. 이전에는 2-인자 pow()와 2진 거듭제곱 연산자에서만 호출됐어.
참고
오른쪽 피연산자의 타입이 왼쪽 피연산자 타입의 하위 클래스이고 그 하위 클래스가 연산에 대해 반영 메서드의 다른 구현을 제공한다면, 이 메서드는 왼쪽 피연산자의 비반영 메서드보다 먼저 호출돼. 이 동작은 하위 클래스가 조상의 연산을 오버라이드할 수 있게 해.
object.__iadd__(self, other), object.__isub__(self, other), object.__imul__(self, other), object.__imatmul__(self, other), object.__itruediv__(self, other), object.__ifloordiv__(self, other), object.__imod__(self, other), object.__ipow__(self, other[, modulo]), object.__ilshift__(self, other), object.__irshift__(self, other), object.__iand__(self, other), object.__ixor__(self, other), object.__ior__(self, other)
이 메서드들은 확장 산술 할당(augmented arithmetic assignments)(+=, -=, *=, @=, /=, //=, %=, **=, <<=, >>=, &=, ^=, |=)을 구현하기 위해 호출돼. 이 메서드들은 제자리에서 연산을 수행(self 수정)하고 결과를 반환하려 시도해야 해(여기서 결과는 self일 수도 아닐 수도 있어). 특정 메서드가 정의되지 않았거나 그 메서드가 NotImplemented를 반환하면, 확장 할당은 일반 메서드로 폴백해. 예를 들어 x가 __iadd__() 메서드를 가진 클래스의 인스턴스라면 x += y는 x = x.__iadd__(y)와 같아. __iadd__()이 존재하지 않거나 x.__iadd__(y)가 NotImplemented를 반환하면, x + y의 평가에서와 같이 x.__add__(y)와 y.__radd__(x)가 고려돼. 특정 상황에서 확장 할당은 예상치 못한 오류를 초래할 수 있지만(Why does a_tuple[i] += ['item'] raise an exception when the addition works?), 이 동작은 사실 데이터 모델의 일부야.
object.__neg__(self), object.__pos__(self), object.__abs__(self), object.__invert__(self)
단항 산술 연산(-, +, abs(), ~)을 구현하기 위해 호출돼.
object.__complex__(self), object.__int__(self), object.__float__(self)
내장 함수 complex(), int(), float()을 구현하기 위해 호출돼. 적절한 타입의 값을 반환해야 해.
object.__index__(self)
operator.index()을 구현하기 위해, 그리고 파이썬이 숫자 객체를 정수 객체로 무손실 변환해야 할 때마다(슬라이싱이나 내장 bin(), hex(), oct() 함수에서처럼) 호출돼. 이 메서드의 존재는 숫자 객체가 정수 타입임을 나타내. 정수를 반환해야 해. __int__(), __float__(), __complex__()이 정의되지 않으면 해당 내장 함수 int(), float(), complex()은 __index__()로 폴백해.
object.__round__(self[, ndigits]), object.__trunc__(self), object.__floor__(self), object.__ceil__(self)
내장 함수 round()와 math 함수 trunc(), floor(), ceil()을 구현하기 위해 호출돼. __round__()에 ndigits가 전달되지 않는 한 이러한 모든 메서드는 객체의 값을 Integral(보통 int)로 버린 값을 반환해야 해.
버전 3.14에서 변경: int()는 더 이상 __trunc__() 메서드에 위임하지 않아.
With 문 컨텍스트 관리자 (With Statement Context Managers)
*컨텍스트 관리자(context manager)*는 with 문을 실행할 때 수립할 런타임 컨텍스트를 정의하는 객체야. 컨텍스트 관리자는 코드 블록 실행을 위해 원하는 런타임 컨텍스트로의 진입과 그로부터의 퇴장을 처리해. 컨텍스트 관리자는 보통 with 문(The with statement 섹션에 설명)을 사용해 호출되지만, 메서드를 직접 호출해 사용할 수도 있어.
컨텍스트 관리자의 전형적인 용도에는 다양한 종류의 전역 상태 저장 및 복원, 리소스 잠금 및 잠금 해제, 열린 파일 닫기 등이 있어.
컨텍스트 관리자에 대한 자세한 내용은 Context Manager Types 참고. object 클래스 자체는 컨텍스트 관리자 메서드를 제공하지 않아.
object.__enter__(self)
이 객체와 관련된 런타임 컨텍스트로 진입해. with 문은 문의 as 절에 대상이 지정되어 있으면 이 메서드의 반환 값을 그 대상(들)에 바인딩해.
object.__exit__(self, exc_type, exc_value, traceback)
이 객체와 관련된 런타임 컨텍스트에서 퇴장해. 매개변수들은 컨텍스트가 퇴장하게 만든 예외를 설명해. 컨텍스트가 예외 없이 퇴장되면 세 인자 모두 None이야. 예외가 제공되고, 메서드가 그 예외를 억제하고 싶다면(즉 전파되는 것을 막고 싶다면) 참 값을 반환해야 해. 그렇지 않으면 이 메서드에서 퇴장할 때 예외는 정상적으로 처리돼. __exit__() 메서드는 전달된 예외를 다시 발생시키지(reraise) 않아야 한다는 점에 유의해. 이는 호출자의 책임이야.
참고: PEP 343 - The "with" statement. 파이썬 with 문의 명세, 배경, 예제.
클래스 패턴 매칭에서 위치 인자 커스터마이즈 (Customizing positional arguments in class pattern matching)
패턴에서 클래스 이름을 사용할 때, 패턴의 위치 인자는 기본적으로 허용되지 않아. 즉 case MyClass(x, y)는 MyClass에 특별한 지원이 없으면 일반적으로 유효하지 않아. 그런 종류의 패턴을 사용하려면 클래스가 match_args 속성을 정의해야 해.
object.__match_args__
이 클래스 변수에는 문자열의 튜플을 할당할 수 있어. 이 클래스가 위치 인자를 가진 클래스 패턴에서 사용되면, 각 위치 인자는 *match_args*의 해당 값을 키워드로 사용하여 키워드 인자로 변환돼. 이 속성의 부재는 ()로 설정하는 것과 같아.
예를 들어 MyClass.__match_args__가 ("left", "center", "right")라면 case MyClass(x, y)는 case MyClass(left=x, center=y)와 같다는 뜻이야. 패턴의 인자 수는 *match_args*의 요소 수보다 작거나 같아야 한다는 점에 유의해. 더 크면 패턴 매치 시도가 TypeError를 발생시켜.
버전 3.10에서 추가됨.
참고: PEP 634 - Structural Pattern Matching. 파이썬 match 문의 명세.
버퍼 타입 흉내 내기 (Emulating buffer types)
버퍼 프로토콜은 파이썬 객체가 저수준 메모리 배열에 대한 효율적 접근을 노출하는 방법을 제공해. 이 프로토콜은 bytes와 memoryview 같은 내장 타입이 구현하고, 서드파티 라이브러리가 추가 버퍼 타입을 정의할 수 있어.
버퍼 타입은 보통 C로 구현되지만, 프로토콜을 파이썬에서 구현하는 것도 가능해.
object.__buffer__(self, flags)
self에서 버퍼가 요청될 때(예: memoryview 생성자에 의해) 호출돼. flags 인자는 요청된 버퍼의 종류를 나타내는 정수로, 예를 들어 반환된 버퍼가 읽기 전용인지 쓰기 가능한지에 영향을 미쳐. inspect.BufferFlags는 플래그를 해석하는 편리한 방법을 제공해. 이 메서드는 memoryview 객체를 반환해야 해.
스레드 안전성: 자유 스레딩(free-threaded) 파이썬에서 구현은 내부 내보내기(export) 카운터를 원자적 연산으로 관리해야 해. 이 메서드는 여러 스레드에서 동시에 호출되어도 안전해야 하고, 반환된 버퍼의 기반 데이터는 해당 __release_buffer__() 호출이 완료될 때까지 유효하게 유지되어야 해. 자세한 내용은 memoryview 객체의 스레드 안전성 참고.
object.__release_buffer__(self, buffer)
버퍼가 더 이상 필요하지 않을 때 호출돼. buffer 인자는 이전에 __buffer__()가 반환한 memoryview 객체야. 이 메서드는 버퍼와 관련된 모든 리소스를 해제해야 해. 이 메서드는 None을 반환해야 해.
스레드 안전성: 자유 스레딩 파이썬에서 내보내기 카운터의 감소는 원자적 연산을 사용해야 해. 마지막 해제가 다른 스레드의 동시 해제와 경합할 수 있으므로 리소스 정리는 스레드 안전해야 해. 정리를 수행할 필요가 없는 버퍼 객체는 이 메서드를 구현할 필요가 없어.
버전 3.12에서 추가됨.
참고:
- PEP 688 - Making the buffer protocol accessible in Python. 파이썬
__buffer__와__release_buffer__메서드를 도입. collections.abc.Buffer: 버퍼 타입을 위한 ABC.
애너테이션 (Annotations)
함수, 클래스, 모듈은 애너테이션을 포함할 수 있어. 애너테이션은 기호(symbol)에 정보(보통 타입 힌트)를 연관시키는 방법이야.
object.__annotations__
이 속성은 객체의 애너테이션을 담고 있어. 지연 평가(lazily evaluated)되므로, 속성에 접근하면 임의의 코드가 실행되고 예외가 발생할 수 있어. 평가가 성공하면 속성은 변수 이름에서 애너테이션으로의 매핑인 딕셔너리로 설정돼.
버전 3.14에서 변경: 애너테이션은 이제 지연 평가됨.
object.__annotate__(format)
애너테이트 함수야. 속성/매개변수 이름을 그 애너테이션 값으로 매핑하는 새 딕셔너리 객체를 반환해. 애너테이션 값을 제공해야 하는 형식을 지정하는 format 매개변수를 받아. 이는 annotationlib.Format enum의 멤버 또는 enum의 멤버에 해당하는 값을 가진 정수여야 해.
애너테이트 함수가 요청된 형식을 지원하지 않으면 NotImplementedError를 발생시켜야 해. 애너테이트 함수는 항상 VALUE 형식을 지원해야 하며, 이 형식으로 호출될 때 NotImplementedError()를 발생시키면 안 돼. VALUE 형식으로 호출되면 애너테이트 함수는 NameError를 발생시킬 수 있어. 다른 형식을 요청하며 호출될 때는 NameError를 발생시키면 안 돼.
객체에 애너테이션이 없으면 __annotate__는 (삭제할 수 없으므로) 빈 딕셔너리를 반환하는 함수로 설정하기보다는 None으로 설정하는 것이 좋아.
버전 3.14에서 추가됨.
참고: PEP 649 — Deferred evaluation of annotation using descriptors. 애너테이션의 지연 평가와 __annotate__ 함수를 도입.
특수 메서드 조회 (Special method lookup)
사용자 정의 클래스의 경우, 특수 메서드의 암시적 호출은 객체의 인스턴스 딕셔너리가 아니라 객체의 타입에 정의된 경우에만 올바르게 작동한다는 보장이 있어. 그 동작이 다음 코드가 예외를 발생시키는 이유야:
>>> class C:
... pass
...
>>> c = C()
>>> c.__len__ = lambda: 5
>>> len(c)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: object of type 'C' has no len()
이 동작의 근거는 __hash__()와 __repr__() 같은, 타입 객체를 포함한 모든 객체가 구현하는 여러 특수 메서드와 관련돼. 이 메서드들의 암시적 조회가 기존 조회 과정을 사용한다면, 타입 객체 자체에서 호출될 때 실패할 거야:
>>> 1 .__hash__() == hash(1)
True
>>> int.__hash__() == hash(int)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: descriptor '__hash__' of 'int' object needs an argument
이런 식으로 클래스의 unbound 메서드를 잘못 호출하려 시도하는 것을 때때로 '메타클래스 혼동(metaclass confusion)'이라고 부르며, 특수 메서드를 조회할 때 인스턴스를 우회함으로써 피해:
>>> type(1).__hash__(1) == hash(1)
True
>>> type(int).__hash__(int) == hash(int)
True
정확성을 위해 인스턴스 속성들을 우회하는 것 외에도, 암시적 특수 메서드 조회는 일반적으로 객체의 메타클래스의 __getattribute__() 메서드조차 우회해:
>>> class Meta(type):
... def __getattribute__(*args):
... print("Metaclass getattribute invoked")
... return type.__getattribute__(*args)
...
>>> class C(object, metaclass=Meta):
... def __len__(self):
... return 10
... def __getattribute__(*args):
... print("Class getattribute invoked")
... return object.__getattribute__(*args)
...
>>> c = C()
>>> c.__len__() # Explicit lookup via instance
Class getattribute invoked
10
>>> type(c).__len__(c) # Explicit lookup via type
Metaclass getattribute invoked
10
>>> len(c) # Implicit lookup
10
이런 방식으로 __getattribute__() 메커니즘을 우회하는 것은 특수 메서드 처리에서 어느 정도의 유연성을 희생하는 대가로 인터프리터 내부의 상당한 속도 최적화 여지를 제공해. (특수 메서드는 인터프리터가 일관되게 호출하기 위해 클래스 객체 자체에 설정되어 있어야 해.)
코루틴 (Coroutines)
Awaitable 객체 (Awaitable Objects)
awaitable 객체는 일반적으로 __await__() 메서드를 구현해. async def 함수에서 반환된 코루틴 객체들은 awaitable이야.
참고
types.coroutine()로 데코레이팅된 제너레이터에서 반환된 제너레이터 이터레이터 객체도 awaitable이지만, __await__()은 구현하지 않아.
object.__await__(self)
iterator를 반환해야 해. awaitable 객체를 구현하는 데 사용해야 해. 예를 들어 asyncio.Future는 await 표현식과 호환되도록 이 메서드를 구현해. object 클래스 자체는 awaitable이 아니며 이 메서드를 제공하지 않아.
참고
언어는 __await__이 반환한 이터레이터가 생성하는 객체의 타입이나 값에 어떤 제한도 두지 않아. 이는 awaitable 객체를 관리할 비동기 실행 프레임워크(예: asyncio)의 구현에 특정한 것이기 때문이야.
버전 3.5에서 추가됨.
참고: awaitable 객체에 대한 추가 정보는 PEP 492 참고.
코루틴 객체 (Coroutine Objects)
코루틴 객체는 awaitable 객체야. 코루틴의 실행은 __await__()을 호출하고 결과를 반복함으로써 제어할 수 있어. 코루틴이 실행을 마치고 반환하면, 이터레이터는 StopIteration을 발생시키고 예외의 value 속성이 반환 값을 담아. 코루틴이 예외를 발생시키면 이터레이터가 그것을 전파해. 코루틴은 처리되지 않은 StopIteration 예외를 직접 발생시키면 안 돼.
코루틴은 아래 나열된 메서드들도 가지는데, 이는 제너레이터의 메서드와 유사해(Generator-iterator methods 참고). 하지만 제너레이터와 달리 코루틴은 반복을 직접 지원하지 않아.
코루틴은 각각 yield, send, return 값의 타입에 대해 제네릭(Generic)이야.
버전 3.5.2에서 변경: 코루틴을 두 번 이상 await하는 것은 RuntimeError야.
coroutine.send(value)
코루틴의 실행을 시작하거나 재개해. value가 None이면 이는 __await__()이 반환한 이터레이터를 전진시키는 것과 같아. value가 None이 아니면 이 메서드는 코루틴을 일시 중지시킨 이터레이터의 send() 메서드에 위임해. 결과(반환 값, StopIteration, 또는 다른 예외)는 위에서 설명한 __await__() 반환 값을 반복할 때와 같아.
coroutine.throw(value), coroutine.throw(type[, value[, traceback]])
코루틴에서 지정된 예외를 발생시켜. 이 메서드는 그러한 메서드가 있으면 코루틴을 일시 중지시킨 이터레이터의 throw() 메서드에 위임해. 그렇지 않으면 예외는 일시 중지 지점에서 발생해. 결과(반환 값, StopIteration, 또는 다른 예외)는 위에서 설명한 __await__() 반환 값을 반복할 때와 같아. 예외가 코루틴에서 잡히지 않으면 호출자에게 다시 전파돼.
버전 3.12에서 변경: 두 번째 시그니처(type[, value[, traceback]])는 deprecated이며 파이썬의 미래 버전에서 제거될 수 있어.
coroutine.close()
코루틴이 스스로 정리하고 종료하게 해. 코루틴이 일시 중지되어 있으면 이 메서드는 그러한 메서드가 있으면 코루틴을 일시 중지시킨 이터레이터의 close() 메서드에 먼저 위임해. 그런 다음 일시 중지 지점에서 GeneratorExit을 발생시켜 코루틴이 즉시 스스로 정리하게 해. 마지막으로 코루틴은 시작된 적이 없어도 실행을 마친 것으로 표시돼. 코루틴 객체는 파괴되려 할 때 위 과정을 사용해 자동으로 닫혀.
비동기 이터레이터 (Asynchronous Iterators)
*비동기 이터레이터(asynchronous iterator)*는 __anext__ 메서드에서 비동기 코드를 호출할 수 있어. 비동기 이터레이터는 async for 문에서 사용할 수 있어. object 클래스 자체는 이 메서드들을 제공하지 않아.
object.__aiter__(self)
비동기 이터레이터 객체를 반환해야 해.
object.__anext__(self)
이터레이터의 다음 값으로 이어지는 awaitable을 반환해야 해. 반복이 끝나면 StopAsyncIteration 오류를 발생시켜야 해.
비동기 이터러블 객체의 예:
class Reader:
async def readline(self):
...
def __aiter__(self):
return self
async def __anext__(self):
val = await self.readline()
if val == b'':
raise StopAsyncIteration
return val
버전 3.5에서 추가됨.
버전 3.7에서 변경: 파이썬 3.7 이전에는 __aiter__()이 비동기 이터레이터로 이어지는 awaitable을 반환할 수 있었어. 파이썬 3.7부터 __aiter__()은 비동기 이터레이터 객체를 반환해야 해. 다른 무엇이든 반환하면 TypeError 오류가 발생해.
비동기 컨텍스트 관리자 (Asynchronous Context Managers)
비동기 컨텍스트 관리자는 __aenter__와 __aexit__ 메서드에서 실행을 일시 중지할 수 있는 컨텍스트 관리자야. 비동기 컨텍스트 관리자는 async with 문에서 사용할 수 있어. object 클래스 자체는 이 메서드들을 제공하지 않아.
object.__aenter__(self)
__enter__()과 의미상 유사하며, 유일한 차이는 awaitable을 반환해야 한다는 거야.
object.__aexit__(self, exc_type, exc_value, traceback)
__exit__()과 의미상 유사하며, 유일한 차이는 awaitable을 반환해야 한다는 거야.
비동기 컨텍스트 관리자 클래스의 예:
class AsyncContextManager:
async def __aenter__(self):
await log('entering context')
async def __aexit__(self, exc_type, exc, tb):
await log('exiting context')
버전 3.5에서 추가됨.
각주 (Footnotes)
- 어떤 경우에는 통제된 조건 하에서 객체의 타입을 바꾸는 것이 가능하긴 해. 하지만 일반적으로 좋은 생각은 아니야. 잘못 처리하면 매우 이상한 동작으로 이어질 수 있으니까.
__hash__(),__iter__(),__reversed__(),__contains__(),__class_getitem__(),__fspath__()메서드는 이에 대한 특별 처리를 가져. 다른 것들은 여전히TypeError를 발생시키지만,None이 호출 가능하지 않다는 동작에 의존하여 그렇게 할 수 있어.- 여기서 "지원하지 않음"은 클래스에 그런 메서드가 없거나 메서드가
NotImplemented를 반환한다는 뜻이야. 오른쪽 피연산자의 반영 메서드로 강제로 폴백하고 싶다면 메서드를None으로 설정하지 마. 그렇게 하면 오히려 그런 폴백을 명시적으로 차단하는 반대 효과가 있어. - 같은 타입의 피연산자에 대해서는 non-reflected 메서드(예:
__add__())가 실패하면 연산이 지원되지 않는 것으로 가정되며, 그래서 reflected 메서드가 호출되지 않아. - 오른쪽 피연산자의 타입이 왼쪽 피연산자 타입의 하위 클래스라면, reflected 메서드가 우선권을 가져 하위 클래스가 조상의 연산을 오버라이드할 수 있게 해.
더 알아보기
데이터 모델과 연결되는 주제를 더 깊이 보고 싶다면 이 문서들이 도움이 돼:
- 파이썬 실행 모델 (Execution Model) - 이름이 어떻게 바인딩되고 스코프가 어떻게 동작하는지
- 표준형 (Built-in Types) - 각 내장 타입의 특수 메서드와 프로토콜
- Descriptor 사용법 안내 (Descriptor HowTo) - descriptor 프로토콜의 자세한 동작 원리
- typing 모듈의 Generics - 제네릭 타입과
__class_getitem__활용 - abc 모듈 (추상 기본 클래스) -
__instancecheck__/__subclasscheck__로 가상 하위 클래스 만들기 - PEP 649 - 애너테이션 지연 평가와
__annotate__