1

関数テキストをバイトコードにコンパイルして実行する前に、前処理を試みることにしました。これは単にトレーニング用です。それが使用されるのに満足のいく解決策になる状況を私はほとんど想像しません。この方法で解決したい問題が1つありましたが、最終的にはより良い方法が見つかりました。したがって、これはトレーニングと新しいことを学ぶためだけのものであり、実際の使用法ではありません。

コンパイル前にかなり変更したいソースコードを持つ関数があると仮定します。

def f():
    1;a()
    print('Some statements 1')
    1;a()
    print('Some statements 2')

たとえば、1;コメントが付けられる場合と付けられない場合があるように、その行の一部に。を付けます。たとえば、関数の変更は異なる場合があります。

これらの行にコメントするために、私はデコレータを作成しました。以下のコード全体:

from __future__ import print_function


def a():
    print('a()')


def comment_1(s):
    lines = s.split('\n')
    return '\n'.join(line.replace(';','#;',1) if line.strip().startswith('1;') else line for line in lines)


def remove_1(f):    
    import inspect
    source = inspect.getsource(f)    
    new_source = comment_1(source)
    with open('temp.py','w') as file:
        file.write(new_source)
    from temp import f as f_new
    return f_new


def f():
    1;a()
    print('Some statements 1')
    1;a()
    print('Some statements 2')


f = remove_1(f) #If decorator @remove is used above f(), inspect.getsource includes @remove inside the code.

f()

inspect.getsourcelinesを使用して関数fコードを取得しました。次に、テキスト処理を行いました(この場合は)で始まるコメント行1;です。その後、temp.pyモジュールに保存し、インポートします。そしてf、メインモジュールに関数が装飾されます。

デコレータが適用されたときの出力は次のとおりです。

Some statements 1
Some statements 2

適用されない場合は次のとおりです。

a()
Some statements 1
a()
Some statements 2

私が嫌いなのは、コンパイルされた関数をロードするためにハードドライブを使用しなければならないことです。一時モジュールに書き込んでインポートせずに実行できますtemp.pyか?

2番目の質問は、デコレータを上に配置することについてですf@replace。これを行うと、このデコレータでテキストをinspect.getsourcelines返します。のテキストfから手動で削除できます。fただし、複数のデコレータが適用されている可能性があるため、これは非常に危険です。そこで、私は古いスタイルの装飾構文に頼りましたf = remove_1(f)。しかし、それでも、通常の装飾技術を使用することは可能@replaceですか?

4

2 に答える 2

1

execソースでステートメントを呼び出すことにより、一時ファイルの作成を回避できます。(コンパイルをさらに制御したい場合は、compile事前に明示的に呼び出すこともできますが、コンパイルは自動的に行われるため、必要ありません。)正しく呼び出すと、名前空間からグローバル変数にアクセスする場合に関数が正しく機能するという追加の利点があります。そのモジュールの。execexecexec

2番目の質問で説明されている問題は、実行中にデコレータを一時的にブロックすることで解決できます。そうすれば、デコレータは他のすべてのものと同じように残りますが、操作はできません。

これが更新されたソースです。

from __future__ import print_function

import sys


def a():
    print('a()')


def comment_1(s):
    lines = s.split('\n')
    return '\n'.join(line.replace(';','#;',1) if line.strip().startswith('1;') else line for line in lines)

_blocked = False

def remove_1(f):
    global _blocked
    if _blocked:
        return f
    import inspect
    source = inspect.getsource(f)    
    new_source = comment_1(source)
    env = sys.modules[f.__module__].__dict__
    _blocked = True
    try:
        exec new_source in env
    finally:
        _blocked = False
    return env[f.__name__]


@remove_1
def f():
    1;a()
    print('Some statements 1')
    1;a()
    print('Some statements 2')


f()

def remove_1(f):    
    import inspect
    source = inspect.getsource(f)    
    new_source = comment_1(source)
    env = sys.modules[f.__module__].__dict__.copy()
    exec new_source in env
    return env[f.__name__]
于 2012-09-02T18:41:44.773 に答える
0

user4815162342の回答にあるソリューションの修正バージョンを残しておきます。質問へのコメントで示唆されたように、モジュールを使用astしての一部を削除します。それを作るために、私は主にこの記事の情報に依存しました。f

aこの実装は、スタンドアロン式としてのすべての出現を削除します。

from __future__ import print_function
import sys
import ast
import inspect


def a():
    print('a() is called')


_blocked = False

def remove_1(f):
    global _blocked
    if _blocked:
        return f
    import inspect
    source = inspect.getsource(f)

    a = ast.parse(source) #get ast tree of f

    class Transformer(ast.NodeTransformer):
        '''Will delete all expressions containing 'a' functions at the top level'''
        def visit_Expr(self, node): #visit all expressions
            try:
                if node.value.func.id == 'a': #if expression consists of function with name a
                    return None #delete it
            except(ValueError):
                pass
            return node #return node unchanged
    transformer = Transformer()
    a_new = transformer.visit(a)
    f_new_compiled = compile(a_new,'<string>','exec')

    env = sys.modules[f.__module__].__dict__
    _blocked = True
    try:
        exec(f_new_compiled,env)
    finally:
        _blocked = False
    return env[f.__name__]


@remove_1
def f():
    a();a()
    print('Some statements 1')
    a()
    print('Some statements 2')


f()

出力は次のとおりです。

Some statements 1
Some statements 2
于 2012-09-02T22:18:58.650 に答える