2009-06-10 12 views
6

私は、オブジェクトの存続期間を通してアクティブな内部データベース接続を持っています。プログラムの実行の最後に、接続をコミットして閉じなければなりません。これまでは明示的にcloseメソッドを使用しましたが、これはやや面倒です。特に、呼び出しコードで例外が発生する可能性がある場合は特にそうです。オブジェクト破棄時の内部pysqlite接続のクリーンアップ

閉鎖には__del__メソッドを使用することを検討していますが、オンラインで読んだ後に懸念があります。これは有効な使用パターンですか?内部リソースが__del__で正しく解放されることを確認できますか?

This discussionも同様の質問がありましたが、満足のいく回答は見つかりませんでした。私は明示的なcloseメソッドを望んでおらず、withを使うことはオプションではありません。なぜなら、私のオブジェクトは単にopen-play-closeとして使われるのではなく、別の大きなオブジェクトのメンバとして保持されているからです。 GUIで実行している間にそれを使用します。

C++には完全に安全なリソースを解放できるデストラクタがありますので、Pythonにも合意されたものがあると思います。何らかの理由でそれは事実ではないようであり、コミュニティの多くは__del__に対して誓います。代わりに何がありますか?

答えて

5

あなたはこの暗示は、その接続を前提としているモジュールは、アプリケーション全体で同じオブジェクトを保持するので、接続モジュールを作成し、そしてatexitモジュール

# db.py: 
import sqlite3 
import atexit 

con = None 

def get_connection(): 
    global con 
    if not con: 
     con = sqlite3.connect('somedb.sqlite') 
    atexit.register(close_connection, con) 
    return con 

def close_connection(some_con): 
    some_con.commit() 
    some_con.close() 

# your_program.py 
import db 
con = db.get_connection() 
cur = con.cursor() 
cur.execute("SELECT ...") 

でそれを閉じるために機能を登録することができますあなたのアプリケーションでは、モジュールグローバルが提供する単一インスタンス(シングルトン)のように見えます。

そうでない場合は、デストラクタを使用できます。

しかし、デストラクタはガーベッジコレクタと循環参照でうまくいっていません(デストラクタが呼び出される前に循環参照を自分で削除する必要があります)。そうでない場合(複数の接続が必要です)、デストラクタ。周りの言葉を引用しないでください。

また、C++については、間違っています。 C++でデストラクタを使用する場合は、オブジェクトを定義するブロックが終了する(Pythonのwithなど)か、deleteキーワード(newで作成されたオブジェクトの割り当てを解除する)を使用すると呼び出されます。外側では、デストラクタではない明示的なclose()を使用する必要があります。だから、Pythonのようなものです。ガベージコレクタを持っているので、Pythonは "より良い"ものです。

+0

このような接続を使用するオブジェクトでは、「漏れやすい抽象化」の方法では、このような仕方がないのでしょうか?なぜ私はC++のようなデストラクタを使うことができないのですか? –

+0

@eliben:できます。しかし、デストラクタはガベージコレクタと循環参照(デストラクタが呼び出される前に循環参照を自分自身で削除する必要があります)には向いておらず、アプリケーション内の接続はグローバルモジュールが提供する単一インスタンス(シングルトン)のようです。そうでない場合(複数の接続が必要な場合)、デストラクタに行くことができます。周りの言葉を引用しないでください。 – nosklo

+0

再C++のデストラクタ、まったくそうではありません。正しいRAIIでコーディングすると、リソースへのポインターは、カウントが保証された方法で0に達したときに割り当て解除されるスマートポインター(おそらく参照カウント)として保持されます。しかし、これには余分な機械(スマートポインタ)も必要です。 –

6

withの文章をお読みください。そのユースケースを説明しています。

withステートメントで使用されている__enter____exit__メソッドを処理する "コンテキストマネージャ"クラスで接続をラップする必要があります。

詳細については、PEP 343を参照してください。


編集

class AnObjectWhichMustBeClosed(object): 
    def __enter__(self): 
     # acquire 
    def __exit__(self, type, value, traceback): 
     # release 
    def open(self, dbConnectionInfo): 
     # open the connection, updating the state for __exit__ to handle. 

class ALargerObject(object): 
    def __init__(self): 
     pass 
    def injectTheObjectThatMustBeClosed(self, anObject): 
     self.useThis = anObject 

class MyGuiApp(self): 
    def run(self): 
     # build GUI objects 
     large = ALargeObject() 
     with AnObjectWhichMustBeClosed() as x: 
      large.injectTheObjectThatMustBeClosed(x) 
      mainLoop() 

一部を「私のオブジェクトは、単にオープンプレイクローズとして使用されていませんが、他のメンバーとして維持され、大きなオブジェクト」人々はこれを「Dependency Injection」と「Inversion of Control」と呼んでいます。他の人々はこれをストラテジーと呼んでいます。 "ObjectThatMustBeClosed"は、大きなオブジェクトにプラグインされた戦略です。アセンブリは、GUIアプリケーションのトップレベルで作成されます。これは通常、データベースなどのリソースが取得されるためです。

+0

ここでは「ここで」使用することはできないと言いましたが、質問そのものに理由が説明されています。何か不足していますか?イベントに基づいて動作する大規模なGUIベースのクラスでオブジェクトがインスタンスとして保持されているときに、 'with'がどのように有効に使用できるのか説明できますか? –

+0

@ S.Lott:それはSOのバグでしょうか?私の質問には4つの段落があります - 第3は 'with'とGUIを言います –

+0

元々、あなたの解決策は幾分過度のものです。私は閉じられている必要があるそのようなオブジェクトが10個あると想像してください...私が必要としたのはデストラクタでした! –

関連する問題