Python: Class Inheritance and Incoming Arguments - python

ClassA to inherits from Base class which inherits from built-in dict class.
'name' and 'id' are Base class attributes. 'win' and 'mac' are attributes ClassA attributes.
How should I put a logic in this code so classA instance could be declared as easy as:
myInstance=ClassA(myDictArg)
===============================
class Base(dict):
"""Base is the base class from which all other classes derrive.
Base class inherits from build-in dict type.
"""
id = 'id'
name = 'name'
def __init__(self, arg=None):
"""Initialise Base Class"""
dict.__init__(self)
self[Base.id] = -1
self[Base.name] = None
if 'id' in arg.keys() and arg['id']: self['id']=arg['id']
if 'name' in arg.keys() and arg['name']: self['name']=arg['name']
class ClassA(Base):
"""ClassA is a class inherited from a Base class."""
def __init__(self, arg=None):
if arg==None: raise Exception('arg==None')
Base.__init__(self)
self.arg = arg
# set a generic to ClassA Attrs
self['win']=None
self['mac']=None
myDictArg= {'id':1, 'name':'MyName', 'win':'c:/windows', 'mac': '/Volumes/mac/'}
myInstance=ClassA(myDictArg)
print myInstance

This class structure has the advantage that it keeps the signature of dict which is pretty flexible and sets default values only if they aren't provided (which I think was the original goal). It also (due to judicious use of super) is well set up to support cooperative multiple inheritance (Horray!).
class Base(dict):
def __init__(self, *args, **kwargs):
super(Base, self).__init__(*args, **kwargs)
self.setdefault('id', -1)
self.setdefault('name', None)
class ClassA(Base):
"""ClassA is a class inherited from a Base class."""
def __init__(self, *args, **kwargs):
if not (args or kwargs):
raise Exception('you need to give me *something*!')
super(ClassA, self).__init__(*args, **kwargs)
self.setdefault('win', None)
self.setdefault('mac', None)

What you've written looks like it should work.. not that I've ran it myself. So I am making an assumption that you are looking for the bug in this situation...
one possibility for a problem is the fact that you are replacing the arg variable after 'id' and 'name' have been set, effectively erasing them.. I think a better idea would be to merge the args. Although the following code may not be the most pythonic.. It might look something like this.
for key in arg.keys()
self.arg[key] = arg[key]
another problem is that you aren't even passing in your args object into the base class's constructor.
I suggest you change that to
Base.__init__(self, args)
Otherwise, arg in the Base class will revert to the default; None.

Related

Proper way of defining init() methods with different number of parameters (to be used in multiple inheritance)

This is my current setup:
class Base():
def __init__(self):
pass
# ...other methods
class A(Base):
def __init__(self, dst, fname, alg, src=None):
super().__init__()
# I then use these instance variables throughout instance methods.
self.src = [] if src is None else src
self.dst = dst
self.fname = fname
self.alg = alg
# ...other methods
class B(Base):
def __init__(self, fname):
super().__init__()
# I then use these instance variables throughout instance methods.
self.fname = fname
# ...other methods
class C(A, B):
"""Here is my problem.
When I try to inherit A and B this way,
I keep getting those "missing required positional args..." errors
"""
def __init__(self, dst, src=None):
super().__init__()
# I then use these instance variables throughout instance methods.
self.fname = fname
# ...other methods
Here is what I am trying to do right now:
class Base():
def __init__(self, *args, **kwargs):
pass
class A(Base):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class B(Base):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class C(A, B):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
My main question:
What is the "best" (standard, most preferred, efficient, readable etc.) way to handle such situations?
P.S.
I read the following article, Python’s super() considered super!, but I could not derive the best answer for myself after reading it.
Also, I referenced this SO question but the accepted answer does not have different number of params like I do...
By the way, I am willing to hear that my design (class hierarchy) is bad overall. Technically, I could relocate methods that I am trying to inherit from A and B in C to the Base class...I hope (did not try yet)...
Based on your implementation, to remove the error, I think it would be best to explicitly use the base class' required parameters based on the method resolution order (in your case, the base class for the class C should be class A).
class C(A, B):
def __init__(self, dst, src=None):
fname = 'some value'
super().__init__(dst, fname, alg)
This will be the simplest solution that can be done. But if you are willing to make design changes, I would recommend using composition instead of inheritance for python. I assume that you have methods in class A and class B that is needed by class C. With composition, needed functions/operations can just be added to the classes that needs it. It would also prevent code smells like refused bequest
Python has an advantage that supports multiple inheritance, even the box office technology java doesn't supports this feature because it leads to overriding of methods when both extended class have a same method name.
But python overcomes this problem and supporting multiple inheritance. To use multiple inheritance in python, should know the arguments to define in proper way.
class C(A, B):
def __init__(self, dst, src=None):
super().__init__()
self.fname = fname
In the above obove code, Class C inherits Classes A and B. Here first of all super().__init__() navigate to Class A, search for .__init__(), if init() or some other method abc() not in Class A, then next it will look up into Class B,
These navigation go through based on sequential order that you inherits into Class C
Here Solution for you problem is, you defined super class __init__() with parameters in both Class A & B, and calling super class super().__init__() with out parameters in Class C.

