Python parent class accessing class variable of child class - python

I'm currently trying to implement some inheritance in my Python project and have hit a road block. I'm trying to create a baseParentClass that will handle the basic functionality for a lot of child classes. In this specific example I am trying to initialize an instance with a number of attributes (set to 0), stored as a class variable list (called ATTRS) in the Child. I am unsure of how to use this ATTRS in the parent class.
class Parent(object):
def __init__():
for attr in ATTRS:
setattr(self, attr, 0)
class Child(Parent):
#class variable
ATTRS = [attr1, attr2, attr3]
def __init__():
super(Child, self).__init__()
I could store ATTRS as self.ATTRS in Child, and then use self.ATTRS in Parent successfully, but it seems preferable to me to have them stored as a class variable.
Alternatively I could pass ATTRS as a parameter like so:
class Child(Parent):
#class variable
ATTRS = [attr1, attr2, attr3]
def __init__():
super(Child, self).__init__(ATTRS)
but I'm wondering whether this somehow defeats the point of using inheritance in the first place?
I'd be grateful for any ideas, hints or feedback whether I'm barking up the wrong tree completely!
Thanks

You were close. This works:
class Parent(object):
def __init__(self):
for attr in self.ATTRS:
setattr(self, attr, 0)
class Child(Parent):
#class variable
ATTRS = ['attr1', 'attr2', 'attr3']
def __init__(self):
super(Child, self).__init__()
Here, you set the ATTRS list at the class level, and conveniently access it in self.ATTRS at baseclass's __init__.

Adding to the above answer,
Although ATTR is a class variable in the child class, the instance variable of the child class is able to access the class variable ATTR because, Python does the following using namespaces
Checks the namespace of the instance (self.dict) for the ATTR variable.
If the above fails then it checks the namespace of its class. So it is able to get the value of the class variable although it's accessed using the instance variable.

Related

Limit access to a class function to child classes that inherit from the class

I have a use case where there are some attributes that are common to different categories that I capture through a base class and then there are some category specific parameters that are assigned in the child class definitions. For example,
class Parent:
def __init__(self):
self.category_param = None
def run_category(self):
self.category_param.run()
class Child(Parent):
def __init__(self, cat_param):
super(Child, self).__init__()
self.category_param = cat_param
Although the function run_category has the same form for all instantiations of class Child, it does use a category specific param that is None/undefined in class Parent.
I am looking for a clean way to prevent being able to call run_category() from an instantiation of class Parent.
parent = Parent()
parent.run() # Should throw an error
I would like the above code to throw an error message that I have control over (since the error message None has no attribute run() is undesirable and would be the current way the above code crashes).
A possibility is of course to define run_category as,
def run_category(self):
if self.category_param is not None:
self.category_param.run()
else:
raise NotImplementedError("method run() is not available for abstract base class Parent")
but I was looking for something cleaner/more pythonic.

Extend Python class's init method

I have a BaseClass and an AbstractClass that inherits from the BaseClass. This is the structure I have in place
class BaseClass(object):
def __init__(self, initialize=True):
self.name = 'base_class'
self.start = 0
if initialize:
self.start = 100
class AbstractClass(BaseClass):
def __init__(self):
self.name = 'asbtract_class'
super(BaseClass, self).__init__()
I want to pass the abstract class an initialize parameter that gets transferred to the base class and if True sets the object's start value to 100.
I tried using the super(BaseClass, self).__init__() but the abstract class gets no start attribute. I get an error when I try to access it.
How can I pass a value the initialize argument to the AbstractClass and use the BaseClass's __init__ method to set the start attribute on the AbstractClass.
The code I used
best = BaseClass()
abs = AbstractClass()
abs.start # AttributeError: 'AbstractClass' object has no attribute 'start'
To invoke the constructor of the super class you should use the class name of the sub class and not the super class, i.e.:
class AbstractClass(BaseClass):
def __init__(self):
super(AbstractClass, self).__init__()
self.name = 'abstract_class'
Note also that I changed the order of invoking the constructor of the super class and setting the name attribute. If you set it before calling the super, the attribute would be overridden by the constructor of the super class, which is most likely not what you intended
And as #Sraw pointed out, for python 3 the notation of calling the super no longer requires the referencing of the class name and can be simplified to
class AbstractClass(BaseClass):
def __init__(self):
super().__init__()

Refer to a class outside its method?

