파이썬 구문에 새로운 문장을 추가 할 수 있습니까?
당신은 새로운 문을 (같은 추가 할 수 print
, raise
, with
파이썬의 구문)?
말해봐 ..
mystatement "Something"
또는,
new_if True:
print "example"
당신이 해야하는 것이 아니라 가능하다면 (파이썬 인터프리터 코드를 수정하는 것이 부족한 경우)
-이 유용하게 사용할 수있는 파이썬에 새로운 문 추가 : 파이썬 내부를 여기에 인용을 :
이 기사는 파이썬의 프론트 엔드가 어떻게 작동하는지 더 잘 이해하기위한 시도입니다. 문서와 소스 코드를 읽는 것만으로도 지루할 수 있으므로 여기서는 실제 접근 방식을 취하고 있습니다. until
파이썬에 문장을 추가하겠습니다 .
이 기사의 모든 코딩은 Python Mercurial 저장소 미러 의 최첨단 Py3k 브랜치에 대해 수행되었습니다 .
until
문
일부 언어는 루비처럼이 until
에 보완 문을 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
. Python 소스 코드를 구문 분석 트리로 변환하는 LL (1) 구문 분석기입니다. 파서 생성기의 입력은 파일 Grammar/Grammar
[1] 입니다. 이것은 파이썬의 문법을 지정하는 간단한 텍스트 파일입니다.
[1] : 여기서부터 파이썬 소스의 파일에 대한 참조는 소스 트리의 루트에 상대적으로 주어집니다. 소스 트리의 루트는 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 of Python과 잘 어울리지 않는다고 생각하기 때문에).
두 번째 변경 사항은 위의 스 니펫에서 볼 수 있듯이 compound_stmt
포함 규칙을 수정하는 것입니다 until_stmt
. while_stmt
또 다시 직후 입니다.
당신이 실행하면 make
수정 한 후 Grammar/Grammar
, 통지 것을 pgen
프로그램이 실행되어 다시 생성 Include/graminit.h
하고 Python/graminit.c
, 다음 몇 가지 파일을 재 컴파일 얻을.
AST 생성 코드 수정
파이썬 파서가 파싱 트리를 생성 한 후,이 트리는 AST로 변환되는데, AST 는 컴파일 프로세스의 후속 단계에서 작업하기 가 훨씬 간단하기 때문입니다.
그래서 우리는 방문을거야 Parser/Python.asdl
파이썬하는 AST의 구조를 정의하고 우리의 새로운에 대한 AST 노드를 추가 until
바로 아래에 다시 문 while
:
| While(expr test, stmt* body, stmt* orelse)
| Until(expr test, stmt* body)
이제를 실행 make
하면 많은 파일을 컴파일하기 전에 Parser/asdl_c.py
AST 정의 파일에서 C 코드를 생성하기 위해 실행됩니다. 이것은 (처럼 Grammar/Grammar
) 프로그래밍을 단순화하기 위해 미니 언어 (즉, DSL)를 사용하는 파이썬 소스 코드의 또 다른 예입니다. 또한 Parser/asdl_c.py
파이썬 스크립트이므로 일종의 부트 스트랩 입니다. 처음부터 파이썬을 빌드하려면 파이썬을 이미 사용할 수 있어야합니다.
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
and와 n
같은 일부 매크로를 사용하여 구문 분석 트리 노드에 액세스합니다 . 이것들은 이해할만한 가치가 있습니다-그들의 코드가 있습니다 .NCH
CHILD
Include/node.h
탈선 : AST 구성
until
명세서에 대해 새로운 유형의 AST를 작성하기로 선택 했지만 실제로는 필요하지 않습니다. 다음과 같은 이유로 일부 작업을 저장하고 기존 AST 노드의 구성을 사용하여 새로운 기능을 구현할 수있었습니다.
until condition:
# do stuff
기능적으로 다음과 같습니다.
while not condition:
# do stuff
에서 Until
노드 를 만드는 대신 노드를 자식으로 사용하여 노드를 ast_for_until_stmt
만들 수있었습니다 . AST 컴파일러는 이러한 노드를 처리하는 방법을 이미 알고 있으므로 프로세스의 다음 단계를 건너 뛸 수 있습니다.Not
While
AST를 바이트 코드로 컴파일
다음 단계는 AST를 파이썬 바이트 코드로 컴파일하는 것입니다. 컴파일에는 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
소스 트리 의 핑 양은 그것이 호출되는 위치를 보여주지 않습니다 . 이 경우 하나의 옵션 만 남습니다 (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
이 있는 모듈 설명서를 살펴보면 진행 상황을 이해할 수 있습니다.
그게 다야, 우리는 끝났어 ... 그렇지 않습니까?
모든 변경을 수행하고 running 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)
바이트 코드를 표시하는 대신 함수 실행 ( ) 을 시도 했습니다. 그 결과는 다음과 같은 것이 었습니다.
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 컴파일러가 수행하는 단계 중 하나는 컴파일하는 코드에 대한 기호 테이블을 작성하는 것입니다. 호출 은 코드 생성 함수와 유사한 방식으로 AST를 걷는 기호 테이블 모듈 ( ) PySymtable_Build
을 PyAST_Compile
호출 Python/symtable.c
합니다. 각 범위에 대한 기호 테이블을 사용하면 컴파일러가 전역 변수 및 범위에 대한 로컬 변수와 같은 주요 정보를 파악할 수 있습니다.
이 문제를 해결하려면, 우리는 수정해야 할 symtable_visit_stmt
함수를 Python/symtable.c
처리하기위한 코드를 추가 until
하기위한 유사한 코드 후, 문을 while
문 [3] :
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]: By the way, without this code there’s a compiler warning for Python/symtable.c
. The compiler notices that the Until_kind
enumeration value isn’t handled in the switch statement of symtable_visit_stmt
and complains. It’s always important to check for compiler warnings!
And now we really are done. Compiling the source after this change makes the execution of myfoo(3)
work as expected.
Conclusion
In this article I've demonstrated how to add a new statement to Python. Albeit requiring quite a bit of tinkering in the code of the Python compiler, the change wasn't difficult to implement, because I used a similar and existing statement as a guideline.
The Python compiler is a sophisticated chunk of software, and I don't claim being an expert in it. However, I am really interested in the internals of Python, and particularly its front-end. Therefore, I found this exercise a very useful companion to theoretical study of the compiler's principles and source code. It will serve as a base for future articles that will get deeper into the compiler.
References
I used a few excellent references for the construction of this article. Here they are, in no particular order:
- PEP 339: Design of the CPython compiler - probably the most important and comprehensive piece of official documentation for the Python compiler. Being very short, it painfully displays the scarcity of good documentation of the internals of Python.
- "Python Compiler Internals" - an article by Thomas Lee
- "Python: Design and Implementation" - a presentation by Guido van Rossum
- Python (2.5) Virtual Machine, A guided tour - a presentation by Peter Tröger
One way to do things like this is to preprocess the source and modify it, translating your added statement to python. There are various problems this approach will bring, and I wouldn't recommend it for general usage, but for experimentation with language, or specific-purpose metaprogramming, it can occassionally be useful.
For instance, lets say we want to introduce a "myprint" statement, that instead of printing to the screen instead logs to a specific file. ie:
myprint "This gets logged to file"
would be equivalent to
print >>open('/tmp/logfile.txt','a'), "This gets logged to file"
There are various options as to how to do the replacing, from regex substitution to generating an AST, to writing your own parser depending on how close your syntax matches existing python. A good intermediate approach is to use the tokenizer module. This should allow you to add new keywords, control structures etc while interpreting the source similarly to the python interpreter, thus avoiding the breakage crude regex solutions would cause. For the above "myprint", you could write the following transformation code:
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
(This does make myprint effectively a keyword, so use as a variable elsewhere will likely cause problems)
The problem then is how to use it so that your code is usable from python. One way would just be to write your own import function, and use it to load code written in your custom language. ie:
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
This requires you handle your customised code differently from normal python modules however. ie "some_mod = myimport("some_mod.py")
" rather than "import some_mod
"
Another fairly neat (albeit hacky) solution is to create a custom encoding (See PEP 263) as this recipe demonstrates. You could implement this as:
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)
Now after this code gets run (eg. you could place it in your .pythonrc or site.py) any code starting with the comment "# coding: mylang" will automatically be translated through the above preprocessing step. eg.
# 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")
Caveats:
There are problems to the preprocessor approach, as you'll probably be familiar with if you've worked with the C preprocessor. The main one is debugging. All python sees is the preprocessed file which means that text printed in the stack trace etc will refer to that. If you've performed significant translation, this may be very different from your source text. The example above doesn't change line numbers etc, so won't be too different, but the more you change it, the harder it will be to figure out.
Yes, to some extent it is possible. There is a module out there that uses sys.settrace()
to implement goto
and comefrom
"keywords":
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"
Short of changing and recompiling the source code (which is possible with open source), changing the base language is not really possible.
Even if you do recompile the source, it wouldn't be python, just your hacked-up changed version which you need to be very careful not to introduce bugs into.
However, I'm not sure why you'd want to. Python's object-oriented features makes it quite simple to achieve similar results with the language as it stands.
General answer: you need to preprocess your source files.
More specific answer: install EasyExtend, and go through following steps
i) Create a new langlet ( extension language )
import EasyExtend
EasyExtend.new_langlet("mystmts", prompt = "my> ", source_ext = "mypy")
Without additional specification a bunch of files shall be created under EasyExtend/langlets/mystmts/ .
ii) Open mystmts/parsedef/Grammar.ext and add following lines
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
This is sufficient to define the syntax of your new statement. The small_stmt non-terminal is part of the Python grammar and it's the place where the new statement is hooked in. The parser will now recognize the new statement i.e. a source file containing it will be parsed. The compiler will reject it though because it still has to be transformed into valid Python.
iii) Now one has to add semantics of the statement. For this one has to edit msytmts/langlet.py and add a my_stmt node visitor.
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) cd to langlets/mystmts and type
python run_mystmts.py
Now a session shall be started and the newly defined statement can be used:
__________________________________________________________________________________
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
Quite a few steps to come to a trivial statement, right? There isn't an API yet that lets one define simple things without having to care about grammars. But EE is very reliable modulo some bugs. So it's just a matter of time that an API emerges that lets programmers define convenient stuff like infix operators or small statements using just convenient OO programming. For more complex things like embedding whole languages in Python by means of building a langlet there is no way of going around a full grammar approach.
Here's a very simple but crappy way to add new statements, in interpretive mode only. I'm using it for little 1-letter commands for editing gene annotations using only sys.displayhook, but just so I could answer this question I added sys.excepthook for the syntax errors as well. The latter is really ugly, fetching the raw code from the readline buffer. The benefit is, it's trivially easy to add new statements this way.
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
I've found a guide on adding new statements:
https://troeger.eu/files/teaching/pythonvm08lab.pdf
Basically, to add new statements, you must edit Python/ast.c
(among other things) and recompile the python binary.
While it's possible, don't. You can achieve almost everything via functions and classes (which wont require people to recompile python just to run your script..)
It's possible to do this using EasyExtend:
EasyExtend (EE) is a preprocessor generator and metaprogramming framework written in pure Python and integrated with CPython. The main purpose of EasyExtend is the creation of extension languages i.e. adding custom syntax and semantics to Python.
It's not exactly adding new statements to the language syntax, but macros are a powerful tool: https://github.com/lihaoyi/macropy
Not without modifying the interpreter. I know a lot of languages in the past several years have been described as "extensible", but not in the way you're describing. You extend Python by adding functions and classes.
There is a language based on python called Logix with which you CAN do such things. It hasn't been under development for a while, but the features that you asked for do work with the latest version.
Some things can be done with decorators. Let's e.g. assume, Python had no with
statement. We could then implement a similar behaviour like this:
# ====== 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())
It is a pretty unclean solution however as done here. Especially the behaviour where the decorator calls the function and sets _
to None
is unexpected. For clarification: This decorator is equivalent to writing
def _(infile): ...
_ = mywith(open(...))(_) # mywith returns None.
and decorators are normally expected to modify, not to execute, functions.
I used such a method before in a script where I had to temporarily set the working directory for several functions.
Ten years ago you couldn't, and I doubt that's changed. However, it wasn't that hard to modify the syntax back then if you were prepared to recompile python, and I doubt that's changed, either.
참고URL : https://stackoverflow.com/questions/214881/can-you-add-new-statements-to-pythons-syntax
'IT박스' 카테고리의 다른 글
사용자 지정 MSBuild 작업을 만들 때 C # 코드에서 현재 프로젝트 디렉토리를 얻는 방법은 무엇입니까? (0) | 2020.07.22 |
---|---|
플레이스 홀더 Mixin SCSS / CSS (0) | 2020.07.22 |
GetLastError ()에서 반환 한 오류 코드에서 오류 메시지를 얻는 방법은 무엇입니까? (0) | 2020.07.21 |
Jade : 단락 내부의 링크 (0) | 2020.07.21 |
JQuery는 특정 클래스 접두사가있는 첫 번째 상위 요소를 찾습니다. (0) | 2020.07.21 |