【问题标题】:How do I write a decorator that restores the cwd?如何编写恢复 cwd 的装饰器?
【发布时间】:2010-09-15 04:57:45
【问题描述】:

如何编写一个装饰器,将当前工作目录恢复到调用装饰函数之前的状态?换句话说,如果我在执行os.chdir() 的函数上使用装饰器,则在调用函数后不会更改 cwd。

【问题讨论】:

  • 你问了这个问题并在 3 分钟内自己回答了,因为……?显然,即使在提出问题之前,您就已经有了答案(几乎无法改进)。我真的很想知道你的推理。
  • FAQ 说“问和回答你自己的编程问题也很好”。它列出了三个必需的问题标准,“你不知道答案”不是其中之一。
  • 我已经编写了代码,但结果证明(重构后)我不需要它。我认为 stackoverflow 是一个存档的好地方,也许其他人可以从中受益。
  • 我认为“Jeopardy”评论只是应该意味着标题应该仍然是“我如何为 X 编写一个装饰器?”,而不是“这里是 X 的有用装饰器”。我同意问题中的注释可以避免人们在您打字时浪费时间在重复的解决方案上。但他们可能会比你的更好......
  • 我很高兴我提出了这个问题,因为 codeape 改进了我的回答,而 ΤZΩΤZΙΟΥ 想出了一个我没有想到的上下文管理器。

标签: python decorator cwd


【解决方案1】:

path.py 模块(在处理 python 脚本中的路径时应该使用它)有一个上下文管理器:

subdir = d / 'subdir' #subdir is a path object, in the path.py module
with subdir:
  # here current dir is subdir

#not anymore

(学分来自 Roberto Alsina 的 this blog post

【讨论】:

  • 如果 path.py 现在是内置的,也许你应该回答stackoverflow.com/questions/3899761/…
  • 不幸的是它不是,但谢谢我不知道这个问题
  • 我想我误解了你的答案。您的意思是上下文管理器内置于 path.py 中吗? (我以为你的意思是 path.py 现在已内置到 Python 中。)
  • 因为 Daryl Spitzer 假设 path.pypathlib 相同,您也可以假设他们的上下文管理器的行为方式相同,但事实并非如此:pathlib.Path 不接触CWD 而是充当来自open() 的上下文管理器(“关闭”可能打开的文件描述符)。
【解决方案2】:

给出的答案没有考虑到包装函数可能引发异常。在这种情况下,该目录将永远不会被恢复。下面的代码在前面的答案中添加了异常处理。

作为装饰者:

def preserve_cwd(function):
    @functools.wraps(function)
    def decorator(*args, **kwargs):
        cwd = os.getcwd()
        try:
            return function(*args, **kwargs)
        finally:
            os.chdir(cwd)
    return decorator

作为上下文管理器:

@contextlib.contextmanager
def remember_cwd():
    curdir = os.getcwd()
    try:
        yield
    finally:
        os.chdir(curdir)

【讨论】:

    【解决方案3】:

    已经给出了装饰者的答案;它按要求在函数定义阶段工作。

    使用 Python 2.5+,您还可以选择在函数 call 阶段使用上下文管理器执行此操作:

    from __future__ import with_statement # needed for 2.5 ≤ Python < 2.6
    import contextlib, os
    
    @contextlib.contextmanager
    def remember_cwd():
        curdir= os.getcwd()
        try: yield
        finally: os.chdir(curdir)
    

    如果需要,可以在函数调用时使用:

    print "getcwd before:", os.getcwd()
    with remember_cwd():
        walk_around_the_filesystem()
    print "getcwd after:", os.getcwd()
    

    这是一个不错的选择。

    编辑:我按照 codeape 的建议添加了错误处理。由于我的答案已被投票,因此提供一个完整的答案是公平的,所有其他问题除外。

    【讨论】:

    • 而且可以用来写前面提到的装饰器:)
    • 错误处理是否需要显式的 try/finally?我认为上下文管理器的重点是 MANAGER.__exit__ 总是被调用。但是,我从来没有尝试过 contextlib 中的装饰器,所以我不知道问题是什么。
    • 不,它不需要明确的try/finally,但您很可能需要try/except 子句来处理故障。
    【解决方案4】:
    def preserve_cwd(function):
       def decorator(*args, **kwargs):
          cwd = os.getcwd()
          result = function(*args, **kwargs)
          os.chdir(cwd)
          return result
       return decorator
    

    它的使用方法如下:

    @preserve_cwd
    def test():
      print 'was:',os.getcwd()
      os.chdir('/')
      print 'now:',os.getcwd()
    
    >>> print os.getcwd()
    /Users/dspitzer
    >>> test()
    was: /Users/dspitzer
    now: /
    >>> print os.getcwd()
    /Users/dspitzer
    

    【讨论】:

      猜你喜欢
      • 2021-01-31
      • 2021-01-28
      • 2015-03-22
      • 2014-12-26
      • 2023-03-28
      • 1970-01-01
      • 2011-04-20
      • 2018-12-10
      • 2012-10-28
      相关资源
      最近更新 更多