相关代码如下:
for file in files:
with readfile(file) as openfile:
molecules.append(process_file_fn(openfile))
我从上面的代码中收到此错误:
src/datamodules/components/edm/process.py", line 92, in process_xyz_files with readfile(file) as openfile: AttributeError: __enter__
这是readfile的定义:
if tarfile.is_tarfile(data):
tardata = tarfile.open(data, "r")
files = tardata.getmembers()
def readfile(data_pt):
return tardata.extractfile(data_pt)
我的数据是1234.xyz.tar.bz2
任何对我的见解/建议都很感激。先谢谢你了
我尝试定义在函数和循环中读取的模式,但遇到了相同的错误。
我想说你的用例不需要上下文管理器。 我想你可以直接写一个电话:
openfile = readfile(file)
。
如果您确实需要上下文管理器,您可以像这样定义您的函数:
@contextlib.contextmanager
def readfile(data_pt): yield tardata.extractfile(data_pt)
contextlib.contextmanager
将为您定义方法__enter__
和__exit__
。
您需要编写一个带有
__enter__
和 __exit__
方法的类才能使用这样的 with
语句。
查看此处的响应:在 python 中的自定义类中实现使用“with object() as f”
如果您不想实现上下文管理器,您可以尝试将 for 循环更改为:
for file in files:
openfile = readfile(file)
molecules.append(process_file_fn(openfile))
不确定这是否有帮助,但无论如何......
您可以编写自己的上下文管理器类来处理单个 tar 文件。为了简单起见,我们假设我们想要做的就是提取档案中的已知成员。因此我们的类可能看起来像这样:
import tarfile
class TarfileHandler:
def __init__(self, filename):
self._filename = filename
self._fd = None
@property
def fd(self):
if self._fd is None:
self._fd = tarfile.open(self._filename)
return self._fd
def extract(self, member):
try:
return self.fd.extractfile(member)
except Exception:
pass
def __enter__(self):
return self
def __exit__(self, *_):
if self._fd:
self._fd.close()
self._fd = None
现在让我们设计一个用例。我们知道 tar 文件在哪里。我们知道它包含“foo.txt”。我们想要提取“foo.txt”并将其复制到某个地方。
TARFILE = 'mytarfile.tar'
MEMBER = 'foo.txt'
TARGET = 'foo.txt'
with TarfileHandler(TARFILE) as tfh:
if data := tfh.extract(MEMBER):
with open(TARGET, 'wb') as out:
out.write(data.read())
希望这向您展示如何实现上下文管理器类以及如何使其适应您的需求
到目前为止,其他答案都没有解释到底是什么导致了
AttributeError: __enter__
。
归根结底,OP 正在尝试使用来自 TarFile.extractfile()
的返回值作为上下文管理器。
TarFile.extractfile()
返回的值可以是 either 和 io.BufferedReader
或 None
:
对象。对于所有其他现有成员,返回io.BufferedReader
。 ...None
查看这是如何在
tarfile
源中实现的。
虽然
BufferedReader
可以用作上下文管理器,但None
显然不能。
因此,例如,如果您对代表目录的成员调用
AttributeError: __enter__
,就会出现 extractfile()
。
OP示例的解决方法可能是检查成员是否是文件:
for file in files:
if file.isfile(): # <-- NEW
with readfile(file) as openfile:
...
请注意,在OP的示例中,
files = tardata.getmembers()
意味着file
实际上是一个存档member,以TarInfo
实例的形式存在。为了避免混淆,我将分别重命名为 members
和 member
。