Jupyter - Split Classes in multiple Cells - python

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

Two solutions were provided to this problem on Github issue "Define a Python class across multiple cells #1243" which can be found here: https://github.com/jupyter/notebook/issues/1243
One solution is using a magic function from a package developed for this specific case called jdc - or Jupyter dynamic classes. The documentation on how to install it and how to use can be found on package url at https://alexhagen.github.io/jdc/
The second solution was provided by Doug Blank and which just work in regular Python, without resorting to any extra magic as follows:
Cell 1:
class MyClass():
def method1(self):
print("method1")
Cell 2:
class MyClass(MyClass):
def method2(self):
print("method2")
Cell 3:
instance = MyClass()
instance.method1()
instance.method2()
I tested the second solution myself in both Jupyter Notebook and VS Code, and it worked fine in both environments, except that I got a pylint error [pylint] E0102:class already defined line 5 in VS Code, which is kind of expected but still runs fine. Moreover, VS Code was not meant to be the target environment anyway.

I don't feel like that whole stuff to be a issue or a good idea... But maybe the following will work for you:
# First cell
class Foo(object):
pass
# Other cell
def __init__(self, var):
self.var = var
Foo.__init__ = __init__
# Yet another cell
def print_var(self):
print(self.var)
Foo.print_var = print_var
I don't expect it to be extremely robust, but... it should work for regular classes.
EDIT: I believe that there are a couple of situations where this may break. I am not sure if that will resist code inspection, given that the method lives "far" from the class. But you are using a notebook, so code inspection should not be an issue (?), although keep that in mind if debugging.
Another possible issue can be related to use of metaclasses. If you try to use metaclasses (or derive from some class which uses a metaclass) that may broke it, because metaclasses typically expect to be able to know all the methods of the class, and by dynamically adding methods to a class, we are bending the rules on the flow of class creation.
Without metaclasses or some "quite-strange" use cases, the approach should be safe-ish.
For "simple" classes, it is a perfectly valid approach. But... it is not exactly an expected feature, so (ab)using it may give some additional problems which I may not

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

There is no way to split a single class,
You could however, add methods dynamically to an instance of it
CELL #1
import types
class A:
def __init__(self, var):
self.var = var
a = A()
And in a different cell:
CELL #2
def print_var(self):
print (self.var)
a.print_var = types.MethodType( print_var, a )
Now, this should work:
CELL #3
a.print_var()

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.

thanks#Medhat Omr, it works for me for the #classmethod as well.
Base class in the first cell
class Employee:
# define two class variables
num_empl = 0
raise_amt = 1.05
def __init__(self, first, last, pay):
self.first = first
self.last = last
self.pay = pay
...
...
#classmethod in an another cell:
class Employee(Employee):
#classmethod
def set_raise_amt(cls, amount):
cls.raise_amt = amount
empl = Employee("Jahn", "Smith", 65000)
Employee.set_raise_amt(1.04)
print(empl.full_name() + " is getting " + str(empl.apply_raise()))

Related

Don't break if class instance is passed as initialization argument

