Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python 개선 제안 한국어 번역

PEP 576 – 내장 함수 클래스 합리화

Author:
Mark Shannon <mark at hotpy.org>
BDFL-Delegate:
Petr Viktorin
Status:
Withdrawn
Type:
Standards Track
Created:
10-May-2018
Python-Version:
3.8
Post-History:
17-May-2018, 23-Jun-2018, 08-Jul-2018, 29-Mar-2019

Table of Contents

번역·라이선스 안내

이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판

초록

CPython 내부에서 사용하는 “FastcallKeywords” 규약을 서드파티 코드에 공개하고, inspect 모듈이 덕 타이핑을 사용하도록 하십시오. 이를 결합하면 서드파티 C 확장과 Cython과 같은 도구가 내장 함수 및 Python 함수와 동일한 호출 규약을 사용하는 객체를 생성할 수 있으므로, len이나 print와 같은 내장 함수와 동등한 성능을 얻을 수 있습니다.

기존 코드의 성능이 소폭 향상될 것으로 예상합니다.

동기

현재 서드파티 모듈 작성자는 C로 함수를 구현할 때 딜레마에 직면합니다. 기존 내장 함수 또는 메서드 클래스 중 하나를 사용하거나, C로 자체 사용자 정의 클래스를 구현해야 합니다. 첫 번째 선택지는 호출 가능 객체의 내부에 접근할 수 있는 능력을 잃게 합니다. 두 번째 선택지는 추가적인 유지 관리 부담을 초래하며, 더 중요하게는 성능에 상당한 부정적인 영향을 미칩니다.

이 PEP는 서드파티 C 모듈 작성자와 Cython과 같은 도구가 CPython 내부에서 내장 함수 및 메서드에 사용되는 더 빠른 호출 규약을 활용하도록 하며, Python으로 구현된 함수에 비해 기능을 잃지 않도록 하는 것을 목표로 합니다.

인트로스펙션

inspect 모듈은 호출 가능 객체를 인트로스펙션할 때 덕 타이핑을 완전히 지원합니다.

inspect.Signature.from_callable() 함수는 호출 가능 객체의 시그니처를 계산합니다. 객체에 __signature__ 속성이 있으면 inspect.Signature.from_callable() 은 단순히 해당 속성을 반환합니다. 덕 타이핑을 더욱 지원하기 위해 호출 가능 객체에 __text_signature__가 있으면 __signature__를 이를 바탕으로 생성합니다.

즉, 서드파티 내장 함수는 적절한 경우 __text_signature__를 구현하고, 필요한 경우 더 비용이 큰 __signature__를 구현할 수 있습니다.

서드파티 호출 가능 객체에 대한 효율적인 호출

현재 대부분의 호출은 “FastcallKeywords” 내부 호출 규약을 사용하여 사용자 정의 코드에서 functions 및 method_descriptors로 디스패치됩니다. 이 PEP는 이 호출 규약을 C 함수 포인터를 통해 구현할 것을 제안합니다. 이 바이너리 인터페이스를 구현하는 서드파티 호출 가능 객체는 내장 함수만큼 빠르게 호출될 가능성이 있습니다.

호출 가능 객체 클래스를 베이스 클래스로 사용하는 행위에 대한 금지 지속

현재 새 클래스의 베이스 클래스로 function, method 또는 method_descriptor를 사용하려는 모든 시도는 TypeError로 실패합니다. 서브클래스가 __call__ 메서드를 재정의할 때 오류가 발생하는 것을 방지하므로 이러한 동작이 바람직합니다. 호출 가능 객체를 서브클래스화할 수 있다면 function 또는 method_descriptor를 호출할 때마다 __call__ 메서드가 재정의되지 않았는지 추가로 확인해야 합니다. 추가 호출 메커니즘을 공개하면 오류가 발생할 가능성이 더 커집니다. 결과적으로 추가 호출 인터페이스를 구현하는 서드파티 클래스는 베이스 클래스로 사용할 수 없습니다.

새 클래스 및 기존 클래스의 변경 사항

Python에 노출되는 변경 사항

  1. 새로운 내장 클래스인 builtin_function이 추가됩니다.
  2. types.BuiltinFunctionTypebuiltin_function_or_method이 아니라 builtin_function을 참조합니다.
  3. builtin_function클래스의 인스턴스는 builtin_function_or_method__module__속성을 유지하고, func_modulefunc_globals속성을 새로 갖게 됩니다. func_module은 함수가 속한 모듈에 액세스할 수 있도록 합니다. 이는 단순히 모듈의 이름을 반환하는 __module__속성과는 다르다는 점에 유의하십시오. func_globals속성은 func_module.__dict__와 동등하며, 동일한 이름을 가진 Python 함수 속성을 모방하기 위해 제공됩니다.
  4. method_descriptor인스턴스를 해당 인스턴스가 속한 클래스의 인스턴스에 바인딩하면 builtin_function_or_method대신 bound_method가 생성됩니다. 이는 이제 method_descriptors가 Python 함수의 동작을 더욱 유사하게 모방한다는 의미입니다. 즉, [].appendbuiltin_function_or_method대신 bound_method가 됩니다.

C API 변경 사항

  1. 내장 함수를 생성하기 위한 새로운 함수 PyBuiltinFunction_New(PyMethodDef *ml, PyObject *module)가 추가됩니다.
  2. PyCFunction_NewEx()PyCFunction_New()는 사용 중단되며, 가능한 경우 PyBuiltinFunction을 반환하고, 그렇지 않으면 builtin_function_or_method를 반환합니다.

C API 및 ABI에서 하위 호환성 유지

제안된 변경 사항은 API 및 ABI 수준 모두에서 완전한 하위 호환성과 상위 호환성을 제공합니다.

내부 C 변경 사항

typeobject.tp_flags필드에 두 개의 새로운 플래그가 허용됩니다. 이는 Py_TPFLAGS_EXTENDED_CALLPy_TPFLAGS_FUNCTION_DESCRIPTOR입니다.

Py_TPFLAGS_EXTENDED_CALL

Py_TPFLAGS_EXTENDED_CALL을 설정하는 모든 내장 클래스의 경우, 이 내장 클래스에 대응하는 C 구조체는 다음과 같이 정의되는 PyExtendedCallable 구조체로 시작해야 합니다.:

typedef PyObject *(*extended_call_ptr)(PyObject *callable, PyObject** args,
                   int positional_argcount, PyTupleObject* kwnames);

typedef struct {
    PyObject_HEAD
    extended_call_ptr ext_call;
} PyExtendedCallable;

Py_TPFLAGS_EXTENDED_CALL을 설정하는 클래스는 베이스 클래스로 사용될 수 없으며, 어떤 파이썬 코드가 이를 베이스 클래스로 사용하려 하면 TypeError가 발생합니다.

Py_TPFLAGS_FUNCTION_DESCRIPTOR

이 플래그가 내장 클래스 F에 설정되어 있으면, 해당 클래스의 인스턴스는 클래스 속성으로 사용될 때 파이썬 함수와 동일하게 동작할 것으로 기대됩니다. 구체적으로, 이는 C.m이 내장 클래스 F의 인스턴스이고 cC의 인스턴스일 때, c.m의 값이 C.mc를 바인딩하는 바운드 메서드여야 함을 의미합니다. 이 플래그가 없다면, 커스텀 호출 가능 객체가 파이썬 함수처럼 동작하면서 동시에 파이썬 함수나 내장 함수만큼 효율적일 수는 없을 것입니다.

기존 C 구조체에 대한 변경

function, method_descriptor, method 클래스는 이에 대응하는 구조체가 PyExtendedCallable 구조체로 시작하도록 변경됩니다.

새로운 확장 호출 인터페이스를 사용하는 서드파티 내장 클래스

파이썬 함수 및 내장 함수와 대등한 호출 성능을 내려면, 서드파티 호출 가능 객체는 tp_flagsPy_TPFLAGS_EXTENDED_CALL 비트를 설정하고, 이에 대응하는 C 구조체가 PyExtendedCallable로 시작하도록 해야 합니다. Py_TPFLAGS_EXTENDED_CALL 비트가 설정된 모든 내장 클래스는 tp_call 함수도 구현해야 하며, 그 동작이 ext_call 함수와 일관되도록 해야 합니다.

이러한 변경이 성능에 미치는 영향

함수 포인터를 각 호출 가능 객체의 클래스가 아니라 각 호출 가능 객체 자체에 추가하면, 디스패치 함수(인자를 정리하고 오류 검사를 수행하는 코드)의 선택을 호출 시점이 아니라 호출 가능 객체가 생성되는 시점에 할 수 있게 됩니다. 이는 인터프리터의 호출 지점과 피호출자의 실행 사이에서 실행되는 명령어 수를 줄여줄 것입니다.

대안 제안

PEP 580은 이 PEP와 동일한 문제를 해결하기 위한 대안적 접근 방식입니다.

참조 구현

초안 구현은 https://github.com/markshannon/cpython/tree/pep-576-minimal 에서 찾을 수 있습니다