__init__ : Inheriting __init__ parameter (dict) to another class's __init__ method? - python

I am confused even after checking many questions asked in SO. I have 2 different class (2 different script) & I want to inherit super class's __init__ method's parameters.
script1.py
class MainClass():
def __init__(self,params):
self.one=params['ONE']
self.two=params['TWO']
self.three=params['THREE']
self.four=params['FOUR']
self.five=params['FIVE']
def a():
#---------
#somecode
#Initializing other class's object to access it's method.
s=SubClass() #HERE I WANT TO PASS 'PARAMS' (WHICH IS A DICTIONARY)
s.method1(....)
script2.py
class SubClass(SuperClass):
def __init__(self,params):
#Here I want all the parameters inside the 'param' in super class.
#(one,two,three...., etc).
#By checking some SO questions, I changed class SubClass() -->
#class Subclass(SuperClass) & below line:
MainClass.__init__(self,params) #But technically I don't have anything
#in param in subclass.
def method1():
#some code...
Since sub class's param doesn't have anything, It gives me an error:
self.one=params['ONE']
TypeError: 'int' object has no attribute '__getitem__'
I am not getting:
How can I access all the parameters of super class to sub class in a simplest way? I don't want to pass individual arguments (like self.one, self.two..) to the sub class.
If I am calling third class inside SubClass -> method1 --> Call 3rd class same as passing 'params'. Is it possible?

Is this what you need?
script1.py
class MainClass():
def __init__(self,params):
# Save params for use by a
self.params = params
self.one=params['ONE']
self.two=params['TWO']
...
self.five=params['FIVE']
def a():
s=SubClass(self.params)
s.method1(...)
script2.py
class SubClass(SuperClass):
def __init__(self,params):
MainClass.__init__(self,params)
def method1():
#some code...

You can pass any and all the non-keyword arguments from the subclass's __init__()to the superclass's like this:
class SubClass(SuperClass):
def __init__(self, *params):
MainClass.__init__(self, *params)
...
This same idea will work for other methods, too.

Related

Python: Call derived class method from another class

I have searched all the related this stackoverflow question but its not satisfied my issue.
BaseHandler.py
class BaseHandler(object):
def __init__(self, rHandler, path, param):
self._rHandler = rHandler
self._server = self._rHandler.server
self._path = path
self._param = param
def _getElement(self, name):
return name + "append"
MyClass.py
class MyClass(BaseHandler.BaseHandler):
def getA(self):
print "Some info"
def getB(self):
el = self._getElement("T") #baseclass method
print ebl
I wanted to call getB from the below class.
RThread.py
import MyClass
class RThread(object):
def someMethod(self):
clr = MyClass.MyClass
clr.getB()
I am getting the following error:
TypeError: unbound method getB() must be called with MyClass instance as first argument (got nothing instead)
When I try the following:
clr = MyClass.MyClass()
I am getting the following error:
init() takes exactly 4 arguments (1 given)
So kindly help me how to call this method from different class.
You need to instantiate the class in order to call a method on it.
def someMethod(self):
clr = MyClass.MyClass(*args)
clr.getB()
In the case you want the method to be callable from the class you need to use either #staticmethod or #classmethod
#staticmethod
def getB():
return self._getElement("T")
However, you are using the self. notation which requires an instance. So you would need to flag the _getElement method with #staticmethod as well. Static methods do not have access to the parent class. You can use the #classmethod decorator to do so.
#classmethod
def getB(cls):
return cls._getElement("T")
You're not calling the method correctly; you need to create an object. This is how you create an object, which is what you were doing, except you weren't passing in enough parameters.
clr = MyClass.MyClass()
Since MyClass inherits from BaseHandler and you did not override its constructor, you're using the constructor from BaseHandler, which has four arguments, one of which is self.
def __init__(self, rHandler, path, param):
...
So, try something like this:
clr = MyClass.MyClass(arg1, arg2, arg3)
clr.getB()

super not working with class decorators?

