Python 为什么不是';上下文管理器关闭文件描述符?

Python 为什么不是';上下文管理器关闭文件描述符?,python,windows,python-3.x,mmap,contextmanager,Python,Windows,Python 3.x,Mmap,Contextmanager,我正在尝试创建一个使用mmap的上下文管理器,它本身就是一个上下文管理器。起初,我遇到了一个愚蠢的打开文件问题,答案很快解释了为什么它不能按预期工作 根据这些信息,我尝试了两种不同的方法来纠正这个问题,但都没有奏效 第一种方法是使用contextlib的@contextmanager装饰器: from contextlib import contextmanager import os import mmap #contextmanager def memory_map(filename, a

我正在尝试创建一个使用
mmap
的上下文管理器,它本身就是一个上下文管理器。起初,我遇到了一个愚蠢的打开文件问题,答案很快解释了为什么它不能按预期工作

根据这些信息,我尝试了两种不同的方法来纠正这个问题,但都没有奏效

第一种方法是使用
contextlib
@contextmanager
装饰器:

from contextlib import contextmanager
import os
import mmap

#contextmanager
def memory_map(filename, access=mmap.ACCESS_WRITE):
    size = os.path.getsize(filename)
    fd = os.open(filename, os.O_RDWR)
    print('about to yield')
    with mmap.mmap(fd, size, access=access) as m:
        yield m
    print('in finally clause')
    os.close(fd)  # Close the associated file descriptor.

test_filename = 'data'

# First create the test file.
size = 1000000
with open(test_filename, 'wb') as f:
     f.seek(size - 1)
     f.write(b'\x00')

# Read and modify mmapped file in-place.
with memory_map(test_filename) as m:  # Causes AttributeError: __enter__
    print(len(m))
    print(m[0:10])
    # Reassign a slice.
    m[0:11] = b'Hello World'

# Verify that changes were made
print('reading back')
with open(test_filename, 'rb') as f:
     print(f.read(11))

# Delete test file.
# Causes:
# PermissionError: [WinError 32] The process cannot access the file because it
# is being used by another process: 'data'
os.remove(test_filename)
但结果是:

回溯(最近一次呼叫最后一次):
文件“memory_map.py”,第27行,在
内存映射(测试文件名)为m:#导致AttributeError:u输入__
AttributeError:\u输入__
在下一次尝试中,我尝试显式创建上下文管理器类:

import os
import mmap

class MemoryMap:
    def __init__(self, filename, access=mmap.ACCESS_WRITE):
        print('in MemoryMap.__init__')
        size = os.path.getsize(filename)
        self.fd = os.open(filename, os.O_RDWR)
        self.mmap = mmap.mmap(self.fd, size, access=access)

    def __enter__(self):
        print('in MemoryMap.__enter__')
        return self.mmap

    def __exit__(self, exc_type, exc_value, traceback):
        print('in MemoryMap.__exit__')
        os.close(self.fd)  # Close the associated file descriptor.
        print('  file descriptor closed')


test_filename = 'data'

# First create the test file.
size = 1000000
with open(test_filename, 'wb') as f:
     f.seek(size - 1)
     f.write(b'\x00')

# Read and modify mmapped file in-place.
with MemoryMap(test_filename) as m:
    print(len(m))
    print(m[0:10])
    # Reassign a slice.
    m[0:11] = b'Hello World'

# Verify that changes were made
print('reading back')
with open(test_filename, 'rb') as f:
     print(f.read(11))

# Delete test file.
# Causes PermissionError: [WinError 32] The process cannot access the file
# because it is being used by another process: 'data'
os.remove(test_filename)
这使它更进一步,但是
PermissionError
又回来了,这让我非常困惑,因为文件描述符在该版本中已关闭,正如您在生成的输出中所看到的:

内存映射中的
。\uuuu init__
在MemoryMap中输入__
1000000
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
在内存映射中。\u退出__
文件描述符已关闭
回首
“你好,世界”
回溯(最近一次呼叫最后一次):
文件“memory_map2.py”,第47行,在
删除(测试文件名)
PermissionError:[WinError 32]进程无法访问该文件,因为另一个进程正在使用该文件:“data”
看来我又被卡住了。有什么问题(以及如何解决)的想法吗?此外,如果两者都可以修复,如果你有意见,哪一个更好

解决方案

两个代码段中都有错误。第一个是一个简单的印刷错误。
contextmanger
decorator已被注释掉。应该是:

@contextmanager  # Leading "#" changed to "@".
def memory_map(filename, access=mmap.ACCESS_WRITE):
    size = os.path.getsize(filename)
    fd = os.open(filename, os.O_RDWR)
    ...
在第二种情况下,这是因为
mmap
本身没有在
\uuuuu exit\uuuuuu()
方法中关闭,只是关闭了关联的文件描述符。我从未想到过这一点,因为引发的异常与第一个案例中的异常相同

    def __exit__(self, exc_type, exc_value, traceback):
        print('in MemoryMap.__exit__')
        self.mmap.close()  # ADDED.
        os.close(self.fd)  # Close the associated file descriptor.
        print('  file descriptor closed')

在第二次尝试时,需要关闭内存映射文件:

def __exit__(self, exc_type, exc_value, traceback):
    self.mm.close()
    print('in MemoryMap.__exit__')
    os.close(self.fd)  # Close the associated file descriptor.
    print('  file descriptor closed')

这对你有帮助吗?我只读了错误
#contextmanager
不是
@contextmanager
。而且,您的“in finally子句”实际上不在
finally
子句中。@ElisByberi:谢谢。我实际上看了这个问题,虽然它很相似,但没有看到它是如何应用的,但根据下面的答案,可能不是…@user2357112:是的,这是第一个片段的问题。一定是把我上一个问题的复制和粘贴搞砸了。我会接受的,因为你把它作为正式答案发布了……谢谢。它和我的问题下的一条评论为这两个版本提供了修复。我猜我一直在混淆关闭
mmap
对象和关闭与其关联的文件。在Windows上,一个文件不能被多个进程访问。您需要先关闭mmap对象,然后再关闭文件对象。答案是正确的。