简体   繁体   中英

IPython autoreload gives error for repeated calls to Python2 super()

I am prototyping stuff in an IPython notebook that is loading Python2 code from modules on my computer. I activated reloading magic commands to make it easier to go back-and-forth between my text editor and the notebook when I make a code change and re-run a cell to see its affect:

%reload_ext autoreload
%autoreload 2

I am working in Python 2.7.10 because I working with some legacy code that doesn't compile for 3. Part of my work is extending some classes in this legacy code and overloading some of their methods. But, I also need to call some of the original base methods to do important work. For example:

class LegacyBase:

    def important_method(self):
        #old stuff i'll need

class NewClass(LegacyBase):

    #overload base method
    def important_method(self):
       #do some new stuff

       while 1:
           #call old method to do stuff
           super(NewClass, self).important_method() #use old Python2 super calls :(

           #some break conditions

When I call important_method() with some NewClass instance the first time in my notebook (meaning, after a kernel reset) it runs fine. The loop is such that the super call is happening more than once! No errors

But, if I go and modify some code to my new method in my text editor and go back to the IPython cell and call it again I get the following error at the line in my overloaded important_method() where the super call is made.

TypeError: super(type, obj): obj must be an instance or subtype of type

Note: I tried just naming my new method a different name because I thought it was something to do with the overloading method calling itself again but that didn't help. Also, I want them to be the same name because this is an API method and I want users of the legacy code to be able to call the same methods they know from before.

Any idea how to use reloading in IPython notebooks with these Python2 super calls?

Thanks!

Your existing instances are still pointing to the old class from before the reload.

You need to either re-create the instances, or update their __class__ attribute:

instance_of_newclass.__class__ = NewClass

Reproducing the problem

I use a module reload_test.py modeled after your example:

class LegacyBase:

    def important_method(self):
        print 'doing'

class NewClass(LegacyBase):

    def important_method(self):
        for x in range(3):
            super(NewClass, self).important_method()

In my IPython Notebook:

In [1]: %reload_ext autoreload
        %autoreload 2

In [2]: import reload_test

In [3]: nc = reload_test.NewClass()

Calling a method throws this exception:

In [4]: nc.important_method()

    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-5-09eb0eac244b> in <module>()
    ----> 1 nc.important_method()

    /Users/mike/tmp/reload_test.py in important_method(self)
          9 
         10        for x in range(3):
    ---> 11            super(NewClass, self).important_method()

    TypeError: must be type, not classobj

The class LegacyBase needs to inherit from object :

class LegacyBase(object):

    def important_method(self):
        print 'doing'

class NewClass(LegacyBase):

    def important_method(self):
        for x in range(3):
            super(NewClass, self).important_method()

Now, I can reproduce your problem. Works the first time:

In [5]: nc = reload_test.NewClass()

In [6]: nc.important_method()
    doing
    doing
    doing

After modifying the file in an editor:

class LegacyBase(object):

    def important_method(self):
        print 'doing'

class NewClass(LegacyBase):

    def important_method(self):
        for x in range(3):
            super(NewClass, self).important_method()

Calling the methods throws an exception:

In [7]: nc.important_method()

     ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-8-09eb0eac244b> in <module>()
    ----> 1 nc.important_method()

    /Users/mike/tmp/reload_test.py in important_method(self)
          9 
         10        for x in range(3):
    ---> 11            super(NewClass, self).important_method()

    TypeError: super(type, obj): obj must be an instance or subtype of type

Solution

Just make a new instance:

In [8]: nc = reload_test.NewClass()

In [9]: nc.important_method()
    doing 2
    doing 2
    doing 2

In practice this means just re-executing the cell that holds nc = reload_test.NewClass() .

Might be a bit hacky, but I found setting the parent methods to new attributes in the constructor would enable auto-reloading.

class LegacyBase:

    def important_method(self):
        #old stuff i'll need

class NewClass(LegacyBase):

    def __init__(self, *args, **kwargs):
        self.__parent_important_method = super(NewClass, self).important_method
        super(NewClass, self).__init__(*args, **kwargs)

    #overload base method
    def important_method(self):
       #do some new stuff

       while 1:
           #call old method to do stuff
           self.__parent_important_method()
           #some break conditions

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM