上下文管理器适合这项工作吗?

发布于 2024-12-18 04:26:30 字数 2005 浏览 7 评论 0原文

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

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

The code pasted below does the following:

  • creates an import hook
  • creates a context manager which sets the meta_path and cleans on exit.
  • dumps all the imports done by a program passed in input in imports.log

Now I was wondering if using a context manager is a good idea in this case, because actually I don't have the standard try/finally flow, but just a set up and clean up.

Another thing — with this line:

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

does cc become None? Shouldn't it be a CollectorContext object?

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()

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

过度放纵 2024-12-25 04:26:30

我觉得这个概念是可以的。另外,我认为没有任何理由反对将清理内容放在 finally: 子句中,因此上下文管理器非常适合。

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

如果您不想这样做,请更改您的 __enter__ 方法返回其他内容

此方法返回的值绑定到使用此上下文管理器的 with 语句的 as 子句中的标识符。

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)

I think this concept is ok. As well, I don't see any reasons against having the clean-up stuff in a finally: clause, so the context manager fits perfectly.

Your cc is None, because you told it to be so.

If you don't want that, change your __enter__ method to return something else:

The value returned by this method is bound to the identifier in the as clause of with statements using this context manager.

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"

and then

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)
揪着可爱 2024-12-25 04:26:30

如果您总是希望进行清理,则应该使用上下文管理器。如果您使用低级特殊方法实现上下文管理器,我不确定您在哪里使用 try..finally 。如果您使用 @contextmanager 装饰器,则可以在“自然”的方式,所以这就是你使用 try..finally 而不是将异常作为参数获取的地方。

另外,cc 将是您从 __enter__() 返回的值。在您的情况下,。我理解上下文管理器设计的方式是返回值是“上下文”。上下文管理器所做的就是设置和清理发生其他事情的上下文。例如,数据库连接将创建事务,并且数据库操作发生在这些事务的范围内。

也就是说,上述内容只是为了提供最大的灵活性。直接创建一个上下文(管理自身)并返回 self 并没有什么问题,或者如果您不需要使用 with 中的上下文值,甚至不返回任何内容,都没有问题。 >。由于您不在任何地方使用 cc ,因此您可以直接执行而不用担心返回值:

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

If you always want the cleanup to occur, you should use a context manager. I'm not sure where you use try..finally if you implement the context manager using the low-level special methods. If you use the @contextmanager decorator, you code the context manager in a "natural" way, so that's where you use try..finally instead of getting the exception as a parameter.

Also, cc will be the value you return from __enter__(). In your case, None. The way I understand the context manager design is that the return value is the "context". What the context manager does is set up and clean up contexts in which something else happens. E.g. a database connection will create transactions, and database operations happen in the scope of those transactions.

That said, the above is just there to provide maximum flexibility. There's nothing wrong with just creating a context (that manages itself) directly and returning self, or even not returning anything if you don't need to use the context value inside the with. Since you don't use cc anywhere, you could just do and not worry about the return value:

with CollectorContext(cl, sys.argv, 'imports.log'):
        progname = sys.argv[0]
        code = compile(open(progname).read(), progname, 'exec')
        exec(code)
你列表最软的妹 2024-12-25 04:26:30

谢谢大家,现在它工作顺利,我实际上想要返回一些东西,因为我想将“运行”封装在上下文管理器中,所以我得到如下所示的东西。

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

Thanks everyone now it works smoothly, I actually wanted with to return something because I wanted to encapsulate the "run" inside the context manager, so I get something as below.

Moreover, now I store the old sys.argv and restore it on exit, probably not fundamental but still a nice thing to do I think..

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()
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文