Lets define simple class decorator function, which creates subclass and adds 'Dec' to original class name only:
def decorate_class(klass):
new_class = type(klass.__name__ + 'Dec', (klass,), {})
return new_class
Now apply it on a simple subclass definition:
class Base(object):
def __init__(self):
print 'Base init'
#decorate_class
class MyClass(Base):
def __init__(self):
print 'MyClass init'
super(MyClass, self).__init__()
Now, if you try instantiate decorated MyClass, it will end up in an infinite loop:
c = MyClass()
# ...
# File "test.py", line 40, in __init__
# super(MyClass, self).__init__()
# RuntimeError: maximum recursion depth exceeded while calling a Python object
It seems, super can't handle this case and does not skip current class from inheritance chain.
The question, how correctly use class decorator on classes using super ?
Bonus question, how get final class from proxy-object created by super ? Ie. get object class from super(Base, self).__init__ expression, as determined parent class defining called __init__.
If you just want to change the class's .__name__ attribute, make a decorator that does that.
from __future__ import print_function
def decorate_class(klass):
klass.__name__ += 'Dec'
return klass
class Base(object):
def __init__(self):
print('Base init')
#decorate_class
class MyClass(Base):
def __init__(self):
print('MyClass init')
super(MyClass, self).__init__()
c = MyClass()
cls = c.__class__
print(cls, cls.__name__)
Python 2 output
MyClass init
Base init
<class '__main__.MyClassDec'> MyClassDec
Python 3 output
MyClass init
Base init
<class '__main__.MyClass'> MyClassDec
Note the difference in the repr of cls. (I'm not sure why you'd want to change a class's name though, it sounds like a recipe for confusion, but I guess it's ok for this simple example).
As others have said, an #decorator isn't intended to create a subclass. You can do it in Python 3 by using the arg-less form of super (i.e., super().__init__()). And you can make it work in both Python 3 and Python 2 by explicitly supplying the parent class rather than using super.
from __future__ import print_function
def decorate_class(klass):
name = klass.__name__
return type(name + 'Dec', (klass,), {})
class Base(object):
def __init__(self):
print('Base init')
#decorate_class
class MyClass(Base):
def __init__(self):
print('MyClass init')
Base.__init__(self)
c = MyClass()
cls = c.__class__
print(cls, cls.__name__)
Python 2 & 3 output
MyClass init
Base init
<class '__main__.MyClassDec'> MyClassDec
Finally, if we just call decorate_class using normal function syntax rather than as an #decorator we can use super.
from __future__ import print_function
def decorate_class(klass):
name = klass.__name__
return type(name + 'Dec', (klass,), {})
class Base(object):
def __init__(self):
print('Base init')
class MyClass(Base):
def __init__(self):
print('MyClass init')
super(MyClass, self).__init__()
MyClassDec = decorate_class(MyClass)
c = MyClassDec()
cls = c.__class__
print(cls, cls.__name__)
The output is the same as in the last version.
Since your decorator returns an entirely new class with different name, for that class MyClass object doesn't even exist. This is not the case class decorators are intended for. They are intended to add additional functionality to an existing class, not outright replacing it with some other class.
Still if you are using Python3, solution is simple -
#decorate_class
class MyClass(Base):
def __init__(self):
print 'MyClass init'
super().__init__()
Otherwise, I doubt there is any straight-forward solution, you just need to change your implementation. When you are renaming the class, you need to rewrite overwrite __init__ as well with newer name.
The problem is that your decorator creates a subclass of the original one. That means that super(Myclass) now point to... the original class itself!
I cannot even explain how the 0 arg form of super manages to do the job in Python 3, I could not find anything explicit in the reference manual. I assume it must use the class in which it is used at the time of declaration. But I cannot imagine a way to get that result in Python2.
If you want to be able to use super in the decorated class in Python 2, you should not create a derived class, but directly modify the original class in place.
For example, here is a decorator that prints a line before and after calling any method:
def decorate_class(klass):
for name, method in klass.__dict__.iteritems(): # iterate the class attributes
if isinstance(method, types.FunctionType): # identify the methods
def meth(*args, **kwargs): # define a wrapper
print "Before", name
method(*args, **kwargs)
print "After", name
setattr(klass, name, meth) # tell the class to use the wrapper
return klass
With your example it gives as expected:
>>> c = MyClass()
Before __init__
MyClass init
Base init
After __init__

How can I add to the initial definition of a python class inheriting from another class?

