131

Pythonの構文に新しいステートメント(、、など)printraise追加できますか?with

言う、許可する。

mystatement "Something"

または、

new_if True:
    print "example"

必要な場合はそれほど多くはありませんが、可能であれば(Pythonインタープリターコードを変更する以外)

4

13 に答える 13

173

これは役に立つかもしれません - Python internals: adding a new statement to Python、ここで引用:


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

この記事のすべてのコーディングは、 Python Mercurial リポジトリ ミラーの最先端の Py3k ブランチに対して行われました。

untilステートメント_

Ruby などの一部の言語には、 (は と同等) をuntil補完するステートメントがあります。Ruby では、次のように記述できます。whileuntil num == 0while num != 0

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 つの変更を加える必要があります。until1 つ目は、ステートメントの定義を追加することです。whileステートメントが定義されている場所を見つけ( )、 [2]の下while_stmtに追加しました。until_stmt

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] : これは、私が慣れていないソース コードを変更するときに使用する一般的な手法を示しています: work by similarity。この原則ですべての問題が解決するわけではありませんが、プロセスを確実に緩和できます。のためにしなければならないことはすべて のためにwhileもしなければならないのでuntil、これはかなり良いガイドラインとして役立ちます。

elseの定義から節を除外することにしたことに注意してください。これはuntil、少し異なるものにするためです (率直に言ってelse、ループの節が嫌いで、Zen of Python にうまく適合するとは思わないからです)。

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

makeを変更した後に実行すると、プログラムが実行されて と が再生成され、いくつかのファイルが再コンパイルされるGrammar/Grammarことに注意してください。pgenInclude/graminit.hPython/graminit.c

AST 生成コードの変更

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

そこで、Python の AST の構造を定義する にアクセスし、新しいステートメントParser/Python.asdlの AST ノードを のすぐ下に追加します。untilwhile

| 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 が既に利用可能である必要があります。

新しく定義した AST ノードを管理するコードをParser/asdl_c.py(ファイルInclude/Python-ast.hと にPython/Python-ast.c) 生成しましたが、関連する解析ツリー ノードをそれに変換するコードを手動で記述する必要があります。これはファイルで行われますPython/ast.c。そこでは、という名前の関数がast_for_stmtステートメントの解析ツリー ノードを AST ノードに変換します。繰り返しになりますが、古くからの友人whileである に導かれて、複合ステートメントを処理するための大きな部分に飛び込みswitch、節 for を追加し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;
}

繰り返しますが、これは同等の を詳しく調べながらコーディングされましたが、 for句をサポートしないことにast_for_while_stmtしたという違いがあります。予想どおり、AST は、条件式やステートメントの本体など、他の AST 作成関数を使用して再帰的に作成されます。最後に、という名前の新しいノードが返されます。untilelseast_for_exprast_for_suiteuntilUntil

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

余談:AST合成

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

until condition:
   # do stuff

機能的には次のものと同等です。

while not condition:
  # do stuff

Untilでノードを作成する代わりに、ノードを子として持つノードをast_for_until_stmt作成することもできました。AST コンパイラはこれらのノードの処理方法を既に認識しているため、プロセスの次のステップをスキップできます。NotWhile

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);

どういうことかと思ったら、これは AST 定義ファイルから自動的に生成されUntil_kindた定数 (実際には列挙型の値) です。とにかく、もちろん、まだ存在しないと呼びます。少し話します。_stmt_kindInclude/Python-ast.hcompiler_until

私のように好奇心旺盛な人なら、それcompiler_visit_stmtが独特であることに気付くでしょう。grepソースツリーが呼び出された場所を明らかにする -ping の量はありません。この場合、残るオプションは 1 つだけです。それは、C マクロ 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;
}

自白しなければならないことがあります。このコードは、Python バイトコードの深い理解に基づいて書かれたものではありません。記事の残りの部分と同様に、これは kincompiler_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 番です。条件が 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'

おっと... これは良くありません。それで、何がうまくいかなかったのですか?

シンボルテーブルが欠落しているケース

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

symtable_visit_stmtこの問題を解決するには、 の関数を変更して、ステートメントの同様のコードの後に​​ステートメントをPython/symtable.c処理するコードを追加する必要があります[3] :untilwhile

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 Compiler Internals」 - Thomas Lee による記事
  • 「Python: 設計と実装」 - Guido van Rossum によるプレゼンテーション
  • Python (2.5) 仮想マシン、ガイド付きツアー - Peter Tröger によるプレゼンテーション

元のソース

于 2012-02-02T06:39:44.873 に答える
56

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

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

myprint "This gets logged to file"

と同等です

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

正規表現の置換から AST の生成、構文が既存の python にどれだけ近いかに応じて独自のパーサーを作成するなど、置換の方法にはさまざまなオプションがあります。適切な中間アプローチは、トークナイザー モジュールを使用することです。これにより、ソースを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を参照)を作成することです。これを次のように実装できます。

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 が認識するのは前処理されたファイルだけです。つまり、スタック トレースなどに出力されるテキストはそれを参照します。大幅な翻訳を行った場合、これはソース テキストとは大きく異なる可能性があります。上の例は行番号などは変えていないのであまり変わらないのですが、変えれば変えるほどわかりづらくなります。

于 2008-10-18T23:15:04.043 に答える
21

はい、ある程度可能です。「キーワード」を実装するために使用するモジュールがあります。sys.settrace()gotocomefrom

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"
于 2008-10-19T18:52:00.270 に答える
14

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

ソースを再コンパイルしたとしても、それは Python ではなく、バグを持ち込まないように細心の注意を払う必要があるハッキングされた変更されたバージョンです。

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

于 2008-10-18T11:16:45.477 に答える
13

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

より具体的な回答: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に言語全体を埋め込むなど、より複雑なことについては、完全な文法アプローチを回避する方法はありません。

于 2008-10-21T05:26:58.630 に答える
12

これは、解釈モードのみで新しいステートメントを追加するための非常に単純ですが、くだらない方法です。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

于 2011-01-01T02:23:19.563 に答える
4

新しいステートメントを追加するためのガイドを見つけました。

https://troeger.eu/files/teaching/pythonvm08lab.pdf

基本的に、新しいステートメントを追加するには、Python/ast.c(とりわけ)Pythonバイナリを編集して再コンパイルする必要があります。

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

于 2008-10-18T13:26:37.120 に答える
3

EasyExtendを使用してこれを行うことが可能です:

EasyExtend(EE)は、純粋なPythonで記述され、CPythonと統合されたプリプロセッサージェネレーターおよびメタプログラミングフレームワークです。EasyExtendの主な目的は、拡張言語の作成、つまりPythonにカスタム構文とセマンティクスを追加することです。

于 2008-10-20T02:29:20.377 に答える
3

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

于 2015-07-01T01:34:23.450 に答える
2

そのようなことを行うことができるLogixと呼ばれる Python ベースの言語があります。しばらく開発されていませんが、ご要望の機能は最新バージョンで動作します

于 2008-10-19T08:36:50.883 に答える
2

デコレータでできることもあります。たとえば、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.

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

以前、いくつかの関数の作業ディレクトリを一時的に設定しなければならなかったスクリプトで、このような方法を使用しました。

于 2015-01-08T13:53:13.363 に答える
1

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

于 2008-10-18T11:18:17.470 に答える
0

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

于 2008-10-18T11:10:15.007 に答える