2017-07-11 13 views
2

私はPython 3用の«モジュールエキスパンダー»ツールを開発しようとしましたが、いくつか問題があります。Pythonモジュールをインポートせずに模倣する方法は?

与えられたPythonスクリプトmain.pyの場合、ツールは機能的に同等のPythonスクリプトexpanded_main.pyを生成します。各インポート文は、インポートされたモジュールの実際のコードで置き換えられます。これは、インポートされたPythonのソースコードがアクセス可能であることを前提としています。正しい方法で仕事をするには、Pythonの組み込みモジュールastと、astorというサードパーティのツールを使って、ASTをPythonソースにダンプすることができます。このインポートエキスパンダーの動機は、スクリプトを1つのバイトコードチャンクにコンパイルすることができるようにするためです。Python VMはモジュールのインポートを考慮してはいけません(MicroPythonなどでは便利です)。

最も単純なケースは、文です:これを変換するために

from import my_module1 import * 

、私のツールは、ファイルmy_module1.pyを探し、それがこのファイルの内容によってimport文を置き換えます。次に、expanded_main.pyは、モジュールが通常の方法でインポートされたかのように、my_moduleで定義されている任意の名前にアクセスできます。私はトリックを明らかにする可能性のある微妙な副作用は気にしない。また、簡略化するために、以前のインポート(アスタリスク付き)としてfrom import my_module1 import a, b, cを扱い、副作用の可能性については考慮していません。ここまでは順調ですね。

ここに私の要点があります。インポートのこの味を扱うことができる方法:

class my_module2: 
    # content of my_module2.py 
    … 
:私の最初のアイデアはインデントPythonのファイルの内容をモジュールと同じ名前を持つクラスを作成してコピーすることによって、これを模倣した
import my_module2 

これは実際には多くの場合動作しますが、悲しいことに、これにはいくつかの不具合があることが判明しました。その1つは、モジュールで定義されたグローバル変数を参照する本体を持つ関数で失敗したことです。たとえば、次の2つのPythonのファイルを考えてみます。

# my_module2.py 
g = "Hello" 
def greetings(): 
    print (g + " World!") 

と実行時

# main.py 
import my_module2 
print(my_module2.g) 
my_module2.greetings() 

main.pyプリント "Hello""Hello World!"。さて、私のエキスパンダーツールは、これを生成しなければならない。 expanded_main.pyの実行時に

# expanded_main.py 
class my_module2: 
    g = "Hello" 
    def greetings(): 
     print (g + " World!") 
print(my_module2.g) 
my_module2.greetings() 

を、最初のprint文はOKです("Hello")が、挨拶機能は、例外が発生します:NameError: name 'g' is not defined。実際に何が起こる はモジュールmy_module2

  • gがでグローバル変数、
  • があるということであるクラスmy_module2は、gmy_module2.gと呼ばれるべきクラス変数、です。あなたは関数、クラス、... my_module2.py中を定義し、他の関数、クラス、...同じmy_module2.pyの中でそれらを参照したい場合

他の同様の副作用が起こります。

これらの問題がどのように解決できるか考えてみましょうか?

モジュールを模倣できる他のPython構造体はありますか?

最後の注意:私は、ツールがネストされたインポート(再帰)の1度を気にして、同じモジュールの2倍の多重インポートに注意することを知っています。私はこれらの話題についてここでは議論するつもりはないと思う。

+0

@Ignacio:私のメッセージで述べたように、Python * 3 *です。まあ、私は3.4と3.6でこれをテストしました。私はPython 2では、例外が異なっていることを知っています(「TypeError:unboundメソッドのgreetings()は、少なくともPython 2.6で、my_module2インスタンスを最初の引数として(何も持っていません)」と呼び出さなければなりません)。 –

+0

なぜあなたはこれをやっていますか? –

+0

@Chris;その目的は自己完結型スクリプトを用意して、自己完結型のPythonバイトコードファイルにコンパイルできるようにすることです。これを実行するには、実行時にPythonのimportiロジックに依存する必要はありません。ファイルシステムにアクセスすることもできません。これは、リソースが制約されているシステム(マイクロコントローラ上のMicroPythonなど)に役立ちます。 –

答えて

1

モジュールのソースコードは、ファンクションのスコープで、具体的にはインスタンスメソッドを実行できます。属性は、対応するクラスの__getattr__を定義し、初期関数のコピーを保持することによって利用可能にすることができます。locals()。ここではいくつかのサンプルコードは次のとおりです。

class Importer: 
    def __init__(self): 

     g = "Hello" 

     def greetings(): 
      print (g + " World!") 

     self._attributes = locals() 

    def __getattr__(self, item): 
     return self._attributes[item] 


module1 = Importer() 
print(module1.g) 
module1.greetings() 

入れ子の輸入がImporterのインスタンスでそれらを同じ方法で置き換えることによって、自然に処理されます。重複したインポートも問題ではありません。

+0

ありがとう!これは賢明で、これはまさに私が探していたものです。 –

関連する問題