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:,因此上下文管理器非常适合。

ccNone,因为你告诉它是这样的。

如果您不希望这样,请更改您的__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 回答