How to change method defaults in Python? - python

I'm building a class, Child, that inherits from another class, Parent. The Parent class has a loadPage method that the Child will use, except that the Child will need to run its own code near the end of the loadPage function but before the final statements of the function. I need to somehow insert this function into loadPage only for instances of Child, and not Parent. I was thinking of putting a customFunc parameter into loadPage and have it default to None for Parent, but have it default to someFunction for Child.
How do I change the defaults for the loadPage method only for instances of Child? Or am I going about this wrong? I feel like I may be overlooking a better solution.
class Parent():
def __init__(self):
# statement...
# statement...
def loadPage(self, pageTitle, customFunc=None):
# statement...
# statement...
# statement...
if customFunc:
customFunc()
# statement...
# statement...
class Child(Parent):
def __init__(self):
Parent.__init__(self)
self.loadPage.func_defaults = (self.someFunction) #<-- This doesn't work

For such things, I do it in a different way :
class Parent():
def loadPage(self, pageTitle):
# do stuff
self.customFunc()
# do other stuff
def customFunc(self):
pass
class Child(Parent):
def customFunc(self):
# do the child stuff
then, a Child instance would do the stuff in customFunc while the Parent instance would do the "standard" stuff.

Modifying your method as little as possible:
class Parent(object):
def __init__(self):
pass
def loadPage(self, pageTitle, customFunc=None):
print 'pageTitle', pageTitle
if customFunc:
customFunc()
class Child(Parent):
def __init__(self):
Parent.__init__(self)
def loadPage(self, pagetitle, customFunc = None):
customFunc = self.someFunction if customFunc is None else customFunc
super(Child, self).loadPage(pagetitle, customFunc)
def someFunction(self):
print 'someFunction'
p = Parent()
p.loadPage('parent')
c = Child()
c.loadPage('child')

I wouldn't try to do this with defaults. Straightforward class inheritance already provides what you need.
class Parent():
def __init__(self):
# statement...
# statement...
def loadPage(self, pageTitle):
# ... #
self.custom_method()
# ... #
def custom_method(self):
pass # or something suitably abstract
class Child(Parent):
def __init__(self):
Parent.__init__(self)
def custom_method(self):
# what the child should do do

Can the statements before the customFunc() call be exported to a function? and the same for the statements after this call.
If yes, then the parent class will just call these two functions, and the child class will have the customFunc() call between them.
So only the calls will be duplicated.
I may be overlooking a better solution.

Well, the best is probably to rely on an internal attribute, so you would have something like this:
class Parent(object):
def __init__(self):
self._custom_func = None
def load_page(self, page_title):
if self._custom_func:
self._custom_func()
class Child(Parent):
def __init__(self):
super(Parent, self).__init__()
self._load_page = some_function

Related

Can an __init__ automatically execute code after calling super?