I'm trying to add flexibility to a python class, so that it notices when one of the init arguments is already an instance of that class. Skip "Initial situation" if you don't mind, how I got here.
Initial situation
I have this class:
class Pet:
def __init__(self, animal):
self._animal = animal
#property
def present(self):
return "This pet is a " + self._animal
...
and there are many functions which accept an instance of this class as an argument (def f(pet, ...)). Everything worked as expected.
I then wanted to add some flexibility to the usage of these functions: if the caller passes a Pet instance, everything keeps on working as before. In all other cases, a Pet instance is created. One way to achieve that, is like this:
def f(pet_or_animal, ...):
if isinstance(pet_or_animal, Pet): #Pet instance was passed
pet = pet_or_animal
else: #animal string was passed
pet = Pet(pet_or_animal)
...
This also works as expected, but these lines are repeated in every function. Not DRY, not good.
Goal
So, I'd like to extract the if/else from each of the functions, and integrate it into the Pet class itself. I tried changing its __init__ method to
class PetA: #I've changed the name to facilitate discussion here.
def __init__(self, pet_or_animal):
if isinstance(pet_or_animal, PetA):
self = pet_or_animal
else:
self._animal = pet_or_animal
...
and start each function with
def f(pet_or_animal, ...):
pet = PetA(pet_or_animal)
...
However, that is not working. If a Pet instance is passed, everything is good, but if a string is called, a Pet instance is not correctly created.
Current (ugly) solution
What is working, is to add a class method to the class, like so:
class PetB: #I've changed the name to facilitate discussion here.
#classmethod
def init(cls, pet_or_animal):
if isinstance(pet_or_animal, PetB):
return pet_or_animal
else:
return cls(pet_or_animal)
def __init__(self, animal):
self._animal = animal
...
and also change the functions to
def f(pet_or_animal, ...):
pet = PetB.init(pet_or_animal) #ugly
...
Questions
Does anyone know, how to change class PetA so, that it has the intended behavior? To be sure, here is the quick test:
pb1 = PetB.init('dog')
pb2 = PetB.init(pb1) #correctly initialized; points to same instance as pb1 (as desired)
pa1 = PetA('cat')
pa2 = PetA(pa1) #incorrectly initialized; pa1 != pa2
More generally, is this the right way to go about adding this flexibility? Another option I considered was writing a separate function to just do the checking, but this too is rather ugly and yet another thing to keep track of. I'd rather keep everything neat and wrapped in the class itself.
And one final remark: I realize that some people might find the added class method (petB) a more elegant solution. The reason I prefer to add to the __init__ method (petA) is that, in my real-world use, I already allow for many different types of initialization arguments. So, there is already a list of if/elif/elif/... statements that check, just which of the possibilities is used by the creator. I'd like to extend that by one more case, namely, if an initialized instance is passed.
Many thanks
I believe your current "ugly" solution is actually the correct approach.
This pushes the flexibility up as far as possible, since it is messy. Even though python allows for arbitrary types and values to float around, your users and yourself will thank you for keeping that constrained to the outermost levels.
I would think of it as (don't need to implement it this way)
class Pet:
#classmethod
def from_animal(cls, ...):
...
#classmethod
def from_pet(cls, ...):
...
#classmethod
def auto(cls, ...):
if is_pet(...):
return cls.from_pet(...)
def __init__(cls, internal_rep):
...
etc.
It is a code smell if you don't know whether your function is taking an object or an initializer. See if you can do processing as up-front as possible with user input and standardize everything beyond there.
You could use a function instead to get the same behaviour you want:
def make_pet_if_required(pet_or_animal):
if isinstance(pet_or_animal, PetA):
return pet_or_animal
else:
return Pet(pet_or_animal)
And then:
def f(pet_or_animal, ...):
pet = make_pet_if_required(pet_or_animal)
...
For more "beauty" you can try turning that function call into a decorator.

Refer to parent's class method without parent's class name

I hope you are doing great. This questions is really about getting rid of the reference to base class.
Basically I want to collect all methods of a child class methods at the class level instead of the instance level, using a parent classmethod. However, I was told that the base class name is really long.
The first piece works but is really annoying because of the long name. Even in the clean version I have to do A.eat everytime.
I promise people won't define another method "eat" in any child like B. Can I actually get rid of the base class reference so that I can use #eat?
class IDontWantToDoThisButNameHasToBeThisLong(object):
a = []
#classmethod
def eat(cls, func):
cls.a.append(func)
class B(IDontWantToDoThisButNameHasToBeThisLong):
#IDontWantToDoThisButNameHasToBeThisLong.eat
def apple( self, x ):
print x
IDontWantToDoThisButNameHasToBeThisLong.eat( lambda x: x+1 )
x = B()
IDontWantToDoThisButNameHasToBeThisLong.a[0](x, 1)
print IDontWantToDoThisButNameHasToBeThisLong.a[1](1)
Clean version:
class A(object):
a = []
#classmethod
def eat(cls, func):
cls.a.append(func)
class B(A):
#A.eat
def apple( self, x ):
print x
A.eat( lambda x: x+1 )
x = B()
A.a[0](x, 1)
print A.a[1](1)
Sincerely,
The class IDontWantToDoThisButNameHasToBeThisLong is really just an object. In python, most thingsa are an object, so we can assign just about anything to a variable, including a class.
What you could do here is something like the following
class IDontWantToDoThisButNameHasToBeThisLong(object):
a = []
#classmethod
def eat(cls, func):
cls.a.append(func)
A = IDontWantToDoThisButNameHasToBeThisLong
class B(A):
#A.eat
def apple( self, x ):
print x
A.eat( lambda x: x+1 )
x = B()
IDontWantToDoThisButNameHasToBeThisLong.a[0](x, 1)
A.a[0](x, 1)
print IDontWantToDoThisButNameHasToBeThisLong.a[1](1)
There's no perfect solution for what you want to do, but there are a few different approaches that might be good enough.
To start with the simplest, you could give your long class a shorter name before using class method in the child classes:
class IDontWantToDoThisButNameHasToBeThisLong(object):
...
A = IDontWantToDoThisButNameHasToBeThisLong
# later code can use A.whatever()
Another option would be to move the decorator out of the class with the long name, so that your later code would refer to it directly as a global, rather than a class method. This would require it to be slightly redesigned (which might break things if you ever intend for there to be multiple different a lists that are accessed through the same decorator called via different classes):
class IDontWantToDoThisButNameHasToBeThisLong(object):
a = []
def eat(func):
IDontWantToDoThisButNameHasToBeThisLong.a.append(func) # only need to use the name once
return func # I suspect you want this too (a decorator should return a callable)
# later code can use #eat as a decorator, without referring to the long class name
A hybrid of those two approaches might be to leave the existing class method definition intact, but to create another global name for the bound method that's easier to access:
eat = IDontWantToDoThisButNameHasToBeThisLong.eat
A final possible approach would be to use fancier programming with metaclasses, or (if you're using Python 3.6) __init_subclass__ or similar, to achieve the goal you have in mind without needing to use a class method as a decorator. I'm not going to include code for that, since the best way to do this probably depends on more details of your design than what you've show in your example.

do's and don'ts of __init__ method

I was just wondering if it's considered wildly inappropriate, just messy, or unconventional at all to use the init method to set variables by calling, one after another, the rest of the functions within a class. I have done things like, self.age = ch_age(), where ch_age is a function within the same class, and set more variables the same way, like self.name=ch_name() etc. Also, what about prompting for user input within init specifically to get the arguments with which to call ch_age? The latter feels a little wrong I must say. Any advice, suggestions, admonishments welcome!
I always favor being lazy: if you NEED to initialize everything in the constructor, you should--in a lot of cases, I put a general "reset" method in my class. Then you can call that method in init, and can re-initialize the class instance easily.
But if you don't need those variables initially, I feel it's better to wait to initialize things until you actually need them.
For your specific case
class Blah1(object):
def __init__(self):
self.name=self.ch_name()
def ch_name(self):
return 'Ozzy'
you might as well use the property decorator. The following will have the same effect:
class Blah2(object):
def __init__(self):
pass
#property
def name():
return 'Ozzy'
In both of the implementations above, the following code should not issue any exceptions:
>>> b1 = Blah1()
>>> b2 = Blah2()
>>> assert b1.name == 'Ozzy'
>>> assert b2.name == 'Ozzy'
If you wanted to provide a reset method, it might look something like this:
class Blah3(object):
def __init__(self, name):
self.reset(name)
def reset(self, name):
self.name = name

What are the elegant ways to do MixIns in Python?

I need to find an elegant way to do 2 kinds of MixIns.
First:
class A(object):
def method1(self):
do_something()
Now, a MixInClass should make method1 do this: do_other() -> A.method1() -> do_smth_else() - i.e. basically "wrap" the older function. I'm pretty sure there must exist a good solution to this.
Second:
class B(object):
def method1(self):
do_something()
do_more()
In this case, I want MixInClass2 to be able to inject itself between do_something() and do_more(), i.e.: do_something() -> MixIn.method1 -> do_more(). I understand that probably this would require modifying class B - that's ok, just looking for simplest ways to achieve this.
These are pretty trivial problems and I actually solved them, but my solution is tainted.
Fisrt one by using self._old_method1 = self.method1(); self.method1() = self._new_method1(); and writing _new_method1() that calls to _old_method1().
Problem: multiple MixIns will all rename to _old_method1 and it is inelegant.
Second MixIn one was solved by creating a dummy method call_mixin(self): pass and injecting it between calls and defining self.call_mixin(). Again inelegant and will break on multiple MixIns..
Any ideas?
Thanks to Boldewyn, I've found elegant solution to first one (I've forgot you can create decorators on-the-fly, without modifying original code):
class MixIn_for_1(object):
def __init__(self):
self.method1 = self.wrap1(self.method1)
super(MixIn_for_1, self).__init__()
def wrap1(self, old):
def method1():
print "do_other()"
old()
print "do_smth_else()"
return method1
Still searching for ideas for second one (this idea won't fit, since I need to inject inside of old method, not outside, like in this case).
Solution for second is below, replacing "pass_func" with lambda:0.
I think, that can be handled in quite a Pythonic way using decorators. (PEP 318, too)
Here is another way to implement MixInClass1, MixinClass2:
Decorators are useful when you need to wrap many functions. Since MixinClass1 needs to wrap only one function, I think it is clearer to monkey-patch:
Using double underscores for __old_method1 and __method1 plays a useful role in MixInClass1. Because of Python's name-mangling convention, using the double underscores localizes these attributes to MixinClass1 and allows you to use the very same attribute names for other mix-in classes without causing unwanted name-collisions.
class MixInClass1(object):
def __init__(self):
self.__old_method1,self.method1=self.method1,self.__method1
super(MixInClass1, self).__init__()
def __method1(self):
print "pre1()"
self.__old_method1()
print "post1()"
class MixInClass2(object):
def __init__(self):
super(MixInClass2, self).__init__()
def method1_hook(self):
print('MixIn method1')
class Foo(MixInClass2,MixInClass1):
def method1(self):
print "do_something()"
getattr(self,'method1_hook',lambda *args,**kw: None)()
print "do_more()"
foo=Foo()
foo.method1()

Is there a benefit to defining a class inside another class in Python?

What I'm talking about here are nested classes. Essentially, I have two classes that I'm modeling. A DownloadManager class and a DownloadThread class. The obvious OOP concept here is composition. However, composition doesn't necessarily mean nesting, right?
I have code that looks something like this:
class DownloadThread:
def foo(self):
pass
class DownloadManager():
def __init__(self):
dwld_threads = []
def create_new_thread():
dwld_threads.append(DownloadThread())
But now I'm wondering if there's a situation where nesting would be better. Something like:
class DownloadManager():
class DownloadThread:
def foo(self):
pass
def __init__(self):
dwld_threads = []
def create_new_thread():
dwld_threads.append(DownloadManager.DownloadThread())
You might want to do this when the "inner" class is a one-off, which will never be used outside the definition of the outer class. For example to use a metaclass, it's sometimes handy to do
class Foo(object):
class __metaclass__(type):
....
instead of defining a metaclass separately, if you're only using it once.
The only other time I've used nested classes like that, I used the outer class only as a namespace to group a bunch of closely related classes together:
class Group(object):
class cls1(object):
...
class cls2(object):
...
Then from another module, you can import Group and refer to these as Group.cls1, Group.cls2 etc. However one might argue that you can accomplish exactly the same (perhaps in a less confusing way) by using a module.
I don't know Python, but your question seems very general. Ignore me if it's specific to Python.
Class nesting is all about scope. If you think that one class will only make sense in the context of another one, then the former is probably a good candidate to become a nested class.
It is a common pattern make helper classes as private, nested classes.
There is another usage for nested class, when one wants to construct inherited classes whose enhanced functionalities are encapsulated in a specific nested class.
See this example:
class foo:
class bar:
... # functionalities of a specific sub-feature of foo
def __init__(self):
self.a = self.bar()
...
... # other features of foo
class foo2(foo):
class bar(foo.bar):
... # enhanced functionalities for this specific feature
def __init__(self):
foo.__init__(self)
Note that in the constructor of foo, the line self.a = self.bar() will construct a foo.bar when the object being constructed is actually a foo object, and a foo2.bar object when the object being constructed is actually a foo2 object.
If the class bar was defined outside of class foo instead, as well as its inherited version (which would be called bar2 for example), then defining the new class foo2 would be much more painful, because the constuctor of foo2 would need to have its first line replaced by self.a = bar2(), which implies re-writing the whole constructor.
You could be using a class as class generator. Like (in some off the cuff code :)
class gen(object):
class base_1(object): pass
...
class base_n(object): pass
def __init__(self, ...):
...
def mk_cls(self, ..., type):
'''makes a class based on the type passed in, the current state of
the class, and the other inputs to the method'''
I feel like when you need this functionality it will be very clear to you. If you don't need to be doing something similar than it probably isn't a good use case.
There is really no benefit to doing this, except if you are dealing with metaclasses.
the class: suite really isn't what you think it is. It is a weird scope, and it does strange things. It really doesn't even make a class! It is just a way of collecting some variables - the name of the class, the bases, a little dictionary of attributes, and a metaclass.
The name, the dictionary and the bases are all passed to the function that is the metaclass, and then it is assigned to the variable 'name' in the scope where the class: suite was.
What you can gain by messing with metaclasses, and indeed by nesting classes within your stock standard classes, is harder to read code, harder to understand code, and odd errors that are terribly difficult to understand without being intimately familiar with why the 'class' scope is entirely different to any other python scope.
A good use case for this feature is Error/Exception handling, e.g.:
class DownloadManager(object):
class DowndloadException(Exception):
pass
def download(self):
...
Now the one who is reading the code knows all the possible exceptions related to this class.
Either way, defined inside or outside of a class, would work. Here is an employee pay schedule program where the helper class EmpInit is embedded inside the class Employee:
class Employee:
def level(self, j):
return j * 5E3
def __init__(self, name, deg, yrs):
self.name = name
self.deg = deg
self.yrs = yrs
self.empInit = Employee.EmpInit(self.deg, self.level)
self.base = Employee.EmpInit(self.deg, self.level).pay
def pay(self):
if self.deg in self.base:
return self.base[self.deg]() + self.level(self.yrs)
print(f"Degree {self.deg} is not in the database {self.base.keys()}")
return 0
class EmpInit:
def __init__(self, deg, level):
self.level = level
self.j = deg
self.pay = {1: self.t1, 2: self.t2, 3: self.t3}
def t1(self): return self.level(1*self.j)
def t2(self): return self.level(2*self.j)
def t3(self): return self.level(3*self.j)
if __name__ == '__main__':
for loop in range(10):
lst = [item for item in input(f"Enter name, degree and years : ").split(' ')]
e1 = Employee(lst[0], int(lst[1]), int(lst[2]))
print(f'Employee {e1.name} with degree {e1.deg} and years {e1.yrs} is making {e1.pay()} dollars')
print("EmpInit deg {0}\nlevel {1}\npay[deg]: {2}".format(e1.empInit.j, e1.empInit.level, e1.base[e1.empInit.j]))
To define it outside, just un-indent EmpInit and change Employee.EmpInit() to simply EmpInit() as a regular "has-a" composition. However, since Employee is the controller of EmpInit and users don't instantiate or interface with it directly, it makes sense to define it inside as it is not a standalone class. Also note that the instance method level() is designed to be called in both classes here. Hence it can also be conveniently defined as a static method in Employee so that we don't need to pass it into EmpInit, instead just invoke it with Employee.level().

Categories

Resources