質問

新しいステートメントを追加していただけますか (例: print, raise, with) Python の構文に?

許可すると言ってください。

mystatement "Something"

または、

new_if True:
    print "example"

あなたならそれほど多くはありません すべき, 、しかし、それが可能であれば(Pythonインタープリターコードを変更せずに)

役に立ちましたか?

解決

これは役に立つかもしれません - Python の内部:Python に新しいステートメントを追加する, 、ここで引用:


この記事は、Python のフロントエンドがどのように機能するかをより深く理解することを目的としています。ドキュメントやソース コードを読むだけでは少し退屈かもしれないので、ここでは実践的なアプローチをとります。を追加します until Python へのステートメント。

この記事のコーディングはすべて、 Python Mercurial リポジトリ ミラー.

until 声明

Ruby などの一部の言語には、 until ステートメント、これを補うものです while (until num == 0 と同等です while num != 0)。Ruby では次のように書くことができます。

num = 3
until num == 0 do
  puts num
  num -= 1
end

そして、それは印刷されます:

3
2
1

そこで、同様の機能を Python に追加したいと思います。つまり、次のように書けるようになります。

num = 3
until num == 0:
  print(num)
  num -= 1

言語擁護の余談

この記事は、 until Python へのステートメント。このような記述によってコードがより明確になると思いますし、この記事では追加がいかに簡単であるかを示していますが、私は Python のミニマリズムの哲学を完全に尊重しています。私がここでやろうとしているのは、実際のところ、Python の内部動作についての洞察を得ることだけです。

文法の修正

Python は、という名前のカスタム パーサー ジェネレーターを使用します。 pgen. 。これは、Python ソース コードを解析ツリーに変換する LL(1) パーサーです。パーサー ジェネレーターへの入力はファイルです。 Grammar/Grammar[1]. 。これは、Python の文法を指定する単純なテキスト ファイルです。

[1]:これ以降、Python ソース内のファイルへの参照は、ソース ツリーのルート (Python をビルドするためにconfigure および make を実行するディレクトリ) に対して相対的に指定されます。

文法ファイルに 2 つの変更を加える必要があります。1 つ目は、 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 ループの節であり、Python の Zen とはうまく適合しないと思います)。

2 番目の変更は、次のルールを変更することです。 compound_stmt 含める until_stmt, 上のスニペットでわかるように。直後です while_stmt, 、 また。

走るとき make 修正後 Grammar/Grammar, に注目してください。 pgen プログラムを実行して再生成する Include/graminit.h そして Python/graminit.c, 、その後、いくつかのファイルが再コンパイルされます。

AST生成コードの変更

Python パーサーが解析ツリーを作成した後、AST は次のとおりであるため、このツリーは AST に変換されます。 作業がはるかに簡単です コンパイルプロセスの後続の段階で。

それで、私たちは訪問するつもりです Parser/Python.asdl これは Python の 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) を使用した Python ソースコードの別の例です。また、以来、 Parser/asdl_c.py Python スクリプトです。これは一種の ブートストラッピング - Python を最初から構築するには、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, 、違いは、 until 私は支持しないことに決めました else 句。予想どおり、AST は、次のような他の AST 作成関数を使用して再帰的に作成されます。 ast_for_expr 条件式と ast_for_suite の身体のために until 声明。最後に、という名前の新しいノードが追加されました。 Until が返されます。

解析ツリーノードにアクセスすることに注意してください。 n 次のようないくつかのマクロを使用して NCH そして CHILD. 。これらは理解する価値があります - そのコードは Include/node.h.

余談:AST組成

新しいタイプの AST を作成することにしました。 until というステートメントですが、実際にはこれは必要ありません。次の理由から、作業をいくつか節約し、既存の AST ノードの構成を使用して新しい機能を実装することもできました。

until condition:
   # do stuff

機能的には以下と同等です。

while not condition:
  # do stuff

を作成する代わりに、 Until ノードイン ast_for_until_stmt, 、を作成することもできました。 Not ノード While ノードを子として使用します。AST コンパイラはこれらのノードの処理方法をすでに知っているため、プロセスの次のステップをスキップできます。

AST をバイトコードにコンパイルする

次のステップでは、AST を Python バイトコードにコンパイルします。コンパイルの中間結果は CFG (コントロール フロー グラフ) ですが、同じコードで処理されるため、この詳細は今のところ無視し、別の記事に残しておきます。