I have something like this:
class SuperClass(object):
def __init__(self):
# initialization stuff
def always_do_this_last(self):
# cleanup stuff
class SubClass(SuperClass):
def __init__(self):
super().__init__()
# intermediate stuff
self.always_do_this_last()
Is it possible to automatically call that last line? Every subclass of SuperClass needs perform the cleanup.
Instead of overriding __init__, define a method that SuperClass.__init__ will call.
class SuperClass(object):
def __init__(self):
# do some stuff
self.child_init()
self.cleanup()
def cleanup():
...
def child_init(self):
pass
class SubClass(SuperClass):
def child_init(self):
...
You can define SuperClass.__init_subclass__ to ensure child_init is overriden, or use the abc module to make SuperClass.child_init an abstract method
One option could be to use a method that the subclasses could override without overriding __init__(). Maybe like this:
class SuperClass:
def __init__(self):
# initialization stuff
self.setup_subclass()
self.always_do_this_last()
def setup_subclass(self):
pass
def always_do_this_last(self):
# cleanup stuff
class SubClass(SuperClass):
def setup_subclass(self):
# intermediate stuff
Would that work for you?
You have 2 options:
Use a different method as your initializer and call always_do_this_last afterwards
class SuperClass(object):
def __init__(self):
self._init() # initialize
self.always_do_this_last() # clean up
def _init(self):
pass # initialization stuff
def always_do_this_last(self):
pass # cleanup stuff
class SubClass(SuperClass):
def _init(self):
super()._init()
# intermediate stuff
Use a metaclass
class CleanupMeta(type):
def __call__(cls, *args, **kwargs):
obj = super().__call__(*args, **kwargs)
obj.always_do_this_last()
return obj
class SuperClass(metaclass=CleanupMeta):
def __init__(self):
pass # initialization stuff
def always_do_this_last(self):
pass # cleanup stuff
class SubClass(SuperClass):
def __init__(self):
super().__init__()
# intermediate stuff
The other answers here are more than sufficient. I will add that you might want to have a look at the abstract base class if you are implementing a class that requires certain member functions to be implemented.
In the example below the parent requires the initialize and cleanup methods to be defined in each child (try removing one of them to verify an error is raised).
import abc
class SuperClass(object):
__metaclass__ = abc.ABCMeta
def __init__(self):
print("Instantiating Class")
self.initialize()
self.cleanup()
#abc.abstractmethod
def initialize(self):
pass
#abc.abstractmethod
def cleanup(self):
pass
class SubClass(SuperClass):
def __init__(self):
super(SubClass, self).__init__()
def initialize(self):
print("initializing...")
def cleanup(self):
print("... cleanup.")
a = SubClass()

Nested classes that inherit from the outer parent's nested classes in Python 3

Suppose I have the following class in Python 3:
class CoolCar:
#classmethod
def myWheels(cls):
cls.Wheels().out()
class Wheels:
def __init__(self):
self.s = "I'm round!"
def out(self):
print(self.s)
All well and good. Now I want a derived class:
class TerribleTank(CoolCar):
class Wheels(CoolCar.Wheels):
def __init__(self):
self.s = "I'm square!!"
This works as I would expect:
CoolCar.myWheels()
TerribleTank.myWheels()
But what's bothering me is that I have to write CoolCar twice in the definition of TerribleTank. So I tried this:
class TerribleTank(CoolCar):
class Wheels(super().Wheels):
def __init__(self):
self.s = "I'm square!!"
Which does not work. Now, I know it doesn't work because super() is looking for a first-argument self/cls to begin its search.
So finally my question: Is there something like this that works, so that I don't need to explicitly write that second CoolCar?
What about:
class CoolCar:
#classmethod
def myWheels(cls):
cls.Wheels().out()
class Wheels:
def __init__(self):
self.s = "I'm round!"
def out(self):
print(self.s)
class TerribleTank(CoolCar):
class Wheels(TerribleTank.Wheels):
def __init__(self):
self.s = "I'm square!!"
>>> TerribleTank.myWheels()
I'm square!!
basically when you inherit CoolCar in TerribleTank, you set up TerribleTank.Wheels as a reference to CoolCar.Wheels, until you shadow it with your own new definition of it within the TerribleTank definition. So I believe that matches your expectations of not having CoolCar twice in TerribleBank definition ☺
HTH

Pass a parent class as an argument?

