5

以下に貼り付けたコードは、次のことを行います。

  • インポートフックを作成します
  • meta_pathを設定し、終了時にクリーンアップするコンテキスト マネージャを作成します。
  • imports.log の入力に渡されたプログラムによって行われたすべてのインポートをダンプします

この場合、コンテキストマネージャーを使用するのが良い考えかどうか疑問に思っていました。実際には標準try/finallyフローがなく、セットアップとクリーンアップだけがあるからです。

別のこと—次の行で:

with CollectorContext(cl, sys.argv, 'imports.log') as cc:

ccになりますNoneか?CollectorContextそれはオブジェクトであるべきではありませんか?

from __future__ import with_statement
import os
import sys

class CollectImports(object):
    """
    Import hook, adds each import request to the loaded set and dumps
    them to file
    """

    def __init__(self):
        self.loaded = set()

    def __str__(self):
        return str(self.loaded)

    def dump_to_file(self, fname):
        """Dump the loaded set to file
        """
        dumped_str = '\n'.join(x for x in self.loaded)
        open(fname, 'w').write(dumped_str)

    def find_module(self, module_name, package=None):
        self.loaded.add(module_name)


class CollectorContext(object):
    """Sets the meta_path hook with the passed import hook when
    entering and clean up when exiting
    """

    def __init__(self, collector, argv, output_file):
        self.collector = collector
        self.argv = argv
        self.output_file = output_file

    def __enter__(self):
        self.argv = self.argv[1:]
        sys.meta_path.append(self.collector)

    def __exit__(self, type, value, traceback):
        # TODO: should assert that the variables are None, otherwise
        # we are quitting with some exceptions
        self.collector.dump_to_file(self.output_file)
        sys.meta_path.remove(self.collector)


def main_context():
    cl = CollectImports()

    with CollectorContext(cl, sys.argv, 'imports.log') as cc:
        progname = sys.argv[0]
        code = compile(open(progname).read(), progname, 'exec')
        exec(code)


if __name__ == '__main__':
    sys.argv = sys.argv[1:]
    main_context()
4

3 に答える 3

3

このコンセプトでいいと思います。同様に、句にクリーンアップを含めることに反対する理由が見当たらないfinally:ので、コンテキスト マネージャーは完全に適合します。

あなたccは です。あなたNoneがそう言ったからです。

それが望ましくない場合は、__enter__メソッドを変更して別のものを返します

このメソッドによって返される値は、このコンテキスト マネージャーを使用するステートメントのas句の識別子にバインドされます。with

def __enter__(self):
    self.argv = self.argv[1:]
    sys.meta_path.append(self.collector)
    return self
    # or
    return self.collector
    # or
    return "I don't know what to return here"

その後

with CollectorContext(cl, sys.argv, 'imports.log') as cc:
    print cc, repr(cc) # there you see what happens.
    progname = sys.argv[0]
    code = compile(open(progname).read(), progname, 'exec')
    exec(code)
于 2011-11-23T14:23:40.217 に答える
2

常にクリーンアップを実行する場合は、コンテキストマネージャーを使用する必要があります。try..finally低レベルの特殊なメソッドを使用してコンテキストマネージャーを実装する場合、どこで使用するかわかりません。@contextmanagerデコレータを使用する場合は、コンテキストマネージャを「自然な」方法でコーディングするためtry..finally、例外をパラメータとして取得する代わりに、ここで使用します。

また、ccから返す値になります__enter__()。あなたの場合、None。コンテキストマネージャーの設計を理解する方法は、戻り値が「コンテキスト」であるということです。コンテキストマネージャーが行うことは、他の何かが発生するコンテキストをセットアップしてクリーンアップすることです。たとえば、データベース接続はトランザクションを作成し、データベース操作はそれらのトランザクションのスコープ内で発生します。

とはいえ、上記は最大限の柔軟性を提供するためのものです。コンテキスト(それ自体を管理する)を直接作成して返すselfだけで問題はありません。または、内でコンテキスト値を使用する必要がない場合は何も返さないこともありますwith。どこでも使用しないのでcc、戻り値を気にせずに使用できます。

with CollectorContext(cl, sys.argv, 'imports.log'):
        progname = sys.argv[0]
        code = compile(open(progname).read(), progname, 'exec')
        exec(code)
于 2011-11-23T14:28:17.230 に答える
1

スムーズに動作するようになりました。コンテキストマネージャー内で「実行」をカプセル化したかったので、実際に何かを返したかったので、以下のようになりました。

さらに、今は古い sys.argv を保存し、終了時にそれを復元します。おそらく基本的ではありませんが、それでも良いことだと思います..

class CollectorContext(object):
    """Sets the meta_path hook with the passed import hook when
    entering and clean up when exiting
    """

    def __init__(self, collector, argv, output_file):
        self.collector = collector
        self.old_argv = argv[:]
        self.output_file = output_file
        self.progname = self.old_argv[1]

    def __enter__(self):
        sys.argv = self.old_argv[1:]
        sys.meta_path.append(self.collector)
        return self

    def __exit__(self, type, value, traceback):
        # TODO: should assert that the variables are None, otherwise
        # we are quitting with some exceptions
        self.collector.dump_to_file(self.output_file)
        sys.meta_path.remove(self.collector)
        sys.argv = self.old_argv[:]

    def run(self):
        code = compile(open(self.progname).read(), self.progname, 'exec')
        exec(code)


def main_context():
    cl = CollectImports()

    with CollectorContext(cl, sys.argv, 'imports.log') as cc:
        cc.run()
于 2011-11-23T14:39:41.367 に答える