Python의 구문 에 새 문 (예 print
: raise
,, with
)을 추가 할 수 있습니까 ?
허용하라고 ..
mystatement "Something"
또는,
new_if True:
print "example"
해야 한다면 그다지 많지 않지만 가능하다면 (파이썬 인터프리터 코드를 수정 하지 않음 )
Python의 구문 에 새 문 (예 print
: raise
,, with
)을 추가 할 수 있습니까 ?
허용하라고 ..
mystatement "Something"
또는,
new_if True:
print "example"
해야 한다면 그다지 많지 않지만 가능하다면 (파이썬 인터프리터 코드를 수정 하지 않음 )
답변:
유용한 정보 -Python 내부 : Python에 새 문 추가 , 여기에 인용 :
이 기사는 파이썬의 프런트 엔드가 어떻게 작동하는지 더 잘 이해하기위한 시도입니다. 문서와 소스 코드를 읽는 것만으로도 약간 지루할 수 있으므로 여기서 실습 접근 방식을 취하고 있습니다. until
Python에 명령문을 추가하겠습니다 .
이 기사의 모든 코딩은 Python Mercurial 저장소 미러 의 최첨단 Py3k 브랜치에 대해 수행되었습니다 .
until
문Ruby와 같은 일부 언어에는 ( 는)를 until
보완 하는 문이 있습니다. Ruby에서는 다음과 같이 작성할 수 있습니다.while
until num == 0
while num != 0
num = 3
until num == 0 do
puts num
num -= 1
end
그리고 다음과 같이 인쇄됩니다.
3
2
1
그래서 파이썬에 비슷한 기능을 추가하고 싶습니다. 즉, 다음과 같이 작성할 수 있습니다.
num = 3
until num == 0:
print(num)
num -= 1
이 기사는 until
파이썬에 문장을 . 이러한 진술이 코드를 더 명확하게 해줄 것이라고 생각하고이 기사에서는 추가가 얼마나 쉬운 지 보여 주지만, 저는 파이썬의 미니멀리즘 철학을 완전히 존중합니다. 여기서 제가하려는 것은 파이썬의 내부 작동에 대한 통찰력을 얻는 것입니다.
Python은라는 사용자 지정 파서 생성기를 사용합니다 pgen
. 이것은 파이썬 소스 코드를 파스 트리로 변환하는 LL (1) 파서입니다. 파서 생성기에 대한 입력은 파일입니다 Grammar/Grammar
[1] 입니다. 이것은 Python의 문법을 지정하는 간단한 텍스트 파일입니다.
[1] : 여기서부터 Python 소스의 파일에 대한 참조는 Python을 빌드하기 위해 configure 및 make를 실행하는 디렉토리 인 소스 트리의 루트에 상대적으로 제공됩니다.
문법 파일을 두 가지 수정해야합니다. 첫 번째는 until
명령문에 대한 정의를 추가하는 것 입니다. while
문이 정의 된 위치 ( while_stmt
)를 발견 하고 until_stmt
아래에 추가했습니다 [2] :
compound_stmt: if_stmt | while_stmt | until_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated
if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
while_stmt: 'while' test ':' suite ['else' ':' suite]
until_stmt: 'until' test ':' suite
[2] : 이것은 내가 익숙하지 않은 소스 코드를 수정할 때 사용하는 일반적인 기술인 유사성에 의한 작업을 보여줍니다 . 이 원칙은 모든 문제를 해결하지는 않지만 확실히 프로세스를 쉽게 할 수 있습니다. 해야 할 모든 일을 while
위해해야하기 때문에until
하므로 꽤 좋은 지침이됩니다.
의 else
정의에서이 절 을 제외하기로 결정했습니다 until
. 단지 약간 다르게 만들기 위해서입니다 (솔직히else
루프 절을 파이썬의 Zen과 잘 맞지 않는다고 생각하기 때문입니다).
두 번째 변경 사항은 위의 스 니펫에서 볼 수 있듯이을 compound_stmt
포함 하도록 규칙을 수정하는 것입니다 until_stmt
. 바로 직후while_stmt
다시.
make
수정 후 실행 Grammar/Grammar
하면 pgen
프로그램이 실행되어 재생성 Include/graminit.h
되고Python/graminit.c
한 다음 여러 파일이 다시 컴파일됩니다.
Python 파서가 구문 분석 트리를 만든 후이 트리는 AST로 변환됩니다. AST는 작업하기 가 훨씬 더 간단하기 때문입니다. 는 컴파일 프로세스의 후속 단계에서 때문입니다.
그래서 우리는 Parser/Python.asdl
파이썬의 AST의 구조를 정의하는 곳 을 방문 하고 우리의 새로운 until
문장을 위한 AST 노드를 추가 할 것입니다 while
.
| While(expr test, stmt* body, stmt* orelse)
| Until(expr test, stmt* body)
이제를 실행 make
하면 여러 파일을 컴파일하기 전에 Parser/asdl_c.py
AST 정의 파일에서 C 코드를 생성하기 위해 실행됩니다. 이것은 Grammar/Grammar
프로그래밍을 단순화하기 위해 미니 언어 (즉, DSL)를 사용하는 Python 소스 코드의 또 다른 예입니다. 또한 Parser/asdl_c.py
은 Python 스크립트이므로 일종의 부트 스트랩은 - 처음부터 파이썬을 구축, 파이썬은 이미 사용할 수 있습니다.
Parser/asdl_c.py
새로 정의 된 AST 노드 (파일 Include/Python-ast.h
및 Python/Python-ast.c
) 를 관리하기위한 코드를 생성하는 동안 관련 구문 분석 트리 노드를 수동으로 변환하는 코드를 작성해야합니다. 이것은 파일에서 수행됩니다 Python/ast.c
. 여기에서 ast_for_stmt
명령문에 대한 구문 분석 트리 노드를 AST 노드로 변환 하는 함수가 있습니다 . 다시 한 번, 오랜 친구의 안내에 따라 복합 명령문을 처리하기 위해 while
큰 항목으로 바로 이동 switch
하고 다음에 대한 절을 추가합니다 until_stmt
.
case while_stmt:
return ast_for_while_stmt(c, ch);
case until_stmt:
return ast_for_until_stmt(c, ch);
이제 우리는 ast_for_until_stmt
. 여기있어:
static stmt_ty
ast_for_until_stmt(struct compiling *c, const node *n)
{
/* until_stmt: 'until' test ':' suite */
REQ(n, until_stmt);
if (NCH(n) == 4) {
expr_ty expression;
asdl_seq *suite_seq;
expression = ast_for_expr(c, CHILD(n, 1));
if (!expression)
return NULL;
suite_seq = ast_for_suite(c, CHILD(n, 3));
if (!suite_seq)
return NULL;
return Until(expression, suite_seq, LINENO(n), n->n_col_offset, c->c_arena);
}
PyErr_Format(PyExc_SystemError,
"wrong number of tokens for 'until' statement: %d",
NCH(n));
return NULL;
}
다시 말하지만,이 조항 을 지원하지 않기로 결정한 ast_for_while_stmt
차이점을 제외 하고는 동등한을 면밀히 살펴보면서 코딩되었습니다 . 예상대로 AST는 조건 표현식 및 본문 과 같은 다른 AST 생성 함수를 사용하여 재귀 적으로 생성됩니다 .until
else
ast_for_expr
ast_for_suite
until
문의 . 마지막으로 이름 Until
이 지정된 새 노드 가 반환됩니다.
주 우리는 구문 분석 트리 노드에 액세스하는 것이 n
같은 몇 가지 매크로를 사용을 NCH
하고CHILD
. 이것들은 이해할 가치가 있습니다-그들의 코드는 Include/node.h
.
나는 until
성명서에 대해 새로운 유형의 AST를 만들기로 선택 했지만 실제로는 이것이 필요하지 않습니다. 다음과 같은 이유로 기존 AST 노드의 구성을 사용하여 일부 작업을 저장하고 새로운 기능을 구현할 수있었습니다.
until condition:
# do stuff
기능적으로 다음과 같습니다.
while not condition:
# do stuff
에서 Until
노드 를 만드는 대신 노드가있는 노드를 자식으로 ast_for_until_stmt
만들 수 있습니다 . AST 컴파일러는 이러한 노드를 처리하는 방법을 이미 알고 있으므로 프로세스의 다음 단계를 건너 뛸 수 있습니다.Not
While
다음 단계는 AST를 Python 바이트 코드로 컴파일하는 것입니다. 컴파일에는 CFG (Control Flow Graph)라는 중간 결과가 있지만 동일한 코드가이를 처리하므로 지금은이 세부 사항을 무시하고 다른 기사로 남겨 두겠습니다.
다음에 살펴볼 코드는 Python/compile.c
. 의 리드에 따라 명령문을 바이트 코드로 컴파일 while
하는 함수를 찾습니다 compiler_visit_stmt
. 다음에 대한 절을 추가합니다 Until
.
case While_kind:
return compiler_while(c, s);
case Until_kind:
return compiler_until(c, s);
무엇인지 궁금하다면 AST 정의 파일에서 자동으로 생성 된 Until_kind
상수 (실제로 _stmt_kind
열거 형 값)입니다.Include/Python-ast.h
. 어쨌든 우리는compiler_until
는 물론 아직 존재하지 않는 합니다. 잠시만 요.
나처럼 호기심이 많다면 그게 compiler_visit_stmt
특이 하다는 것을 알 수있을 것이다 . grep
소스 트리가 호출 된 위치를 보여주는 -ping의 양은 없습니다 . 이 경우 C macro-fu라는 한 가지 옵션 만 남습니다. 사실, 짧은 조사를 통해 VISIT
다음에 정의 된 매크로를 찾을 수 있습니다 Python/compile.c
.
#define VISIT(C, TYPE, V) {\
if (!compiler_visit_ ## TYPE((C), (V))) \
return 0; \
호출하는 데 사용됩니다 compiler_visit_stmt
.compiler_body
. 하지만 우리 사업으로 돌아가서 ...
약속 한대로 다음과 같습니다 compiler_until
.
static int
compiler_until(struct compiler *c, stmt_ty s)
{
basicblock *loop, *end, *anchor = NULL;
int constant = expr_constant(s->v.Until.test);
if (constant == 1) {
return 1;
}
loop = compiler_new_block(c);
end = compiler_new_block(c);
if (constant == -1) {
anchor = compiler_new_block(c);
if (anchor == NULL)
return 0;
}
if (loop == NULL || end == NULL)
return 0;
ADDOP_JREL(c, SETUP_LOOP, end);
compiler_use_next_block(c, loop);
if (!compiler_push_fblock(c, LOOP, loop))
return 0;
if (constant == -1) {
VISIT(c, expr, s->v.Until.test);
ADDOP_JABS(c, POP_JUMP_IF_TRUE, anchor);
}
VISIT_SEQ(c, stmt, s->v.Until.body);
ADDOP_JABS(c, JUMP_ABSOLUTE, loop);
if (constant == -1) {
compiler_use_next_block(c, anchor);
ADDOP(c, POP_BLOCK);
}
compiler_pop_fblock(c, LOOP, loop);
compiler_use_next_block(c, end);
return 1;
}
고백 할 게 있습니다.이 코드는 파이썬 바이트 코드에 대한 깊은 이해를 기반으로 작성되지 않았습니다. 기사의 나머지 부분과 마찬가지로 친족 compiler_while
기능 을 모방하여 수행되었습니다 . 그러나주의 깊게 읽으면 Python VM이 스택 기반이라는 점을 명심하고 Python 바이트 코드 목록dis
이 있는 모듈 문서를 살펴 봅니다. 설명 무슨 일이 일어나고 있는지 이해할 수 있습니다.
모든 변경을 수행하고를 실행 한 후 make
새로 컴파일 된 Python을 실행하고 새 until
명령문을 시도 할 수 있습니다 .
>>> until num == 0:
... print(num)
... num -= 1
...
3
2
1
짜잔, 작동합니다! dis
다음과 같이 모듈을 사용하여 새 문에 대해 생성 된 바이트 코드를 살펴 보겠습니다 .
import dis
def myfoo(num):
until num == 0:
print(num)
num -= 1
dis.dis(myfoo)
결과는 다음과 같습니다.
4 0 SETUP_LOOP 36 (to 39)
>> 3 LOAD_FAST 0 (num)
6 LOAD_CONST 1 (0)
9 COMPARE_OP 2 (==)
12 POP_JUMP_IF_TRUE 38
5 15 LOAD_NAME 0 (print)
18 LOAD_FAST 0 (num)
21 CALL_FUNCTION 1
24 POP_TOP
6 25 LOAD_FAST 0 (num)
28 LOAD_CONST 2 (1)
31 INPLACE_SUBTRACT
32 STORE_FAST 0 (num)
35 JUMP_ABSOLUTE 3
>> 38 POP_BLOCK
>> 39 LOAD_CONST 0 (None)
42 RETURN_VALUE
가장 흥미로운 작업은 12 번입니다. 조건이 참이면 루프 뒤로 점프합니다. 이것은에 대한 올바른 의미입니다 until
. 점프가 실행되지 않으면 루프 본문은 작업 35의 조건으로 다시 점프 할 때까지 계속 실행됩니다.
내 변경 사항에 대해 기분이 좋아서 myfoo(3)
바이트 코드를 표시하는 대신 함수를 실행 (executing ) 해 보았습니다 . 그 결과는 고무적이지 않았습니다.
Traceback (most recent call last):
File "zy.py", line 9, in
myfoo(3)
File "zy.py", line 5, in myfoo
print(num)
SystemError: no locals when loading 'print'
워 ... 이건 좋지 않아. 그래서 무엇이 잘못 되었습니까?
AST를 컴파일 할 때 Python 컴파일러가 수행하는 단계 중 하나는 컴파일하는 코드에 대한 기호 테이블을 만드는 것입니다. 에 대한 호출 PySymtable_Build
에서 PyAST_Compile
심볼 테이블 모듈에 호출 (Python/symtable.c
코드 생성 기능이 유사한 방식으로 안내 AST). 각 범위에 대한 기호 테이블이 있으면 컴파일러가 전역 변수 및 범위에 로컬 인 변수와 같은 몇 가지 주요 정보를 파악하는 데 도움이됩니다.
문제를 해결하기 위해 우리는 문 을 처리하기위한 코드를 추가 symtable_visit_stmt
하고 Python/symtable.c
, 문 [3]에until
대한 유사한 코드 뒤에 함수 를 수정해야 합니다 .while
case While_kind:
VISIT(st, expr, s->v.While.test);
VISIT_SEQ(st, stmt, s->v.While.body);
if (s->v.While.orelse)
VISIT_SEQ(st, stmt, s->v.While.orelse);
break;
case Until_kind:
VISIT(st, expr, s->v.Until.test);
VISIT_SEQ(st, stmt, s->v.Until.body);
break;
[3] : 그런데이 코드가 없으면 Python/symtable.c
. 컴파일러는 Until_kind
열거 형 값이의 switch 문 symtable_visit_stmt
및 불평 에서 처리되지 않음을 확인합니다 . 컴파일러 경고를 확인하는 것은 항상 중요합니다!
그리고 이제 우리는 정말 끝났습니다. 이 변경 후 소스를 컴파일하면 myfoo(3)
예상대로 작업 이 실행 됩니다.
이 기사에서는 Python에 새 문을 추가하는 방법을 설명했습니다. 파이썬 컴파일러의 코드에 약간의 수정이 필요했지만, 유사한 기존 명령문을 지침으로 사용했기 때문에 변경을 구현하기가 어렵지 않았습니다.
Python 컴파일러는 정교한 소프트웨어 청크이며, 나는 그것에 대한 전문가라고 주장하지 않습니다. 그러나 저는 파이썬의 내부, 특히 프런트 엔드에 정말 관심이 있습니다. 따라서이 연습이 컴파일러의 원리와 소스 코드에 대한 이론적 연구에 매우 유용한 동반자임을 알았습니다. 컴파일러에 대해 더 자세히 살펴볼 향후 기사의 기반이 될 것입니다.
이 기사의 구성을 위해 몇 가지 훌륭한 참고 자료를 사용했습니다. 여기에 특별한 순서가 없습니다.
until
isa
isan
if something isa dict:
if something isan int:
이와 같은 작업을 수행하는 한 가지 방법은 소스를 전처리하고 수정하여 추가 된 명령문을 파이썬으로 변환하는 것입니다. 이 접근 방식에는 여러 가지 문제가 있으며 일반적인 사용에는 권장하지 않지만 언어 실험이나 특정 목적의 메타 프로그래밍에는 유용 할 수 있습니다.
예를 들어, 화면에 인쇄하는 대신 특정 파일에 기록하는 "myprint"문을 도입하려고한다고 가정 해 보겠습니다. 즉 :
myprint "This gets logged to file"
다음과 같을 것입니다
print >>open('/tmp/logfile.txt','a'), "This gets logged to file"
정규식 대체에서 AST 생성, 구문이 기존 파이썬과 얼마나 가까운 지에 따라 자신의 파서를 작성하는 방법에 대한 다양한 옵션이 있습니다. 좋은 중간 접근법은 토크 나이저 모듈을 사용하는 것입니다. 이를 통해 파이썬 인터프리터와 유사하게 소스를 해석하는 동안 새로운 키워드, 제어 구조 등을 추가 할 수 있으므로 원유 정규식 솔루션으로 인한 파손을 피할 수 있습니다. 위의 "myprint"에 대해 다음 변환 코드를 작성할 수 있습니다.
import tokenize
LOGFILE = '/tmp/log.txt'
def translate(readline):
for type, name,_,_,_ in tokenize.generate_tokens(readline):
if type ==tokenize.NAME and name =='myprint':
yield tokenize.NAME, 'print'
yield tokenize.OP, '>>'
yield tokenize.NAME, "open"
yield tokenize.OP, "("
yield tokenize.STRING, repr(LOGFILE)
yield tokenize.OP, ","
yield tokenize.STRING, "'a'"
yield tokenize.OP, ")"
yield tokenize.OP, ","
else:
yield type,name
(이것은 myprint를 효과적으로 키워드로 만들므로 다른 곳에서 변수로 사용하면 문제가 발생할 수 있습니다)
문제는 코드를 파이썬에서 사용할 수 있도록 사용하는 방법입니다. 한 가지 방법은 직접 가져 오기 함수를 작성하고이를 사용하여 사용자 지정 언어로 작성된 코드를로드하는 것입니다. 즉 :
import new
def myimport(filename):
mod = new.module(filename)
f=open(filename)
data = tokenize.untokenize(translate(f.readline))
exec data in mod.__dict__
return mod
그러나 일반 파이썬 모듈과 다르게 사용자 정의 된 코드를 처리해야합니다. 즉 " some_mod = myimport("some_mod.py")
"보다는 " import some_mod
"
(해키이긴하지만) 상당히 깔끔한 또 다른 솔루션은 이 레시피가 보여주는 것처럼 사용자 지정 인코딩 ( PEP 263 참조 )을 만드는 것입니다. 이것을 다음과 같이 구현할 수 있습니다.
import codecs, cStringIO, encodings
from encodings import utf_8
class StreamReader(utf_8.StreamReader):
def __init__(self, *args, **kwargs):
codecs.StreamReader.__init__(self, *args, **kwargs)
data = tokenize.untokenize(translate(self.stream.readline))
self.stream = cStringIO.StringIO(data)
def search_function(s):
if s!='mylang': return None
utf8=encodings.search_function('utf8') # Assume utf8 encoding
return codecs.CodecInfo(
name='mylang',
encode = utf8.encode,
decode = utf8.decode,
incrementalencoder=utf8.incrementalencoder,
incrementaldecoder=utf8.incrementaldecoder,
streamreader=StreamReader,
streamwriter=utf8.streamwriter)
codecs.register(search_function)
이제이 코드가 실행 된 후 (예 : .pythonrc 또는 site.py에 배치 할 수 있음) "# coding : mylang"주석으로 시작하는 모든 코드는 위의 전처리 단계를 통해 자동으로 번역됩니다. 예.
# coding: mylang
myprint "this gets logged to file"
for i in range(10):
myprint "so does this : ", i, "times"
myprint ("works fine" "with arbitrary" + " syntax"
"and line continuations")
주의 사항 :
전 처리기 접근 방식에는 문제가 있습니다. C 전처리기로 작업 한 적이 있다면 아마 익숙 할 것입니다. 주요한 것은 디버깅입니다. 파이썬이 보는 모든 것은 전처리 된 파일이므로 스택 추적 등에 인쇄 된 텍스트가 해당 파일을 참조합니다. 중요한 번역을 수행 한 경우 원본 텍스트와 매우 다를 수 있습니다. 위의 예는 줄 번호 등을 변경하지 않으므로 너무 다르지 않지만 더 많이 변경할수록 파악하기가 더 어려워집니다.
myimport
단지에 포함 된 모듈에 print 1
이 코드 수율의 선의로=1 ... SyntaxError: invalid syntax
b=myimport("b.py")
"및 b.py에 " print 1
" 만 포함하는 것 입니다. 오류에 더 많은 것이 있습니까 (스택 추적 등)?
import
내장 을 사용 __import__
하므로이를 덮어 쓰면 ( 수정 된 가져 오기가 필요한 모듈을 가져 오기 전에 ) 별도의 작업이 필요하지 않습니다myimport
예, 어느 정도 가능합니다. 구현 및 "키워드"에 사용 하는 모듈 이 있습니다 .sys.settrace()
goto
comefrom
from goto import goto, label
for i in range(1, 10):
for j in range(1, 20):
print i, j
if j == 3:
goto .end # breaking out from nested loop
label .end
print "Finished"
변경하고 (소스 코드 컴파일의 짧은 입니다 기본 언어를 변경, 오픈 소스 가능)은 정말 할 수 없습니다.
소스를 재 컴파일하더라도 파이썬이 아니라 버그를 도입하지 않도록 매우주의해야하는 해킹 된 변경된 버전 일뿐입니다.
그러나 왜 당신이 원하는지 잘 모르겠습니다. Python의 객체 지향 기능을 사용하면 해당 언어를 그대로 사용하여 유사한 결과를 쉽게 얻을 수 있습니다.
일반적인 대답 : 소스 파일을 사전 처리해야합니다.
보다 구체적인 답변 : EasyExtend 설치 하고 다음 단계를 수행하십시오.
i) 새 langlet (확장 언어) 만들기
import EasyExtend
EasyExtend.new_langlet("mystmts", prompt = "my> ", source_ext = "mypy")
추가 사양이 없으면 EasyExtend / langlets / mystmts / 아래에 여러 파일이 생성됩니다.
ii) mystmts / parsedef / Grammar.ext를 열고 다음 줄을 추가합니다.
small_stmt: (expr_stmt | print_stmt | del_stmt | pass_stmt | flow_stmt |
import_stmt | global_stmt | exec_stmt | assert_stmt | my_stmt )
my_stmt: 'mystatement' expr
이것은 새 문의 구문을 정의하는 데 충분합니다. small_stmt 비 터미널은 Python 문법의 일부이며 새 문이 연결되는 위치입니다. 파서는 이제 새 문을 인식합니다. 즉,이를 포함하는 소스 파일이 구문 분석됩니다. 컴파일러는 여전히 유효한 Python으로 변환해야하므로이를 거부합니다.
iii) 이제 문장의 의미를 추가해야합니다. 이를 위해 msytmts / langlet.py를 편집하고 my_stmt 노드 방문자를 추가해야합니다.
def call_my_stmt(expression):
"defines behaviour for my_stmt"
print "my stmt called with", expression
class LangletTransformer(Transformer):
@transform
def my_stmt(self, node):
_expr = find_node(node, symbol.expr)
return any_stmt(CST_CallFunc("call_my_stmt", [_expr]))
__publish__ = ["call_my_stmt"]
iv) langlets / mystmts 및 유형으로 cd
python run_mystmts.py
이제 세션이 시작되고 새로 정의 된 명령문을 사용할 수 있습니다.
__________________________________________________________________________________
mystmts
On Python 2.5.1 (r251:54863, Apr 18 2007, 08:51:08) [MSC v.1310 32 bit (Intel)]
__________________________________________________________________________________
my> mystatement 40+2
my stmt called with 42
사소한 진술에 이르기까지 몇 단계를 거치지 않았습니까? 문법에 신경 쓰지 않고 간단한 것을 정의 할 수있는 API는 아직 없습니다. 그러나 EE는 일부 버그 모듈로 매우 신뢰할 수 있습니다. 따라서 프로그래머가 편리한 OO 프로그래밍을 사용하여 중위 연산자 또는 작은 문과 같은 편리한 항목을 정의 할 수있는 API가 등장하는 것은 시간 문제 일뿐입니다. langlet을 구축하여 Python에 전체 언어를 임베드하는 것과 같은 더 복잡한 작업의 경우 완전한 문법 접근 방식을 사용할 방법이 없습니다.
해석 모드에서만 새 문장을 추가하는 매우 간단하지만 엉터리 방법이 있습니다 . 나는 sys.displayhook 만 사용하여 유전자 주석을 편집하기 위해 작은 1 글자 명령에 사용하고 있지만이 질문에 답할 수 있도록 구문 오류에 대해서도 sys.excepthook을 추가했습니다. 후자는 정말 추한데, readline 버퍼에서 원시 코드를 가져옵니다. 이점은 이러한 방식으로 새 문을 추가하는 것이 매우 쉽다는 것입니다.
jcomeau@intrepid:~/$ cat demo.py; ./demo.py
#!/usr/bin/python -i
'load everything needed under "package", such as package.common.normalize()'
import os, sys, readline, traceback
if __name__ == '__main__':
class t:
@staticmethod
def localfunction(*args):
print 'this is a test'
if args:
print 'ignoring %s' % repr(args)
def displayhook(whatever):
if hasattr(whatever, 'localfunction'):
return whatever.localfunction()
else:
print whatever
def excepthook(exctype, value, tb):
if exctype is SyntaxError:
index = readline.get_current_history_length()
item = readline.get_history_item(index)
command = item.split()
print 'command:', command
if len(command[0]) == 1:
try:
eval(command[0]).localfunction(*command[1:])
except:
traceback.print_exception(exctype, value, tb)
else:
traceback.print_exception(exctype, value, tb)
sys.displayhook = displayhook
sys.excepthook = excepthook
>>> t
this is a test
>>> t t
command: ['t', 't']
this is a test
ignoring ('t',)
>>> ^D
새 진술 추가에 대한 가이드를 찾았습니다.
https://troeger.eu/files/teaching/pythonvm08lab.pdf
기본적으로 새 문을 추가하려면 Python/ast.c
(무엇보다도) 파이썬 바이너리를 편집 하고 다시 컴파일 해야합니다 .
가능하지만하지 마십시오. 함수와 클래스를 통해 거의 모든 것을 달성 할 수 있습니다 (스크립트를 실행하기 위해 사람들이 파이썬을 다시 컴파일 할 필요가 없습니다 ..)
EasyExtend를 사용 하여이 작업을 수행 할 수 있습니다 .
EasyExtend (EE)는 순수 Python으로 작성되고 CPython과 통합 된 전 처리기 생성기 및 메타 프로그래밍 프레임 워크입니다. EasyExtend의 주요 목적은 확장 언어를 만드는 것입니다. 즉, 사용자 지정 구문과 의미를 Python에 추가하는 것입니다.
언어 구문에 새 명령문을 정확히 추가하지는 않지만 매크로는 강력한 도구입니다. https://github.com/lihaoyi/macropy
인터프리터를 수정하지 않고서는 안됩니다. 지난 몇 년 동안 많은 언어가 "확장 가능"하다고 설명되었지만 당신이 설명하는 방식은 아닙니다. 함수와 클래스를 추가하여 Python을 확장합니다.
그런 일을 할 수있는 Logix 라는 파이썬 기반 언어가 있습니다 . 한동안 개발되지 않았지만 요청한 기능 은 최신 버전에서 작동 합니다.
데코레이터로 몇 가지 작업을 수행 할 수 있습니다. 예를 들어 파이썬에 with
진술 이 없다고 가정 해 봅시다 . 그런 다음 다음과 같은 유사한 동작을 구현할 수 있습니다.
# ====== Implementation of "mywith" decorator ======
def mywith(stream):
def decorator(function):
try: function(stream)
finally: stream.close()
return decorator
# ====== Using the decorator ======
@mywith(open("test.py","r"))
def _(infile):
for l in infile.readlines():
print(">>", l.rstrip())
그러나 여기에서 한 것처럼 꽤 불결한 해결책입니다. 특히 데코레이터가 함수를 호출하고 설정 _
하는 동작 None
은 예상치 못한 것입니다. 설명 :이 데코레이터는
def _(infile): ...
_ = mywith(open(...))(_) # mywith returns None.
데코레이터는 일반적으로 함수를 실행하는 것이 아니라 수정해야합니다.
이전에는 여러 기능에 대해 작업 디렉토리를 임시로 설정해야하는 스크립트에서 이러한 방법을 사용했습니다.