Is it possible to leave a parent class unspecified until an instance is created?
e.g. something like this:
class SomeParentClass:
# something
class Child(unspecifiedParentClass):
# something
instance = Child(SomeParentClass)
This obviously does not work. But is it possible to do this somehow?
You can change the class of an instance in the class' __init__() method:
class Child(object):
def __init__(self, baseclass):
self.__class__ = type(self.__class__.__name__,
(baseclass, object),
dict(self.__class__.__dict__))
super(self.__class__, self).__init__()
print 'initializing Child instance'
# continue with Child class' initialization...
class SomeParentClass(object):
def __init__(self):
print 'initializing SomeParentClass instance'
def hello(self):
print 'in SomeParentClass.hello()'
c = Child(SomeParentClass)
c.hello()
Output:
initializing SomeParentClass instance
initializing Child instance
in SomeParentClass.hello()
Have you tried something like this?
class SomeParentClass(object):
# ...
pass
def Child(parent):
class Child(parent):
# ...
pass
return Child()
instance = Child(SomeParentClass)
In Python 2.x, also be sure to include object as the parent class's superclass, to use new-style classes.
You can dynamically change base classes at runtime. Such as:
class SomeParentClass:
# something
class Child():
# something
def change_base_clase(base_class):
return type('Child', (base_class, object), dict(Child.__dict__))()
instance = change_base_clase(SomeParentClass)
For example:
class Base_1:
def hello(self):
print('hello_1')
class Base_2:
def hello(self):
print('hello_2')
class Child:pass
def add_base(base):
return type('Child', (base, object), dict(Child.__dict__))()
# if you want change the Child class, just:
def change_base(base):
global Child
Child = type('Child', (base, object), dict(Child.__dict__))
def main():
c1 = add_base(Base_1)
c2 = add_base(Base_2)
c1.hello()
c2.hello()
main()
Result:
hello_1
hello_2
Works well in both python 2 and 3.
For more information, see the related question How to dynamically change base class of instances at runtime?

Calling a parent's parent's method, which has been overridden by the parent

How do you call a method more than one class up the inheritance chain if it's been overridden by another class along the way?
class Grandfather(object):
def __init__(self):
pass
def do_thing(self):
# stuff
class Father(Grandfather):
def __init__(self):
super(Father, self).__init__()
def do_thing(self):
# stuff different than Grandfather stuff
class Son(Father):
def __init__(self):
super(Son, self).__init__()
def do_thing(self):
# how to be like Grandfather?
If you always want Grandfather#do_thing, regardless of whether Grandfather is Father's immediate superclass then you can explicitly invoke Grandfather#do_thing on the Son self object:
class Son(Father):
# ... snip ...
def do_thing(self):
Grandfather.do_thing(self)
On the other hand, if you want to invoke the do_thing method of Father's superclass, regardless of whether it is Grandfather you should use super (as in Thierry's answer):
class Son(Father):
# ... snip ...
def do_thing(self):
super(Father, self).do_thing()
You can do this using:
class Son(Father):
def __init__(self):
super(Son, self).__init__()
def do_thing(self):
super(Father, self).do_thing()

python: cleanest way to wrap each method in parent class in a "with"

I have a parent class that has a bunch of class methods:
class Parent():
#classmethod
def methodA(cls):
pass
#classmethod
def methodB(cls):
pass
In my subclass, I would like to wrap a subset of the methods inside a "with". It should achieve this effect:
class Child(Parent):
#classmethod
def methodA(cls):
with db.transaction:
super(Child, cls).methodA()
I have a bunch of methods that follow this pattern and would prefer not to repeat myself. Is there a cleaner way to do this?
It seems you should move the with db.transaction into the base.
Make a method in the base, returning the db.transaction
#staticmethod
def gettransaction(): return db.transaction
then you overload it in the children as/if needed.
In the base you then do
def methodB(cls):
with cls.gettransaction():
bla ...
Here's a complete working example with a dummy transaction
class transact:
def __enter__(a):
print "enter"
def __exit__(a,b,c,d):
print "exit"
class transact2:
def __enter__(a):
print "enter2"
def __exit__(a,b,c,d):
print "exit2"
class Parent():
#staticmethod
def gettrans():
return transact()
def methodA(cl):
with cl.gettrans():
print "A"
class Child(Parent):
pass
#staticmethod
def gettrans():
return transact2()
p=Parent()
p.methodA()
c=Child()
c.methodA()
this results in
enter
A
exit
enter2
A
exit2

Categories