0

I have a simple piece of code that tries to give a convenience to file in Python.

class File:
    def __init__(this, *args):
        this._file = file(*args)

    def __del__(this):
        this._file.close()

def createCallForwarder(method):
    return lambda obj,*args: method(obj._file, *args)

_dict = file.__dict__
for (k,v) in zip(_dict.keys(), _dict.values()):
    if not (k.startswith('__') and k.endswith('__')):
        if v.__class__.__name__ == 'method_descriptor':
            File.__dict__[k] = createCallForwarder(v)

# get the repr method
File.__repr__ = createCallForwarder(dict_proxy['__repr__'])

If i change File to inherit from object, it does not let me assign the methods.

Why is it different?

Aaditya Kalsi
  • 1,039
  • 8
  • 16
  • What in heavens name are you trying to achieve with your code? Why not use a subclass of `file`, or use a `__getattr__` hook to proxy methods? – Martijn Pieters Sep 28 '13 at 16:48
  • Use `setattr` if you want to do the same thing in new style classes: `setattr(File, k, createCallForwarder(v))` – Ashwini Chaudhary Sep 28 '13 at 16:50
  • Thanks! That's exactly what I needed. I dont have that much experience with Python. I was just trying to get a File object that closes itself so I don't have to care. I don't want to leak file objects. – Aaditya Kalsi Sep 28 '13 at 16:52
  • 2
    Just a style tip: Python, by convention, uses `self` instead of `this`. –  Sep 28 '13 at 16:54
  • And don't use `this` in python, Python's convention is to use `self`. – Ashwini Chaudhary Sep 28 '13 at 16:54
  • 2
    @AadityaKalsi: Python file objects **already do that**. They **have** a `__del__` handler of their own. – Martijn Pieters Sep 28 '13 at 16:56
  • And at the same time, relying on an object's finalizer (your own or the built-in `file`) to clean up resources is a really bad idea. It *mostly* works in *most* circumstances on CPython. But as soon as you get used to not cleaning up explicitly, the files get caught up in reference cycles or your code is run in a different Python implementations, and once that happens you're screwed because you have a thousand leaks and no experience with preventing them. Just use a `with` statement, it's just a matter of habit. –  Sep 28 '13 at 16:59

2 Answers2

4

You should not be accessing __dict__ directly at all.

Use a __getattr__ method to proxy calls to the underlying self._file object instead:

class File(object):
    def __init__(self, *args):
        self._file = open(*args)

    def __getattr__(self, name):
        return getattr(self._file, name)

I've also switched the code to best-practices; using self instead of this and using open() instead of file().

For new-style objects (inheriting from object), use setattr() to set arbitrary attributes. There is no need to use a call forwarder wrapper, however. You could have taken the bound methods of self._file and set those directly on self too:

class File(object):
    def __init__(self, *args):
        self._file = open(*args)
        for name in dir(self._file):
            setattr(self, name, getattr(self._file, name))

If all you wanted was a file object that auto-closes on garbage collection, then you went through a lot of trouble for nothing. Python file objects already have a __del__ handler that does exactly that. It is just not exposed as an explicit __del__ function, instead the C implementation uses a deallocation function that calls close_the_file(f) when deallocated.

Best practice, however, is to use file objects as context manager, using the with statement:

with open(somefilename) as fileobj:
    # do all sorts with fileobj

# here, fileobj will have been closed automatically.

Quoting from the file.close() documentation:

As of Python 2.5, you can avoid having to call this method explicitly if you use the with statement. For example, the following code will automatically close f when the with block is exited:

from __future__ import with_statement # This isn't required in Python 2.6

with open("hello.txt") as f:
    for line in f:
        print line,
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • Thanks! That was very helpful and I prefer the __getattr__ way. The only lil problem I have is that it doesn't play well with readline for auto completion. The reason I have this implementation is because I have other objects who may be writing to the same file multiple times, and to open multiple times is costly. – Aaditya Kalsi Sep 28 '13 at 17:10
  • @AadityaKalsi: Proxying the file object doesn't buy you *anything* in that case. File objects already auto-close in CPython. – Martijn Pieters Sep 28 '13 at 17:12
  • 1
    @AadityaKalsi: You can implement a [`__dir__` method](http://docs.python.org/2/library/functions.html#dir) perhaps to help with autocompletion. – Martijn Pieters Sep 28 '13 at 17:14
0

I was just trying to get a File object that closes itself

Use a with statement which will (among other things), close files for you:

with open('somefile.txt') as the_file:
   for line in the_file:
      # do something with line

# Once outside the with block, the file is automatically closed
print('somefile.txt is closed here') 
Burhan Khalid
  • 169,990
  • 18
  • 245
  • 284