Inheritance and initialization of attributes

class Email():
def __init__(self, store_number):
self.store_number = store_number
def amethod(self):
pass
What is the correct way to pass variables from a sub-class to a parent-class?
should I do:
class MoreSpecificEmail():
def __init__(self, store_number):
Email.__init__(self, store_number=store_number)
def another_method(self):
pass
or:
class MoreSpecificEmail():
def __init__(self, store_number):
self.store_number = store_number
Email.__init__(self, store_number=self.store_number)
I have just been using different abbreviations of store_number in each sub-class to help clarify what's going on in my head. I am sure that is the wrong way, though.
What you currently have isn't inheritance; neither of your classes actually inherits from anything! Firstly, Email should be a "new-style class", inheriting from object:
class Email(object):
# ^ note inheritance from object
def __init__(self, store_number):
self.store_number = store_number
def amethod(self):
pass
Then MoreSpecificEmail should inherit from Email - as it doesn't have any additional instantiation parameters, it can just use the inherited __init__ and doesn't need to define its own:
class MoreSpecificEmail(Email):
# ^ note inheritance from Email
# note no need to define __init__
def another_method(self):
pass
For an example where there are additional __init__ parameters, note that you should use super and rely on the superclass's __init__ to assign the parameters it takes - you only need to assign the attributes that don't get handled by the superclass:
class MoreSpecificEmail(Email):
def __init__(self, store_number, something_else):
super(MoreSpecificEmail, self).__init__(store_number)
# ^ pass it straight on
self.something_else = something_else
def another_method(self):
pass
For more information, see the Python class tutorial.

how to make child class call parent class __init__ automatically?

