24

I'm getting a NameError in the __del__ function of a class. I do not understand why 'open' is not accessible inside the function __del__. I am using Python 3.4.0

Python Code:

class Contoller:

    ...

    def __del__(self):
        store = {}
        ...
        pickle.dump(store, open('data.p', 'wb'))    

class MyWindow(Gtk.Window):

    def __init__(self):
        ...
        self.controller = Contoller(self)
        ...
        self.connect("delete-event", self.quit)
        ...

    ...

    def quit(self, widget, event):
        del self.controller
        Gtk.main_quit()

Error Message:

Traceback (most recent call last):
  File "main.py", line 69, in __del__
NameError: name 'open' is not defined
user3595184
  • 241
  • 1
  • 2
  • 3

3 Answers3

13

user3595184's code used to work until Python 3.4. I know because I've just run into the same issue, calling open() within __del__. Apparently built-in functions no longer work, because of a change in when __del__ is run as the interpreter shuts down.

UPDATE: See https://docs.python.org/3/library/weakref.html, especially the section on finalizers. I have several classes that encapsulate access to resources that had __del__() methods to unlock or clean up as soon as the application would exit, for any reason. Catching all exceptions or exits at the application level and explicitly calling cleanup methods is inconvenient and bug-prone. I'll try using a local finalizer instead.

UPDATE: I'm having better luck with __enter__() and __exit__(), putting the top-level object in a with clause. It appears at least the __exit__() gets called no matter how, so I can call the cleanup methods (__exit__()'s themselves) of all subordinate objects. That's working for me.

Quip11
  • 194
  • 1
  • 7
  • `weakref.finalize()` behaves like `atexit.register()`: good at exiting an application, but `mod_wsgi` doesn't call either when it finishes serving a URI. I might look into gc.collect(), but for now I'm having to trap all exit paths from my application and explicitly call cleanup code. Yuck. – Quip11 Apr 23 '15 at 20:37
4

It is generally not a good idea to rely on __del__ for anything in Python.

Better use a normal method and call it, self.controller.store() or whatever name you find best.

Related discussion is for example in I don't understand this python __del__ behaviour

UPDATE: atexit.register may be what you want, https://docs.python.org/2/library/atexit.html . As featured in this nice article at "Common Mistake #10: Misusing the __del__ method" http://www.toptal.com/python/top-10-mistakes-that-python-programmers-make

As for the explanation, this discussion tells: "At interpreter shutdown, the module's global variables are set to None before the module itself is released." -- perhaps that applies to builtins too, http://bugs.python.org/issue5099

Community
  • 1
  • 1
antont
  • 2,676
  • 1
  • 19
  • 21
  • Of course this will work as a workaround. This however does not explain the behavior. Besides that calling a function directly does also cause multiple execution if multiple instances exist. – user3595184 May 02 '14 at 07:28
  • 1
    True, it does not explain the behaviour. However it is not a workaround but more like a bugfix -- AFAIK you really should not rely on `__del__` being called. – antont May 02 '14 at 11:00
  • That said I have also a guess for the reason. `__del__` is not called when you say `del` (which just removes your reference to the object) but upon garbage collection (or however the py impl at hand works). As you quit the whole application on the next line after `del` I bet the python interpreter then starts shutdown too etc. and I suppose removes builtins like `open` before it gets to clear your object too. – antont May 02 '14 at 11:12
-3

I think it just a scope problem.

self.controller = Contoller(self)

I don't know why you should pass MyWindow class as argument when initiating Controller class. Try to change it to:

self.controller = Contoller()
kangaswad
  • 765
  • 6
  • 12