Jupyter - Split Classes in multiple Cells

Viewed 10178

I wonder if there is a possibility to split jupyter classes into different cells? Lets say:


#first cell:
class foo(object):
    def __init__(self, var):
        self.var = var

#second cell
    def print_var(self):
       print(self.var)

For more complex classes its really annoying to write them into one cell. I would like to put each method in a different cell.

Someone made this this last year but i wonder if there is something build in so i dont need external scripts/imports.

And if not, i would like to know if there is a reason to not give the opportunity to split your code and document / debug it way easier.

Thanks in advance

5 Answers

Here's a decorator which lets you add members to a class:

import functools
def update_class(
    main_class=None, exclude=("__module__", "__name__", "__dict__", "__weakref__")
):
    """Class decorator. Adds all methods and members from the wrapped class to main_class

    Args:
    - main_class: class to which to append members. Defaults to the class with the same name as the wrapped class
    - exclude: black-list of members which should not be copied
    """

    def decorates(main_class, exclude, appended_class):
        if main_class is None:
            main_class = globals()[appended_class.__name__]
        for k, v in appended_class.__dict__.items():
            if k not in exclude:
                setattr(main_class, k, v)
        return main_class

    return functools.partial(decorates, main_class, exclude)

Use it like this:

#%% Cell 1
class MyClass:
    def method1(self):
        print("method1")
me = MyClass()

#%% Cell 2
@update_class()
class MyClass:
    def method2(self):
        print("method2")
me.method1()
me.method2()

This solution has the following benefits:

  • pure python
  • Doesn't change the inheritance order
  • Effects existing instances

Medhat Omr's answer provides some good options; another one I found that I thought someone might find useful is to dynamically assign methods to a class using a decorator function. For example, we can create a higher-order function like the one below, which takes some arbitrary function, gets its name as a string, and assigns it as a class method.

def classMethod(func):
    setattr(MyClass, func.__name__, func)
    return func

We can then use the syntactic sugar for a decorator above each method that should be bound to the class;

@classMethod
def get_numpy(self):
    return np.array(self.data)

This way, each method can be stored in a different Jupyter notebook cell and the class will be updated with the new function each time the cell is run.

I should also note that since this initializes the methods as functions in the global scope, it might be a good idea to prefix them with an underscore or letter to avoid name conflicts (then replace func.__name__ with func.__name__[1:] or however characters at the beginning of each name you want to omit. The method will still have the "mangled" name since it is the same object, so be wary of this if you need to programmatically access the method name somewhere else in your program.

Related