PEP 511 – 코드 변환기 API
- Author:
- Victor Stinner <vstinner at python.org>
- Status:
- Rejected
- Type:
- Standards Track
- Created:
- 04-Jan-2016
- Python-Version:
- 3.6
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
거부 통지
이 PEP는 작성자에 의해 거부되었습니다.
이 PEP는 일반 Python 언어와 유사하지만 호환되지 않는 새로운 Python 계열 프로그래밍 언어를 축복하는 것으로 받아들여졌습니다. Python과 호환되지 않는 구문을 장려하지 않기로 결정했습니다.
이 PEP는 새로운 Python 기능을 실험하기 위한 유용한 도구로도 여겨졌지만, PEP 없이도 importlib 훅만 사용하여 이미 실험할 수 있습니다. 기능이 유용해지면 서드파티 Python 모듈에 의존하는 대신 Python의 직접적인 일부가 되어야 합니다.
마지막으로 이 PEP는 FAT Python 최적화 프로젝트에 의해 추진되었지만, 유의미한 속도 향상을 입증할 수 없었고 가장 고급스럽고 복잡한 최적화를 구현할 시간도 부족했기 때문에 2016년에 중단되었습니다.
초록
바이트코드 및 AST 변환기를 등록하는 API를 제안합니다. .pyc 파일 이름을 변경하는 -o OPTIM_TAG 명령줄 옵션도 추가하며, -o noopt는 피프홀 최적화기를 비활성화합니다. 가져오기 시 .pyc 파일이 없고 코드를 변환하는 데 필요한 코드 변환기도 없으면 ImportError 예외를 발생시킵니다. 코드를 미리 변환하여 .pyc 파일에서 로드하는 경우에는 코드 변환기가 필요하지 않습니다.
근거
Python은 코드를 변환하는 표준 방법을 제공하지 않습니다. 코드를 변환하는 프로젝트는 다양한 훅을 사용합니다. MacroPy 프로젝트는 가져오기 훅을 사용합니다. 자체 모듈 찾기를 sys.meta_path에 추가하여 AST 변환기를 연결합니다. 또 다른 방법은 내장 compile() 함수를 몽키 패치하는 것입니다. 코드 변환기를 연결하는 방법은 이보다 더 많습니다.
Python 3.4는 importlib.abc.SourceLoader에 compile_source() 메서드를 추가했습니다. 그러나 코드 변환은 단순히 모듈을 가져오는 것보다 범위가 넓습니다. 아래에 설명된 사용 사례를 참조하십시오.
최적화기나 전처리기를 작성하는 것은 이 PEP의 범위를 벗어납니다.
사용 사례 1: AST 최적화기
추상 구문 트리(AST)를 변환하는 것은 최적화기를 구현하는 편리한 방법입니다. AST에는 더 많은 정보가 포함되어 있고 더 높은 수준이므로 바이트코드보다 AST를 대상으로 작업하는 것이 더 쉽습니다.
최적화를 미리 수행할 수 있으므로 복잡하지만 느린 최적화를 구현할 수 있습니다.
AST 최적화기로 구현할 수 있는 최적화의 예는 다음과 같습니다.
- Copy propagation:
x=1; y=x를x=1; y=1로 바꿉니다. - Constant folding:
1+1을2로 바꿉니다. - Dead code elimination
가드(PEP 510을 참조하십시오)를 사용하면 훨씬 더 다양한 최적화를 구현할 수 있습니다. 예:
- 이터러블 단순화: 이터러블로 사용될 때
range(3)을(0, 1, 2)로 바꿉니다. - 루프 언롤링
- 순수 내장 함수를 호출합니다.
len("abc")을3으로 대체합니다. - 사용된 내장 심볼을 상수로 복사합니다.
- fatoptimizer에 구현된 최적화도 참조하십시오. 이는 Python 3.6용 정적 최적화기입니다.
다음 이슈는 AST 최적화기를 사용하여 구현할 수 있습니다.
- 이슈 #1346238: AST를 위한 상수 폴딩 최적화 패스입니다.
- 이슈 #2181: 함수 끝에서 지역 변수를 최적화하여 제거합니다.
- 이슈 #2499: 상수에 대한 단항 + 및 not을 폴딩합니다.
- 이슈 #4264: 패치: list.append를 호출하는 대신 LIST_APPEND를 사용하도록 코드를 최적화합니다.
- 이슈 #7682: 상수 표현식을 사용하는 if를 최적화합니다.
- 이슈 #10399: AST 최적화: 함수 호출을 인라인합니다.
- 이슈 #11549: AST 최적화기를 구축하고 일부 기능을 peephole 최적화기에서 이동합니다.
- 이슈 #17068: 상수 문자열을 위한 peephole 최적화입니다.
- 이슈 #17430: 누락된 peephole 최적화입니다.
사용법 2: 전처리기입니다.
AST 변환기를 사용하여 전처리기를 쉽게 구현할 수 있습니다. 전처리기에는 다양하고 서로 다른 용도가 있습니다.
몇 가지 예는 다음과 같습니다.
- 프로덕션에서 코드를 더 빠르게 실행할 수 있도록 어서션 및 로그와 같은 디버그 코드를 제거합니다.
- 꼬리 호출 최적화
- 프로파일링 코드를 추가합니다.
- 지연 평가: lazy_python (바이트코드 변환기) 및 MacroPy의 lazy 매크로 (AST 변환기)를 참조하십시오.
- 딕셔너리 리터럴을 collection.OrderedDict 인스턴스로 변경합니다.
- 상수를 선언합니다. codetransformer의 @asconstants를 참조하십시오.
- SQL 쿼리와 같은 도메인 특화 언어(DSL)입니다. Python 언어 자체를 수정할 필요는 없습니다. SQL과 같은 DSL을 구현하려는 이전 시도인 PEP 335 - Overloadable Boolean Operators은 거부되었습니다.
- 함수형 언어의 패턴 매칭입니다.
- 문자열 보간이지만, PEP 498은 Python 3.6에 병합되었습니다.
MacroPy는 예제와 사용 사례의 긴 목록을 제공합니다.
이 PEP는 새로운 코드 변환기를 추가하지 않습니다. 코드 변환기를 사용하려면 외부 모듈이 필요하며 이를 수동으로 등록해야 합니다.
PyXfuscator도 참조하십시오. Python 난독화기, 역난독화기 및 사용자 지원 디컴파일러입니다.
사용법 3: 모든 최적화 비활성화
Ned Batchelder는 피프홀 최적화기가 코드 커버리지 구현을 더 어렵게 만들기 때문에 이를 비활성화하는 옵션을 추가해 달라고 요청했습니다. python-ideas 메일링 리스트의 논의인 모든 피프홀 최적화 비활성화를 참조하십시오.
이 PEP에서는 피프홀 최적화기를 비활성화하는 새로운 -o noopt 명령줄 옵션을 추가합니다. Python에서는 다음과 같이 간단합니다.:
sys.set_code_transformers([])
다음 Issue #2506을 해결합니다. 최적화를 비활성화하는 메커니즘을 추가합니다.
사용법 4: Python으로 새로운 바이트코드 최적화기 작성
Python 3.6은 피프홀 최적화기를 사용하여 코드를 최적화합니다. 정의상 피프홀 최적화기는 코드를 좁은 관점에서 보므로 기본적인 최적화만 구현할 수 있습니다. 최적화기는 바이트코드를 다시 작성합니다. C로 작성되어 있으므로 개선하기 어렵습니다.
이 PEP를 통해 순수 Python으로 새로운 바이트코드 최적화기를 구현하고 새로운 최적화를 실험할 수 있게 됩니다.
상수 폴딩과 같은 일부 최적화는 AST에서 구현하는 것이 더 쉽지만, 바이트코드에서의 최적화도 여전히 유용합니다. 예를 들어 AST를 바이트코드로 컴파일할 때 컴파일러가 단순하여 아무것도 최적화하려 하지 않기 때문에 불필요한 점프가 생성될 수 있습니다.
사용 사례
이 절에서는 코드 변환기를 언제 어떻게 사용할지 설명하는 사용 사례의 예를 제시합니다.
대화형 인터프리터
Python에서 인기가 있으며 Python을 시연하는 데 흔히 사용되는 대화형 인터프리터와 함께 코드 변환기를 사용할 수 있게 됩니다.
코드는 런타임에 변환되므로 처리 비용이 높은 코드 변환기를 사용하면 인터프리터가 느려질 수 있습니다.
변환된 패키지 빌드
변환된 코드의 패키지를 빌드할 수 있게 됩니다.
변환기에는 구성이 있을 수 있습니다. 구성은 패키지에 저장되지 않습니다.
패키지의 모든 .pyc 파일은 동일한 코드 변환기와 동일한 변환기 구성으로 변환해야 합니다.
서로 다른 최적화기 태그를 사용하여 서로 다른 .pyc 파일을 빌드할 수 있습니다. 예를 들어 기본 구성에는 fat을 사용하고, 함수 인라이닝이 활성화된 다른 구성에는 fat_inline을 사용합니다.
패키지에는 서로 다른 최적화기 태그를 가진 .pyc 파일이 포함될 수 있습니다.
변환된 .pyc 파일이 포함된 패키지 설치
변환된 .pyc 파일이 포함된 패키지를 설치할 수 있게 됩니다.
패키지에 포함된 모든 최적화기 태그의 .pyc 파일을 설치하며, 현재 최적화기 태그에 해당하는 파일만 설치하지는 않습니다.
패키지 설치 시 .pyc 파일 빌드
패키지에 현재 최적화 태그의 .pyc 파일이 전혀 없거나(또는 일부 .pyc 파일이 누락된 경우), 설치 중에 .pyc 파일이 생성됩니다.
최적화 태그의 코드 변환기가 필요합니다. 그렇지 않으면 설치가 오류와 함께 실패합니다.
변환된 코드 실행
변환된 코드를 실행할 수 있게 됩니다.
현재 최적화 태그의 .pyc 파일이 없고 코드를 변환하는 데 필요한 코드 변환기도 없으면 가져올 때 ImportError예외를 발생시킵니다.
여기서 중요한 점은 필요한 모든 .pyc 파일을 이미 사용할 수 있다면 변환된 코드를 실행하는 데 코드 변환기가 필요하지 않다는 것입니다.
코드 변환기 API
코드 변환기는 ast_transformer() 및/또는 code_transformer() 메서드(API는 아래에 설명되어 있음)와 name 속성을 가진 클래스입니다.
효율성을 위해 아무 작업도 수행하지 않는 code_transformer() 또는 ast_transformer() 메서드는 정의하지 마십시오.
name 속성(str)은 최적화 프로그램을 식별하는 데 사용하는 짧은 문자열이어야 합니다. 이 속성은 .pyc 파일 이름을 만드는 데 사용됩니다. 이름에는 점('.'), 대시('-') 또는 디렉터리 구분자가 포함되어서는 안 됩니다. 점은 .pyc 파일 이름에서 필드를 구분하는 데 사용되고, 대시는 코드 변환기 이름을 연결하여 최적화 태그를 만드는 데 사용됩니다.
Note
AST 변환기를 사용하여 가져올 때 모듈을 변환하는 경우 모듈의 완전한 이름을 context에 전달할 수 있으면 좋겠지만, PyParser_ASTFromStringObject()에서는 해당 정보를 사용할 수 없는 것처럼 보입니다.
code_transformer() 메서드
프로토타입:
def code_transformer(self, code, context):
...
new_code = ...
...
return new_code
매개변수:
- code: 코드 객체
- context:
int인 optimize 속성을 가진 객체이며, 최적화 수준(0, 1 또는 2)을 나타냅니다. optimize 속성의 값은compile()함수의 optimize 매개변수에서 가져오며, 기본적으로sys.flags.optimize와 같습니다.
Python의 각 구현은 context 에 추가 속성을 넣을 수 있습니다. 예를 들어 CPython에서 context 에는 다음 속성도 있습니다.
- interactive (
bool): 대화형 모드이면 true입니다.
XXX 플래그를 더 추가해야 합니까?
XXX 플래그 정수를 하위 네임스페이스나 특정 속성으로 대체해야 합니까?
이 메서드는 코드 객체를 반환해야 합니다.
코드 변환기는 바이트코드로 컴파일한 후에 실행됩니다.
ast_transformer() 메서드
프로토타입:
def ast_transformer(self, tree, context):
...
return tree
매개변수:
- tree: AST 트리
- context:
filename속성(str)을 가진 객체
AST 트리를 반환해야 합니다. AST 트리를 제자리에서 수정하거나 새로운 AST 트리를 생성할 수 있습니다.
AST 변환기는 파서가 AST를 생성한 후, 바이트코드로 컴파일하기 전에 호출됩니다. 향후 context에 새 속성이 추가될 수 있습니다.
변경 사항
간단히 말해 다음을 추가합니다:
- -o OPTIM_TAG 명령줄 옵션
- sys.implementation.optim_tag
- sys.get_code_transformers()
- sys.set_code_transformers(transformers)
- ast.PyCF_TRANSFORMED_AST
코드 변환기를 가져오고 설정하는 API
코드 변환기를 등록하는 새 함수를 추가합니다:
sys.set_code_transformers(transformers): 코드 변환기 목록을 설정하고sys.implementation.optim_tag를 업데이트합니다.sys.get_code_transformers(): 코드 변환기 목록을 가져옵니다.
코드 변환기의 순서가 중요합니다. 변환기 A를 실행한 후 변환기 B를 실행하는 경우와 변환기 B를 실행한 후 변환기 A를 실행하는 경우에는 서로 다른 출력이 나올 수 있습니다.
새 코드 변환기를 앞에 추가하는 예:
transformers = sys.get_code_transformers()
transformers.insert(0, new_cool_transformer)
sys.set_code_transformers(transformers)
모든 AST 변환기는 순차적으로 실행되며(예: 두 번째 변환기는 첫 번째 변환기의 입력을 받습니다), 그 후 모든 바이트코드 변환기가 순차적으로 실행됩니다.
최적화 태그
변경 사항:
sys.implementation.optim_tag(str): 최적화 태그를 추가합니다. 기본 최적화 태그는'opt'입니다.sys.implementation.optim_tag를 설정하는 새로운-o OPTIM_TAG명령줄 옵션을 추가하십시오.
importlib의 변경 사항:
importlib는 항상opt를 사용하는 대신sys.implementation.optim_tag를 사용하여 모듈을 가져올 때 사용할.pyc파일 이름을 구성합니다. 코드를 간소화하기 위해 기본 최적화 태그'opt'의 최적화 수준0에 대한 특수한 경우도 제거합니다.- 모듈을 로드할 때
.pyc파일이 없지만.py파일을 사용할 수 있는 경우, 코드 최적화 프로그램의 최적화 태그가 현재 태그와 동일할 때만.py파일을 사용하고, 그렇지 않으면ImportError예외를 발생시킵니다.
모듈을 가져오기 위해 .py 파일을 컴파일할 수 있는지 결정하는 use_py() 함수의 의사 코드:
def transformers_tag():
transformers = sys.get_code_transformers()
if not transformers:
return 'noopt'
return '-'.join(transformer.name
for transformer in transformers)
def use_py():
return (transformers_tag() == sys.implementation.optim_tag)
sys.get_code_transformers()의 순서가 중요합니다. 예를 들어 fat 변환기 다음에 pythran 변환기를 실행하면 최적화 태그 fat-pythran이 생성됩니다.
기본 최적화 태그('opt')에서는 importlib 모듈의 동작이 변경되지 않습니다.
피프홀 최적화기
기본적으로 sys.implementation.optim_tag은 opt이며, sys.get_code_transformers()는 코드 변환기 하나의 목록, 즉 피프홀 최적화기(바이트코드를 최적화함)를 반환합니다.
피프홀 최적화기를 비활성화하려면 -o noopt를 사용하십시오. 이 경우 최적화기 태그는 noopt이며 코드 변환기는 등록되지 않습니다.
-o opt옵션을 사용해도 아무런 효과가 없습니다.
AST 개선 사항
AST 변환기의 구현을 간소화하기 위한 개선 사항입니다.
- 변환된 AST를 가져오려면 새 컴파일러 플래그
PyCF_TRANSFORMED_AST를 추가합니다.PyCF_ONLY_AST는 변환기 실행 전의 AST를 반환합니다.
예시
.pyc 파일 이름
os 모듈의 .pyc 파일 이름 예시입니다.
기본 최적화기 태그가 'opt'일 때:
| .pyc 파일 이름 | 최적화 수준 |
|---|---|
os.cpython-36.opt-0.pyc |
0 |
os.cpython-36.opt-1.pyc |
1 |
os.cpython-36.opt-2.pyc |
2 |
'fat'최적화기 태그일 때:
| .pyc 파일 이름 | 최적화 수준 |
|---|---|
os.cpython-36.fat-0.pyc |
0 |
os.cpython-36.fat-1.pyc |
1 |
os.cpython-36.fat-2.pyc |
2 |
바이트코드 변환기
모든 문자열을 "Ni! Ni! Ni!"로 치환하는 무시무시한 바이트코드 변환기입니다:
import sys
import types
class BytecodeTransformer:
name = "knights_who_say_ni"
def code_transformer(self, code, context):
consts = ['Ni! Ni! Ni!' if isinstance(const, str) else const
for const in code.co_consts]
return types.CodeType(code.co_argcount,
code.co_kwonlyargcount,
code.co_nlocals,
code.co_stacksize,
code.co_flags,
code.co_code,
tuple(consts),
code.co_names,
code.co_varnames,
code.co_filename,
code.co_name,
code.co_firstlineno,
code.co_lnotab,
code.co_freevars,
code.co_cellvars)
# replace existing code transformers with the new bytecode transformer
sys.set_code_transformers([BytecodeTransformer()])
# execute code which will be transformed by code_transformer()
exec("print('Hello World!')")
출력:
Ni! Ni! Ni!
AST 변환기
바이트코드 변환기 예제와 마찬가지로, AST 변환기도 모든 문자열을 "Ni! Ni! Ni!"로 치환합니다:
import ast
import sys
class KnightsWhoSayNi(ast.NodeTransformer):
def visit_Str(self, node):
node.s = 'Ni! Ni! Ni!'
return node
class ASTTransformer:
name = "knights_who_say_ni"
def __init__(self):
self.transformer = KnightsWhoSayNi()
def ast_transformer(self, tree, context):
self.transformer.visit(tree)
return tree
# replace existing code transformers with the new AST transformer
sys.set_code_transformers([ASTTransformer()])
# execute code which will be transformed by ast_transformer()
exec("print('Hello World!')")
출력:
Ni! Ni! Ni!
기타 Python 구현
모든 Python 구현에서 PEP 511을 구현해야 하지만, 바이트코드와 AST는 표준화되어 있지 않습니다.
그런데 CPython의 마이너 버전 사이에서도 AST API에 변경 사항이 있습니다. 차이점이 있지만, 사소한 차이뿐입니다. 예를 들어 Python 2.7과 Python 3.5에서 작동하는 AST 변환기를 작성하는 일은 상당히 쉽습니다.
논의
- [Python-ideas] PEP 511: 코드 변환기 API (2016년 1월)
- [Python-Dev] Python으로 구현된 AST 최적화기 (2012년 8월)
선행 사례
AST 최적화기
이슈 #17515 “Add sys.setasthook() to allow to use a custom AST” optimizer는 코드 변환기 API의 첫 번째 시도였지만 AST에만 국한되었습니다.
2015년에 Victor Stinner은 가드를 사용하여 함수를 전문화하는 AST 최적화기인 fatoptimizer 프로젝트를 작성했습니다.
2014년에 Kevin Conway는 PyCC 최적화기를 만들었습니다.
2012년에 Victor Stinner은 다양한 최적화를 구현하는 AST 최적화기인 astoptimizer 프로젝트를 작성했습니다. 무언가 변경될 경우 최적화를 비활성화하는 가드가 사용되지 않으므로, 가장 흥미로운 최적화는 Python의 의미 체계를 깨뜨립니다.
2011년에 Eugene Toder는 새로운 AST 최적화기에서 일부 피프홀 최적화를 다시 작성하자고 제안했습니다. 이슈 #11549, Build-out an AST optimizer, moving some functionality out of the peephole optimizer입니다. 이 패치는 ast.Lit을 추가합니다(ast.Literal로 이름을 바꾸자는 제안이 있었습니다).
Python 전처리기
- MacroPy: MacroPy는 Python 프로그래밍 언어에서 구문 매크로를 구현한 것입니다. MacroPy는 Python 프로그램을 가져올 때 사용자 정의 함수(매크로)가 Python 프로그램의 추상 구문 트리(AST)에 변환을 수행할 수 있는 메커니즘을 제공합니다.
- pypreprocessor:
#define및#ifdef와 같은 C 스타일 전처리기 지시문을 Python에서 사용할 수 있습니다.
바이트코드 변환기
- codetransformer:
ast모듈의NodeTransformer에서 영감을 받은 CPython용 바이트코드 변환기입니다. - byteplay: Byteplay를 사용하면 Python 코드 객체를 쉽게 다룰 수 있는 동등한 객체로 변환할 수 있으며, 해당 객체를 다시 실제로 실행 가능한 Python 코드 객체로 변환할 수도 있습니다. Python 함수에 기상천외한 변환을 적용하는 데 유용하며, Python 바이트코드의 복잡한 세부 사항을 배우는 데에도 유용합니다. 다음 byteplay 문서를 참조하십시오.
다음도 참조하십시오:
Copyright
This document has been placed in the public domain.