i had a class called CacheObject,and many class extend from it.
now i need to add something common on all classes from this class so i write this
class CacheObject(object):
def __init__(self):
self.updatedict = dict()
but the child class didn't obtain the updatedict attribute.i know calling super init function was optional in python,but is there an easy way to force all of them to add the init rather than walk all the classes and modify them one by one?
I was in a situation where I wanted classes to always call their base classes' constructor in order before they call their own. The following is Python3 code that should do what you want:
class meta(type):
def __init__(cls,name,bases,dct):
def auto__call__init__(self, *a, **kw):
for base in cls.__bases__:
base.__init__(self, *a, **kw)
cls.__init__child_(self, *a, **kw)
cls.__init__child_ = cls.__init__
cls.__init__ = auto__call__init__
class A(metaclass=meta):
def __init__(self):
print("Parent")
class B(A):
def __init__(self):
print("Child")
To illustrate, it will behave as follows:
>>> B()
Parent
Child
<__main__.B object at 0x000001F8EF251F28>
>>> A()
Parent
<__main__.A object at 0x000001F8EF2BB2B0>
I suggest a non-code fix:
Document that super().__init__() should be called by your subclasses before they use any other methods defined in it.
This is not an uncommon restriction. See, for instance, the documentation for threading.Thread in the standard library, which says:
If the subclass overrides the constructor, it must make sure to invoke the base class constructor (Thread.__init__()) before doing anything else to the thread.
There are probably many other examples, I just happened to have that doc page open.
You can override __new__. As long as your base classes doesn't override __new__ without calling super().__new__, then you'll be fine.
class CacheObject(object):
def __new__(cls, *args, **kwargs):
instance = super().__new__(cls, *args, **kwargs)
instance.updatedict = {}
return instance
class Foo(CacheObject):
def __init__(self):
pass
However, as some commenters said, the motivation for this seems a little shady. You should perhaps just add the super calls instead.
This isn't what you asked for, but how about making updatedict a property, so that it doesn't need to be set in __init__:
class CacheObject(object):
#property
def updatedict(self):
try:
return self._updatedict
except AttributeError:
self._updatedict = dict()
return self._updatedict
Hopefully this achieves the real goal, that you don't want to have to touch every subclass (other than to make sure none uses an attribute called updatedict for something else, of course).
There are some odd gotchas, though, because it is different from setting updatedict in __init__ as in your question. For example, the content of CacheObject().__dict__ is different. It has no key updatedict because I've put that key in the class, not in each instance.
Regardless of motivation, another option is to use __init_subclass__() (Python 3.6+) to get this kind of behavior. (For example, I'm using it because I want users not familiar with the intricacies of Python to be able to inherit from a class to create specific engineering models, and I'm trying to keep the structure of the class they have to define very basic.)
In the case of your example,
class CacheObject:
def __init__(self) -> None:
self.updatedict = dict()
def __init_subclass__(cls) -> None:
orig_init = cls.__init__
#wraps(orig_init)
def __init__(self, *args, **kwargs):
orig_init(self, *args, **kwargs)
super(self.__class__, self).__init__()
cls.__init__ = __init__
What this does is any class that subclasses CacheObject will now, when created, have its __init__ function wrapped by the parent class—we're replacing it with a new function that calls the original, and then calls super() (the parent's) __init__ function. So now, even if the child class overrides the parent __init__, at the instance's creation time, its __init__ is then wrapped by a function that calls it and then calls its parent.
You can add a decorator to your classes :
def my_decorator(cls):
old_init = cls.__init__
def new_init(self):
self.updatedict = dict()
old_init(self)
cls.__init__ = new_init
return cls
#my_decorator
class SubClass(CacheObject):
pass
if you want to add the decorators to all the subclasses automatically, use a metaclass:
class myMeta(type):
def __new__(cls, name, parents, dct):
return my_decorator(super().__new__(cls, name, parents, dct))
class CacheObject(object, metaclass=myMeta):
pass

Accessing a parent variable from a nested child class in Python