次に見るコードは次のとおりです Python/compile.c. 。の先導に従って while, 、関数を見つけます compiler_visit_stmt, 、ステートメントをバイトコードにコンパイルする役割を果たします。という条項を追加します。 Until:

case While_kind:
    return compiler_while(c, s);
case Until_kind:
    return compiler_until(c, s);

何かと思ったら Until_kind つまり、それは定数です (実際には _stmt_kind 列挙型) AST 定義ファイルから自動的に生成され、 Include/Python-ast.h. 。とにかく、私たちは電話します compiler_until もちろん、それはまだ存在しません。ちょっと取り掛かります。

私と同じように興味がある人なら気づくでしょう compiler_visit_stmt 独特だ。いくらでも grepソースツリーに -ping を実行すると、どこで呼び出されているかがわかります。この場合、残るオプションは C マクロ fu の 1 つだけです。実際、短い調査で次のことがわかります。 VISIT で定義されたマクロ Python/compile.c:

#define VISIT(C, TYPE, V) {\
    if (!compiler_visit_ ## TYPE((C), (V))) \
        return 0; \

呼び出すために使用されます compiler_visit_stmtcompiler_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;
}

告白したいことがあります。このコードは、Python バイトコードの深い理解に基づいて書かれていません。記事の残りの部分と同様、これは親族を模倣して行われました。 compiler_while 関数。ただし、Python VM がスタックベースであることを念頭に置き、注意深く読んで、そのドキュメントに目を通します。 dis モジュールには、 Python バイトコードのリスト 説明があれば、何が起こっているのかを理解することができます。

それで終わりです...そうじゃないですか?

すべての変更を加えて実行した後、 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 番です。条件が true の場合、ループの後にジャンプします。これは正しいセマンティクスです 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'

うわあ...これは良くありません。それで、何が間違っていたのでしょうか?

シンボルテーブルが欠落している場合

Python コンパイラーが AST をコンパイルするときに実行するステップの 1 つは、コンパイルするコードのシンボル テーブルを作成することです。への呼び出し PySymtable_BuildPyAST_Compile シンボルテーブルモジュールを呼び出します(Python/symtable.c)、コード生成関数と同様の方法で AST を実行します。各スコープにシンボル テーブルがあると、どの変数がスコープに対してグローバルでどの変数がローカルであるかなど、コンパイラーがいくつかの重要な情報を把握するのに役立ちます。

この問題を解決するには、 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]:ちなみに、このコードがないとコンパイラの警告が表示されます。 Python/symtable.c. 。コンパイラは、 Until_kind 列挙値は、switch ステートメントでは処理されません。 symtable_visit_stmt と文句を言う。コンパイラの警告を確認することは常に重要です。

これで本当に終わりです。この変更後にソースをコンパイルすると、 myfoo(3) 期待通りに動作します。

結論

この記事では、Python に新しいステートメントを追加する方法を説明しました。Python コンパイラーのコードをかなりいじる必要がありましたが、同様の既存のステートメントをガイドラインとして使用したため、変更を実装するのは難しくありませんでした。

Python コンパイラは洗練されたソフトウェアの塊であり、私はその専門家であるとは言えません。しかし、私は Python の内部、特にそのフロントエンドに非常に興味があります。したがって、この演習は、コンパイラーの原理とソース コードの理論的研究に非常に役立つものであることがわかりました。これは、コンパイラについてさらに詳しく説明する将来の記事のベースとして機能します。

参考文献

この記事の作成にはいくつかの優れた参考文献を使用しました。以下にそれらを順不同で示します。

  • PEP 339:CPython コンパイラの設計 - おそらく最も重要かつ包括的な部分 正式 Python コンパイラのドキュメント。非常に短いので、Python の内部についての適切なドキュメントが不足していることが痛いほどわかります。
  • 「Python コンパイラの内部」 - Thomas Lee による記事
  • 「パイソン:「設計と実装」 - Guido van Rossum によるプレゼンテーション
  • Python (2.5) 仮想マシン、ガイド付きツアー - Peter Tröger によるプレゼンテーション

オリジナルソース

他のヒント

このようなことを行う1つの方法は、ソースを前処理して変更し、追加したステートメントをpythonに変換することです。このアプローチがもたらすさまざまな問題があり、一般的な使用にはお勧めしませんが、言語の実験や特定の目的のメタプログラミングには、時々役立つことがあります。

