Python ValueError:对上下文管理器范围内的已关闭文件执行I/O操作

Python ValueError:对上下文管理器范围内的已关闭文件执行I/O操作,python,python-3.x,python-3.6,with-statement,contextmanager,Python,Python 3.x,Python 3.6,With Statement,Contextmanager,我读过很多类似的问题,但大多数问题都是通过修正缩进来解决的,所以要么我不懂,要么有一些简单的方法来修正我的问题,但我不认为这与识别有关。 因此,我有一个函数,它基本上使用两个*.txt文件执行一些操作,并返回一个名为的生成器对象tuples,其中包含一些我需要稍后查找的信息 def master_reader(file1, file2): with open(file1, "r", encoding="utf-8") as genomas: with open(file2

我读过很多类似的问题,但大多数问题都是通过修正缩进来解决的,所以要么我不懂,要么有一些简单的方法来修正我的问题,但我不认为这与识别有关。 因此,我有一个函数,它基本上使用两个*.txt文件执行一些操作,并返回一个名为
的生成器对象tuples
,其中包含一些我需要稍后查找的信息

def master_reader(file1, file2):
    with open(file1, "r", encoding="utf-8") as genomas:
        with open(file2, "r", encoding="utf-8") as listas:
            l = parser_listas(listas)
            f = parser_genomas(genomas)
            f = map(intifier, f)
            f = (people_maker(linea, l) for linea in f)
            f = map(genotipo_getter, f)
            f = map(fen_getter, f)
            return f
问题是,当我调用它并将其分配给变量时,一切正常。但我需要将其用作参数,以便在每次需要时调用它,以便对其执行某些查询:

print(valor_caracteristica("TCT", "Luna  Lovegood", master_reader("genomas.txt", "listas.txt")))
但我有一个例外:

Original exception was:
Traceback (most recent call last):
  File "lib.py", line 204, in <module>
    print(valor_caracteristica("TCT", "Luna  Lovegood", master_reader("genomas.txt", "listas.txt")))
  File "lib.py", line 194, in valor_caracteristica
    a = next(filter(lambda x: x.nombre == nombre, file))
  File "lib.py", line 185, in <genexpr>
    f = (people_maker(linea, l) for linea in f)
ValueError: I/O operation on closed file.
原始异常是:
回溯(最近一次呼叫最后一次):
文件“lib.py”,第204行,在
印刷品(valor_caracteristica(“TCT”、“Luna Lovegood”、master_reader(“genomas.txt”、“listas.txt”))
文件“lib.py”,第194行,在valor_caracteristica中
a=next(过滤器(lambda x:x.nombre==nombre,文件))
文件“lib.py”,第185行,在
f=(f中直线A的人物(直线A,l)
ValueError:对关闭的文件执行I/O操作。
map()
返回迭代器。只有当您在
map()
对象上循环时,它才会将函数实际应用于输入iterable的下一个元素

因此,在您开始使用
map
对象和底层生成器表达式之前,不会从文件中读取任何数据。您在函数外部执行此操作,此时文件已经关闭,因为
return f
语句退出了函数并扩展了上下文

解决方法是要么不使用像
map()
这样的惰性对象,要么将函数设置为生成器函数。在处理完文件之前,后者不会退出(并用块发出退出上下文的信号)

这可以非常简单地通过使用的收益率来实现:

def master_reader(file1, file2):
    with open(file1, "r", encoding="utf-8") as genomas:
        with open(file2, "r", encoding="utf-8") as listas:
            l = parser_listas(listas)
            f = parser_genomas(genomas)
            f = map(intifier, f)
            f = (people_maker(linea, l) for linea in f)
            f = map(genotipo_getter, f)
            yield from map(fen_getter, f)
yield from
保持生成器打开,直到底层
map()
对象引发
StopIteration

一个快速演示来说明区别:

>>> from contextlib import contextmanager
>>> @contextmanager
... def democtx():
...     print('Entering the context')
...     yield
...     print('Exiting the context')
...
>>> def return_map():
...     with democtx():
...         return map(lambda x: x**2, range(3))
...
>>> def yield_from_map():
...     with democtx():
...         yield from map(lambda x: x**2, range(3))
...
>>> example1 = return_map()
Entering the context
Exiting the context
>>> example2 = yield_from_map()
>>> next(example2)
Entering the context
0
>>> next(example2)
1
>>> next(example2)
4
>>> next(example2)
Exiting the context
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>从contextlib导入contextmanager
>>>@contextmanager
... def democtx():
...     打印('输入上下文')
...     产量
...     打印('退出上下文')
...
>>>def return_map():
...     使用democtx():
...         返回图(λx:x**2,范围(3))
...
>>>def yield_from_map():
...     使用democtx():
...         地图收益率(λx:x**2,范围(3))
...
>>>示例1=返回\u映射()
进入上下文
退出上下文
>>>示例2=从图()中得出的产量
>>>下一步(示例2)
进入上下文
0
>>>下一步(示例2)
1.
>>>下一步(示例2)
4.
>>>下一步(示例2)
退出上下文
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
停止迭代

请注意,对于
example1
来说,上下文是如何在返回时立即退出的,而
example2
直到迭代开始才打开上下文,直到我们在
map()
对象上完全迭代后才关闭上下文。

因为您使用的是
python3
map
构造返回生成器,这些生成器被延迟计算。因此,当计算生成器
f
时,文件处理程序可能已经关闭(打开文件的上下文管理器会确保这一点)

解决方案是要么评估其中的地图,要么根本不使用它们,然后使用列表理解

return list(f) # evaluate the map statement therein.

# or you should just return a generator as @MartinPieters suggested.

谢谢你的详细解释,Martijn!直到现在,我才意识到在上下文管理器中使用惰性对象的含义。