上下文管理员是否适合这份工作?

时间:2011-11-23 14:00:45

标签: python decorator contextmanager

下面粘贴的代码执行以下操作:

  • 创建导入挂钩
  • 创建一个上下文管理器,用于设置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()

3 个答案:

答案 0 :(得分:3)

我认为这个概念没问题。同样,我没有看到任何理由反对在finally:子句中使用清理内容,因此上下文管理器非常适合。

您的ccNone,因为您说的是as

如果您不想这样做,请更改__enter__ method to return something else

  

此方法返回的值使用此上下文管理器绑定到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)

然后

{{1}}

答案 1 :(得分: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)

答案 2 :(得分:1)

感谢大家现在它运作顺利,我实际上想要返回一些内容,因为我想在上下文管理器中封装“run”,所以我得到如下内容。

此外,现在我存储旧的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()