たとえば、" myprint"を導入したいとしましょう。ステートメント。画面に出力する代わりに、特定のファイルにログを記録します。例:

myprint "This gets logged to file"

と同等になります

print >>open('/tmp/logfile.txt','a'), "This gets logged to file"

既存のpythonと構文がどれだけ一致するかに応じて、正規表現の置換からASTの生成、独自のパーサーの作成まで、置換の方法に関してさまざまなオプションがあります。適切な中間アプローチはトークナイザーモジュールを使用することです。これにより、Pythonインタープリターと同様にソースを解釈しながら、新しいキーワード、制御構造などを追加できるようになり、粗い正規表現ソリューションの破損を回避できます。上記の「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は事実上キーワードになるため、他の場所で変数として使用すると問題が発生する可能性があります)

問題は、Pythonからコードを使用できるようにする方法です。 1つの方法は、独自のインポート関数を作成し、それを使用してカスタム言語で記述されたコードをロードすることです。例:

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

これには、カスタマイズしたコードを通常のpythonモジュールとは異なる方法で処理する必要があります。すなわち、" some_mod = myimport(" some_mod.py")" " import some_mod "

ではなく

別のかなりきちんとした(ハッキーとはいえ)ソリューションは、カスタムエンコーディングを作成することです( PEPを参照してください) 263 this レシピが示しています。これを次のように実装できます。

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プリプロセッサを使用した経験がある場合はおそらく慣れているので、プリプロセッサアプローチには問題があります。主なものはデバッグです。 Pythonが認識するのは、前処理されたファイルのみです。つまり、スタックトレースなどに出力されるテキストはそれを参照します。重要な翻訳を行った場合、これはソーステキストとは大きく異なる場合があります。上記の例では行番号などは変更されませんので、それほど違いはありませんが、変更するほどわかりにくくなります。

はい、ある程度可能です。 sys.settrace()を使用してを実装する module があります> goto および 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"

ソースコードの変更と再コンパイルの短い(オープンソースでは 可能)、ベース言語の変更は実際には不可能です。

ソースを再コンパイルしても、それはpythonではなく、ハックアップされた変更されたバージョンであり、バグを導入しないように非常に注意する必要があります。

しかし、なぜあなたが望むのかはわかりません。 Pythonのオブジェクト指向機能により、現状の言語で同様の結果を得ることが非常に簡単になります。

一般的な答え:ソースファイルを前処理する必要があります。

より具体的な回答: EasyExtend をインストールし、次の手順を実行します

i)新しいラングレット(拡張言語)を作成します

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はいくつかのバグを除けば非常に信頼できます。そのため、プログラマが便利なオブジェクト指向プログラミングを使用して、中置演算子や小さなステートメントなどの便利なものを定義できるAPIが登場するのは時間の問題です。ラングレットを構築して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 (特に)を編集し、Pythonバイナリを再コンパイルする必要があります。

可能ですが、できません。関数とクラスを介してほとんどすべてを達成できます(スクリプトを実行するためだけにPythonを再コンパイルする必要はありません。)

EasyExtend を使用してこれを行うことができます:

>
  

EasyExtend(EE)はプリプロセッサです   ジェネレーターとメタプログラミング   純粋なPythonで書かれたフレームワークと   CPythonと統合されています。メイン   EasyExtendの目的は作成です   拡張言語の追加   Pythonのカスタム構文とセマンティクス。

インタープリターを変更せずに。過去数年間で多くの言語が「拡張可能」と説明されてきましたが、あなたが説明している方法ではありません。関数とクラスを追加してPythonを拡張します。

Logix と呼ばれるPythonベースの言語があります。しばらくの間開発されていませんが、最新バージョンで機能するを求めた機能。

デコレータを使用していくつかのことができます。例えばPythonには 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.

およびデコレータは通常、関数を実行するのではなく変更することが期待されています。

以前は、いくつかの機能の作業ディレクトリを一時的に設定する必要があったスクリプトで、このような方法を使用していました。

新しい構文を言語構文に正確に追加するわけではありませんが、マクロは強力なツールです: https:// github。 com / lihaoyi / macropy

10年前にはできませんでしたが、変更されたとは思いません。ただし、Pythonを再コンパイルする準備ができていれば、当時の構文を変更するのはそれほど難しくありませんでしたし、変更されたことも疑っています。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top