I'm wondering what the best way of accessing a parent variable from a nested subclass is, currently I'm using a decorator.
Is that the only/best way???
I don't want to have to directly access the parent variable (eg. ComponentModel.origin (see below)) as that would require more code in the "config" file, so I'm wondering whether I could assign parent variable in a class which the subclass in question inherits from?
Trivial example of my current solution:
# defined in a big library somewhere:
class LibrarySerialiser(object):
pass
# defined in my module:
class ModelBase:
pass
class SerialiserBase(LibrarySerialiser):
def __init__(self, *args, **kwargs):
# could i some how get hold of origin here without the decorator?
print self.origin
super(SerialiserBase, self).__init__(*args, **kwargs)
def setsubclasses(cls):
cls.Serialiser.origin = cls.origin
return cls
# written by "the user" for the particular application as the
# configuration of the module above:
#setsubclasses
class ComponentModel(ModelBase):
origin = 'supermarket'
class Serialiser(SerialiserBase):
pass
ser = ComponentModel.Serialiser()
This is obviously a trival example that misses all the real logic hence lots of the classes appear void but are really necessary.
FYI, the accepted terminology used when nesting classes as you've done is inner/outer, not parent/child or super/subclass. The parent/child or super/sub relationship refers to inheritance. This makes your decorator's name, setsubclasses, confusing, since there are no subclasses involved!
The unusual thing you're doing here is using the class as a namespace without instantiating it. Normally you would instantiate your ComponentModel and at that time, it is trivial to give your Serialiser inner class a copy of an attribute from its outer class. E.g.:
class ModelBase(object):
def __init__(self):
self.Serialiser.origin = self.origin
# ... then
cm = ComponentModel()
ser = cm.Serialiser()
Better yet, have the outer class instantiate the inner class and pass it a reference to the outer class; then it can grab any attributes it wants itself, whenever it needs them:
class ModelBase(object):
def __init__(self, *args, **kwargs):
serialiser = self.Serialiser(self, *args, **kwargs)
class SerialiserBase(LibrarySerialiser):
def __init__(self, outer, *args, **kwargs):
self.outer = outer
print self.outer.origin
super(SerialiserBase, self).__init__(*args, **kwargs)
# ...
cm = ComponentModel()
ser = cm.serialiser
However, if you insist on being able to get this attribute without instantiating the outer class, you can use a metaclass to set the attribute:
class PropagateOuter(type):
def __init__(cls, name, bases, dct):
type.__init__(cls, name, bases, dct)
if "Serialiser" in dct:
cls.Serialiser.outer = cls
class ModelBase(object):
__metaclass__ = PropagateOuter
# Python 3 version of the above
# class ModelBase(metaclass=PropagateOuter):
# pass
class SerialiserBase(LibrarySerialiser):
def __init__(self, *args, **kwargs):
print self.outer.origin
super(SerialiserBase, self).__init__(*args, **kwargs)
class ComponentModel(ModelBase):
origin = 'supermarket'
class Serialiser(SerialiserBase):
pass
ser = ComponentModel.Serialiser()
This isn't doing anything your decorator isn't, but the user gets it automatically through inheritance rather than having to specify it manually. The Zen of Python says "explicit is better than implicit" so tomato, tomato.
You could even write the metaclass so that it introspects the outer class and puts a reference to that class into every inner class regardless of their name.
By the way, one of the pitfalls of the way you're doing this is that all your model classes must subclass SerialiserBase. If a user of your class just wants the default serialiser, they can't just write Serialiser = SerialiserBase in their class definition, they must write class Serialiser(SerialiserBase): pass. This is because there's only one SerialiserBase and it obviously can't contain a reference to multiple outer classes. Of course, you could write your metaclass to deal with this (e.g. by automatically making a subclass of the specified serialiser if it already has an outer attribute).

initialize base class with variable not coming from derived class

I'm trying to provide framework which allows people to write their own plugins. These plugins are basically derived classes. My base class needs some variables to initialize, how can I initialize my base class without having to let my derived class feed the variable in the base class initialization?
#!/bin/python
class BaseClass():
def __init__(self,config):
self.config=config
def showConfig(self):
print "I am using %s" % self.config
class UserPlugin(BaseClass):
def __init__(self,config):
BaseClass.__init__(self,config)
def doSomething(self):
print "Something"
fubar = UserPlugin('/tmp/config.cfg')
fubar.showConfig()
My goal is to avoid the need to define the config parameter in the UserPlugin class, since this is something I don't want the user who writes a plugin to be bothered with.
You can use argument lists to pass any remaining arguments to the base class:
class UserPlugin(BaseClass):
def __init__(self, *args, **kwargs):
BaseClass.__init__(self, *args, **kwargs)
Based on your Pastebin code, how about this? This avoids using a separate global, instead using a class attribute, which is accessible as a member to all derived classes and their instances.
#!/bin/python
class BaseClass():
config = '/tmp/config.cfg'
def __init__(self):
pass
def showConfig(self):
print "I am using %s" % self.config
class UserPlugin(BaseClass):
def __init__(self):
BaseClass.__init__(self)
def doSomething(self):
print "Something"
fubar = UserPlugin()
fubar.showConfig()
This was the other way to do it that I mentioned before. Keep in mind that if you want to change the value of BaseClass.config itself, you should access it directly (i.e. BaseClass.config = '/foo/path'; otherwise, you wind up creating a custom UPinstance.config value, leaving BaseClass.config unchanged.

Categories