open()如何使用和不使用`with`?

Pet*_*ood 13 python

我想写一个类似的函数open.我希望能够用它来呼叫它with,但也没有with.

当我使用时contextlib.contextmanager,它使我的功能正常工作with:

@contextmanager
def versioned(file_path, mode):
    version = calculate_version(file_path, mode)
    versioned_file = open(file_path, mode)
    yield versioned_file
    versioned_file.close()
Run Code Online (Sandbox Code Playgroud)

所以,我这样使用它:

with versioned('file.txt', 'r') as versioned_file:
    versioned_file.write(...)
Run Code Online (Sandbox Code Playgroud)

如何在没有的情况下使用它with:

versioned_file = versioned('file.txt', 'r')
versioned_file.write(...)
versioned_file.close()
Run Code Online (Sandbox Code Playgroud)

它抱怨说:

AttributeError: 'GeneratorContextManager' object has no attribute 'write'
Run Code Online (Sandbox Code Playgroud)

pok*_*oke 12

问题是contextmanager只提供了那个; 要在with语句中使用的上下文管理器.调用函数并没有返回文件对象,而是一个特殊的背景下产生,其提供__enter____exit__功能.如果您希望with语句和"正常"赋值都起作用,那么您必须将一些对象作为函数的返回值,该对象完全可用并且还提供上下文函数.

您可以通过创建自己的类型并手动提供上下文功能来轻松完成此操作:

class MyOpener:
    def __init__ (self, filename):
        print('Opening {}'.format(filename))
    def close (self):
        print('Closing file.')
    def write (self, text):
        print('Writing "{}"'.format(text))
    def __enter__ (self):
        return self
    def __exit__ (self, exc_type, exc_value, traceback):
        self.close()
Run Code Online (Sandbox Code Playgroud)
>>> f = MyOpener('file')
Opening file
>>> f.write('foo')
Writing "foo"
>>> f.close()
Closing file.

>>> with MyOpener('file') as f:
        f.write('foo')

Opening file
Writing "foo"
Closing file.
Run Code Online (Sandbox Code Playgroud)


Ste*_*sop 6

你有这个:

@contextmanager
def versioned(file_path, mode):
    # some setup code
    yield versioned_file
    # some teardown code
Run Code Online (Sandbox Code Playgroud)

你的基本问题当然是yield来自上下文管理器的内容来自with语句as,但不是你的函数返回的对象.您需要一个返回行为类似于对象open()返回的函数的函数.也就是说,一个自我产生的上下文管理器对象.

你是否可以做到这一点取决于你可以做什么类型的versioned_file.如果你不能改变它,那么你基本上没有运气.如果您可以更改它,那么您需要实现PEP 343中指定的__enter____exit__函数.

但是,在您的示例代码中,它已经拥有它,并且您的拆卸代码与已在上下文退出时执行的操作相同.因此,根本不需要使用contextlib,只需返回结果即可open().

对于其他的例子,你确实需要__enter__并且__exit__,如果你喜欢的风格contextlib(谁不?),你可以弥合两件事情.写一个context用装饰@contextmanager和屈服的功能self.然后执行:

def __enter__(self):
    self.context = context() # if context() is a method use a different name!
    return self.context.__enter__()
def __exit__(self, *args):
    return self.context.__exit__(*args)
Run Code Online (Sandbox Code Playgroud)

无论你发现这比将设置代码分解__enter__为拆解代码更好还是更差,这基本取决于你__exit__.我通常觉得它更好.