如何解决AttributeError: __enter__?

问题描述 投票:0回答:4

相关代码如下:

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

任何对我的见解/建议都很感激。先谢谢你了

我尝试定义在函数和循环中读取的模式,但遇到了相同的错误。

python tar tarfile
4个回答
1
投票

我想说你的用例不需要上下文管理器。 我想你可以直接写一个电话:

openfile = readfile(file)

如果您确实需要上下文管理器,您可以像这样定义您的函数:

@contextlib.contextmanager
def readfile(data_pt): yield tardata.extractfile(data_pt)

装饰器

contextlib.contextmanager
将为您定义方法
__enter__
__exit__


0
投票

您需要编写一个带有

__enter__
__exit__
方法的类才能使用这样的
with
语句。

查看此处的响应:在 python 中的自定义类中实现使用“with object() as f”

如果您不想实现上下文管理器,您可以尝试将 for 循环更改为:

for file in files:
    openfile = readfile(file)
    molecules.append(process_file_fn(openfile))

0
投票

不确定这是否有帮助,但无论如何......

您可以编写自己的上下文管理器类来处理单个 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())

希望这向您展示如何实现上下文管理器类以及如何使其适应您的需求


0
投票

到目前为止,其他答案都没有解释到底是什么导致了

AttributeError: __enter__

原因

归根结底,OP 正在尝试使用来自 TarFile.extractfile()

返回值作为上下文管理器

但是,

TarFile.extractfile()
返回的值可以是 either
io.BufferedReader
None
:

...如果 member 是常规文件或链接,则返回

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

© www.soinside.com 2019 - 2024. All rights reserved.