在__iter__中使用

时间:2019-07-06 22:49:29

标签: python

我必须打开一些文本文件并按行读取,并仅返回包含数字的字符串。

with中使用_iter__语句是一个好主意吗?喜欢:

def __iter__(self):
    with open(file_name) as fp:
        for i in fp:
            if is_number(i):
                yield i

更好的方法是:

def __enter__(self):
    self._fp = open(self._file, 'r')
    return self

def __exit__(self, exc_type, exc_val, exc_tb):
    self._fp.close()

def __iter__(self) -> int:
    for tracker_id in self._fp:
        if re.search('\d', tracker_id):
            yield int(tracker_id)

3 个答案:

答案 0 :(得分:2)

您需要一个生成器,而不是上下文管理器。要创建一个,您可以尝试这样的事情:

import re

def filter_lines(filename: str, pattern: str):
    p = re.compile(pattern)
    with open(filename) as f:
        for line in f:
            if re.search(p, line):
                yield line

if __name__ == "__main__":
    for line in filter_lines('myfile.txt', '\d'):
        print(line)

如果要多次使用它们,请记住要编译它们的正则表达式。

答案 1 :(得分:1)

我认为代码的第二种形式更好。

第一个版本依赖于__iter__返回的迭代器,仅在进行迭代时存在。如果在不取消分配迭代器的情况下碰巧发生了迭代中断,则该文件可能会无限期保持打开状态。

像这样使用它通常是安全的,因为如果循环体中发生异常,则对象及其迭代器将被垃圾回收,因为除for之外没有其他对迭代器的引用循环本身(尽管如果关闭了垃圾收集,则在CPython之外的其他解释器上可能并不安全):

for x in Whatever():  # assuming your methods are in a class named Whatever
    # do stuff

这种替代用法可能并不安全,因为迭代器将存在于堆栈框架中,并且在处理异常时可能会存在很长一段时间:

it = iter(Whatever())

for x in it:
    # do stuff

您的代码的第二种形式明确表明,调用代码负责确保正确清理资源。您可以使用类似这样的名称进行调用,并且可以确信如果引发异常,文件将被关闭:

with Whatever() as w:
    for x in w:
        # do stuff

第二个版本的代码的主要缺点是,您不能同时对同一个对象进行多次迭代,因为它们共享同一个文件对象。如果有人想在同一个文件上进行两次迭代,则需要创建该类的多个实例。

如果对象本身是迭代器,则它的一次性使用性质可能会更自然,而不仅仅是可迭代(例如,这就是文件对象的工作方式):

class Whatever:
    def __enter__(self):
        self._fp = open(self._file, 'r')
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self._fp.close()

    def __iter__(self):
        return self

    def __next__(self)
        tracker_id = next(self._fp)
        while re.search('\d', tracker_id) is None:
            tracker_id = next(self._fp)
        return int(tracker_id)

请注意,我们故意不尝试捕获文件上调用StopIteration可能引发的任何next异常,因为这将表明我们也已完成。

答案 2 :(得分:0)

在第一种情况下,当请求迭代时将打开文件。如果完成多次迭代,则可能会导致额外的I / O。在第二种情况下,即使未进行任何迭代,在with语句中使用对象时也始终打开文件。

需要权衡取舍-一种方法可能更有效,具体取决于对象的使用方式。如果您需要支持多种使用模式,则可能需要结合使用这些方法。在第一次请求迭代时延迟打开文件,然后在__exit__中将其关闭。如果您不需要这种灵活性,请选择最适合该对象使用方式的选项。