I'm trying to define self.data inside a class inheriting from a class
class Object():
def __init__(self):
self.data="1234"
class New_Object(Object):
# Code changing self.data here
But I ran into an issue.
class Object():
def __init__(self):
self.data="1234"
So I have the beginning class here, which is imported from elsewhere, and let's say that the class is a universal one so I can't modify the original at all.
In the original, the instance is referred to as "self" inside the class, and it is defined as self inside the definition __init__.
class New_Object(Object):
# Code changing self.data here
So if I wanted to inherit from the class Object, but define self.data inside New_Object, I thought I would have to define __init__ in New_Object, but this overrides the __init__ from New_Object
Is there any way I could do this without copypasting the __init__ from Object?
You use super to call the original implementation.
class New_Object(Object):
def __init__(self):
super(NewObject, self).__init__()
self.info = 'whatever'
That's what super is for:
class NewObject(Object):
def __init__(self):
super(NewObject, self).__init__()
# self.data exists now, and you can modify it if necessary
You can use super().__init__() to call Object.__init__() from New_Object.__init__().
What you would do:
class Object:
def __init__(self):
print("Object init")
self.data = "1234"
class New_Object(Object):
def __init__(self):
print("calling super")
super().__init__()
print("data is now", self.data)
self.data = self.data.split("3")
o = New_Object()
# calling super
# Object init
# data is now 1234
Note that you do not have to give any arguments to super(), as long as you are using Python 3.
The answer is that you call the superclass's __init__ explicitly during the subclass's __init__. This can be done either of two ways:
Object.__init__(self) # requires you to name the superclass explicitly
or
super(NewObject, self).__init__() # requires you to name the subclass explicitly
The latter also requires you to ensure that you're using "new-style" classes: in Python 3 that's always the case, but in Python 2 you must be sure to inherit from the builtin object class. In Python 3 it can actually be expressed even more simply:
super().__init__()
Personally, in most of my code the "disadvantage" of having to name the superclass explicitly is no disadvantage at all, and Object.__init__() lends transparency since it makes it absolutely clear what is being called. This is because most of my code is single-inheritance only. The super route comes into its own when you have multiple inheritance. See What does 'super' do in Python?
Python 2 example:
class Object(object):
def __init__(self):
self.data = "1234"
class NewObject:
def __init__(self):
# subclass-specific stuff
super(NewObject, self).__init__()
# more subclass-specific stuff

How to override base constructor arguments

If I make a python class which overrides another class, does __init__ get called from base class? What if I want to specify arguments for it?
Base class:
class bar(object):
def __init__(self, somearg = False):
# ...
New class
class foo(bar):
def __init__(self)
# ???
What do I want?
obj = foo() # somearg = True
No, the base class __init__ method is not called, since your derived class provides a new version of the method.
You'd call the parent __init__ method explicitly through the super() proxy and pass in the argument to set a different default:
class foo(bar):
def __init__(self)
super().__init__(somearg=True)

How do I call a Child class method from within a Parent class Method?

I know this question might be pointless but there is a reason why I am looking to do it this way. I want to call something exactly opposite to super()
class A(object):
def use_attack(self, damage, passive, spells):
#do stuff with passed parameters
#return something
def use_spell(self, name , enemy_hp):
#other code
if name == 'Enrage':
#call child method use_attack right here
class B(A):
def use_attack(self):
#bunch of code here
return super(B, self).use_attack(damage, passive, spells)
def use_spell(self, name , enemy_hp):
return super(B , self).use_attack(name ,enemy_hp)
b = B()
b.use_spell('Enrage', 100)
I have a bunch of code in class B's use_attack() method that I would not like to replicate in the parent method of use_spell() .
I would like to call the child method use_attack() in the line indicated.
I have a bunch of code in class B's use_attack() method that I would not like to replicate in the parent method of use_spell() .
Then factor that code out into a method on the parent class. This is exactly what inheritance is for. Children inherit code from parents, not the other way around.
From the python docs: "The mro attribute of the type lists the method resolution search order used by both getattr() and super()"
https://docs.python.org/3/library/functions.html#super
This should help shed some light on Inheritance and Method Resolution Order (mro).
class Foo(object):
def __init__(self):
print('Foo init called')
def call_child_method(self):
self.child_method()
class Bar(Foo):
def __init__(self):
print('Bar init called')
super().__init__()
def child_method(self):
print('Child method called')
bar = Bar()
bar.call_child_method()

Categories

Resources