10

Hey I have a following problem, I need to open a file in __init__(), and with check function I need to check if strings/numbers in rows of this file are the same or not. If they aren't it should return True if they are it should return False, and if there are no more lines None. I do not know how many lines are there going to be in the file. My code is working kind of, tester is giving me 90%, but it says I do not close the file, I understand why it is saying, but do not know where to put the close. However if I opened it with with it should be working but I do not know how to get it working that way.

My Code:

class Program:
    def __init__(self, file_name):
        self.t = open(file_name, 'r')

    def check(self):
        row = self.t.readline()

        array = []

        for i in row.split():
            if i not in array:
                array.append(i)

        if row.split() == []:
            return None
        elif array == row.split():
            return True
        else:
            return False

"""
#testing

if __name__ == '__main__':
    u = Program('file.txt')
    z = True
    while z is not None:
        z = u.check()
        print(z)

"""

Example file:

15 9 22
2014 2015 2014 2015
p py pyt pyth pytho python
ab ab ab ab ab
Matis
  • 611
  • 2
  • 11
  • 27
  • Yes, you never `close` the file. Is the file small enough that you can just read it into memory? Or could you structure your own code as a context manager? – jonrsharpe Dec 19 '14 at 21:32
  • @jonrsharpe Not sure if I exactly understand the second part of the question, but the file should not be somewhat big, you can see the example file up, so it should be similar size, or just a few more lines. – Matis Dec 19 '14 at 21:34
  • If your class has two methods, one of which is `__init__()`, it shouldn't be a class but a function. – Roland Smith Dec 19 '14 at 21:35
  • 3
    So why not keep the *content* of the file, rather than the file handle? Then you can `close` the file in `__init__`. – jonrsharpe Dec 19 '14 at 21:35
  • @RolandSmith I was given a structure, where there was `class Program`, `def __init__()`, and `def check` and I had to work around with this – Matis Dec 19 '14 at 21:36
  • why do you need to open the file in `__init__` rather than in the method where you actually use the file? and as Roland said, why do you even need a class here? – Anentropic Dec 19 '14 at 21:37
  • @Anentropic Also this was said in my task, that I need to open it in `__init__()` – Matis Dec 19 '14 at 21:39
  • @jonrsharpe I am sorry, but can you please explain that a little more, I am sorry but I am just learning python, and also struggling with english, so I did not fully get your comment. – Matis Dec 19 '14 at 21:40
  • then I agree @jonrsharpe's suggestion... read the contents of the file into an attribute (eg an array of lines) in your `__init__` method and then use that in the `check` method – Anentropic Dec 19 '14 at 21:41
  • @Matis Classes can be great, but they are also [overused](http://www.youtube.com/watch?v=o9pEzgHorH0). See the video that I linked to for a more thorough explanation. [Simple is better than complex](https://www.python.org/dev/peps/pep-0020/). – Roland Smith Dec 19 '14 at 21:41
  • @RolandSmith It sounds like this is a class assignment, where for some reason the instructor has insisted that the above structure be used. But I agree with you: if that's all there is to this assignment, then this should be a function, not a class. – jme Dec 19 '14 at 21:42
  • @RolandSmith Thank you for the video, and as jme said, it is an class assignment. And I had to do it this way, I had to use the class and all the structure that is above. Sometimes you have to do silly things, but you have to do them. – Matis Dec 19 '14 at 21:45
  • @jonrsharpe I probably can't just keep the content of the file, as the testing area says it will be running until in finds an empty line, I could maybe add one empty line at the end, but don't know how would the tester react to that. – Matis Dec 19 '14 at 21:55
  • 4
    Is `close()` in `__del__` a good practice? Any body can comment on this solution? – minion Jan 24 '18 at 17:23

2 Answers2

10

Since you open the file in one method and use it in another, you can't use the with statement internal to the class. You can add a method to close the file and let the closing be the caller's problem. A popular solution for the caller is to use contextlib.closing. Putting it all together...

class Program:
    def __init__(self, file_name):
        self.t = open(file_name, 'r')

    def check(self):
        ...

    def close(self):
        if self.t:
            self.t.close()
            self.t = None


import contextlib
with contextlib.closing(Program('myfile.txt')) as program:
    program.check()
tdelaney
  • 73,364
  • 6
  • 83
  • 116
  • 2
    What happens if we just call close from __del__(self)? – Mr.WorshipMe Aug 23 '16 at 11:01
  • 1
    @Mr.WorshipMe - Good question. It would make sense to also call close in the `__del__`. Whether that is sufficient depends on other design goals. For instance, class instances may live a long time after the `with` clause but it may be desirable to close the file as soon as possible. There is a risk that the `__del__` call is deferred on some python implementations leaving the file open or circular reference delay the close until garbage collection. – tdelaney Aug 23 '16 at 19:13
  • The problem is, __del__ is called AFTER the object has been dereferenced (when references to the class object are zero), meaning the name holding the open file no longer exists when you call __del__. – Alan Leuthard Jun 07 '17 at 18:19
0

I guess you should instantiate the class and the "check" method should check one row at a time.

This works, but if your teacher has not told you about the yield statement he will know you are cheating:

class Program(object):
    def __init__(self, fname):
        self.line_checker = self.make_checker(fname)

    def make_checker(self, fname):
        with open(fname) as i:
            for line in i:
                yield len(set(line.split())) < 2

    def check(self):
        try:
            return self.line_checker.next()
        except StopIteration:
            return None
Paulo Scardine
  • 73,447
  • 11
  • 124
  • 153