Как мне написать декоратор, который восстанавливает текущий рабочий каталог до того, каким он был до вызова декорированной функции? Другими словами, если я использую декоратор для функции, которая выполняет os.chdir()
, cwd не изменится после вызова функции.
Как написать декоратор, восстанавливающий cwd?
Ответы (4)
Модуль 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
(кредиты принадлежат этому сообщению в блоге Роберто Альсины)
path.py
совпадает с pathlib
, вы также можете предположить, что их диспетчеры контекста ведут себя таким же образом, но это не так: pathlib.Path
не касается CWD, а вместо этого действует как диспетчер контекста из open()
(закрывает потенциально открытый дескриптор файла).
- person Pugsley; 03.10.2020
Дан ответ для декоратора; он работает на этапе определения функции по запросу.
В Python 2.5+ у вас также есть возможность сделать это на этапе вызова функции с помощью диспетчера контекста:
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()
Это хороший вариант.
РЕДАКТИРОВАТЬ: я добавил обработку ошибок, как это было предложено кодовой лентой. Поскольку за мой ответ проголосовали, будет справедливо предложить исчерпывающий ответ, не считая всех других вопросов.
try/finally
не требуется, но вам, скорее всего, понадобится предложение try/except
для обработки сбоев.
- person tzot; 21.10.2011
В приведенных ответах не учитывается, что обернутая функция может вызвать исключение. В этом случае каталог никогда не будет восстановлен. В приведенном ниже коде к предыдущим ответам добавлена обработка исключений.
как декоратор:
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)
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