I need to deliver something like this in my program
class the_class_name(Parent):
the_attribute = self.parent_class_method()
#the parent class method will return a value
#but I cannot use self here since there's no self
How can I carry this out? Is there any other alternative that can do the job for me?
I have tried using __init__ like this:
def __init__(self):
Parent.__init__(self)
self.attribute = self.the_method()
But then I have problem creating the object, it won't receive any parameters that the Parent class normally receives anymore
Sounds like you are looking for __init__:
class TheClassName(Parent):
def __init__(self):
# Set attribute to the result of the parent method
self.attribute = super(TheClassName, self).the_method()
EDIT
If your parent class has parameters in it's own __init__ function, include them in the child class:
class Parent(object):
def __init__(self, foo, bar):
...
#classmethod
def the_method(cls):
...
class TheClassName(Parent):
def __init__(self, foo, bar):
super(TheClassName, self).__init__(foo, bar)
self.attribute = super(TheClassName, self).the_method()
I don't quite understand why you don't just call the parent method on your child object when you need the value though.
There is no self at that point of the creation of the subclass, nor is there an instance of the Parent class. That means the only Parent class methods you could call would have to be either static or class methods.
To demonstrate:
class Parent(object):
#staticmethod
def static_method():
return 42
#classmethod
def class_method(cls):
return 43
class TheClassName(Parent):
the_attribute = Parent.static_method()
another_attribute = Parent.class_method()
print(TheClassName.the_attribute) # -> 42
print(TheClassName.another_attribute) # -> 43
You must use class methods, declared with the #classmethod decorator, or a #staticmethod. The #classmethod decorator is preferable so that inheritance is handled correctly, i.e. the method is invoked on the derived class (a bit of a technicality, if you are still learning this).
class Alpha(object):
#classmethod
def method1(cls):
return 'method1 has been called on {}'.format(cls)
class Beta(Alpha):
def __init__(self):
self.myattr = Beta.method1()
print(Beta().myattr)
method1 has been called on class <'__main__.Beta'>
Use
super(ClassName, self).methodname(arg)
inside a method
def child_method(self, arg):
super(ClassName, self).methodname(arg)
You cannot use self outside a method.

Turn a instance to its class's child type (calling super for classmethod)

class Parent:
def __init__(self, name):
self.name = name
#classmethod
def get(cls, name):
return cls(name)
def greeting(self):
print 'Hello ' + self.name
class Child(Parent):
#classmethod
def get(cls, name):
p = Parent.get(name)
p.full_name = 'James ' + name
return p
def greeting(self):
print 'Bye ' + self.full_name
as you can see, I added an attribute full_name in the Child's get method to the instance of Parent, the problem is, now the instance if still in the type of Parent, how could I turn it into Child?
You don't need to change the type of the returned object. What you need to do is call the parent class's implementation of the method with the same cls the child class's method is using. This is one of the things super is for. First, you'll need to make sure you're using new-style classes:
class Parent(object):
# continue as usual
Python has two implementations of the class system, because they didn't get it right the first time. Always inherit from object if you're not inheriting from anything else, to make sure you're using the newer, better system.
Now, super. Instead of using Parent.get(name), use a super call:
p = super(Child, cls).get(name)
p will have the correct type, since cls in the parent's method was the same class as cls in the child's method. (Note that if you later added a Grandchild class, this line would produce Grandchild objects instead of Child objects. This is most likely the right behavior.)
Make object as Parent base class:
class Parent(object):
and then change this line in Child p = Parent.get(name) to:
p = super(Child, cls).get(name)
This way super(Child, cls).get(name) will bound cls (Child) to parent get method, so in result Parent.get will be called with Child class as cls parameter. Of course to make this work you need new style classes that why object should be in Parent bases!

Can't access parent member variable in Python

I'm trying to access a parent member variable from an extended class. But running the following code...
class Mother(object):
def __init__(self):
self._haircolor = "Brown"
class Child(Mother):
def __init__(self):
Mother.__init__(self)
def print_haircolor(self):
print Mother._haircolor
c = Child()
c.print_haircolor()
Gets me this error:
AttributeError: type object 'Mother' has no attribute '_haircolor'
What am I doing wrong?
You're mixing up class and instance attributes.
print self._haircolor
You want the instance attribute, not the class attribute, so you should use self._haircolor.
Also, you really should use super in the __init__ in case you decide to change your inheritance to Father or something.
class Child(Mother):
def __init__(self):
super(Child, self).__init__()
def print_haircolor(self):
print self._haircolor

Categories