user8817894
user8817894

Reputation:

Yield n lines from a Generator class in Python

I want to print n lines from a file everytime a Generator class is called.

I have tried the following:

class FileReader:
    def __init__(self, file):
        with open(file, 'r') as fin:
            read_file = fin.read()

            # gen-comp yielding stripped lines
            lines = (line.strip() for line in read_file)
            print(lines)

This simply returns all the lines.

Upvotes: 2

Views: 1459

Answers (4)

strubbly
strubbly

Reputation: 3507

You say you want a generator, and so a simple solution is just to write one directly, using yield. Something like this perhaps (though it might be possible to improve the looping structure of the code):

def FileReader(filename, length):
    with open(filename, 'r') as fin:
        while True:
            group = []
            for i in range(length):
                try:
                    group.append(next(fin).strip())
                except StopIteration:
                    break
            if not group:
                break
            yield group

This then, for example, will print a list of 8 lines each time:

for g in FileReader(filename, 8):
    print(g)

Or here's an alternative that I prefer. I've knocked up an iterator building block, in the spirit of itertools, which groups up any Iterable into lists of a specified length. You can then apply this directly to a file object to do what you want:

import itertools

def group(iterable, length):
    for key,group in itertools.groupby(
                 enumerate(iterable),
                 key=lambda elem : elem[0] // length):
        yield [elem[1] for elem in group]

So this can then be used to just wrap the file object like this:

fin = open(filename, "r")
for g in group(fin, 8):
    print([e.strip() for e in g])

Note that in this case, to preserve the generality of the group function, I've omitted the strip and therefore had to do it at the end.

Upvotes: 1

PyPingu
PyPingu

Reputation: 1747

Your file object is already a generator.

class FileReader:
    def __init__(self, file, lines=3):
        self.fd = open(file, 'r')
        self.lines = lines

    def __call__(self):
        for i in range(self.lines):
            print(self.fd.readline())

This will just print empty lines when you reach the end of the file, if that's not what you want then you can try:

import os

class FileReader:
    def __init__(self, file, lines=3):
        self.fd = open(file, 'r')
        self.lines = lines
        self.end = self.fd.seek(0, os.SEEK_END)
        self.fd.seek(0)

    def __call__(self):
        for i in range(self.lines):
            if self.fd.tell() == self.end:
                print('Reached EOF')
                return
            print(self.fd.readline())

Upvotes: 0

han solo
han solo

Reputation: 6600

You could implement a __call__ method like,

import sys
from itertools import islice

class FileReader:
    def __init__(self, fname, len=3):
        self.fname = fname
        self._len = len

    def __enter__(self):
        self.fd = open(self.fname, 'r')
        return self

    def __call__(self):
        return list(islice(self.fd, self._len))

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


with FileReader(sys.argv[1]) as f:
    print(f())
    print(f())

Upvotes: 3

Chris Doyle
Chris Doyle

Reputation: 12190

I am not that familiar with class programing or generators but the following i drafted seems to fill your criteria to print x number of lines each time the object is called

class FileReader:
    def __init__(self, file, num_lines=10):
        fin = open(file, 'r')
        self.file_handle = fin
        self.num_lines = num_lines

    def __next__(self):
        for i in range(self.num_lines):
            yield self.file_handle.readline().strip()

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

    def __str__(self):
        return "".join(list(next(self)))

myfile = FileReader('query4.txt', 2)
print(myfile)
print(myfile)

INPUT FILE

this is some data
this is other data
data is fun
data is weird
this is the 5th line

As you have stripped new line chars in your original program i have done the same so assume you want to concat the X lines together. If thats not the case then just remove the .strip()

OUTPUT

this is some datathis is other data
data is fundata is weird

Upvotes: 1

Related Questions