Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python - Accessing superclass's class attribute in super().__init__()

Tags:

I have a class Parent with many instance properties, and I always pass a dict to initialize an instance. Like this:

info = {
    "name" : "Bob",
    "col" : 5,
    "line" : 10,
    "type" : "Alien"
}

p = Parent(info)

And in __init__ method I don't want to write this.property_name = value for each property cuz the code will be very long. For instance:

class Parent(object):

    def __init__(self, kwargs):
        this.name = kwargs.get("name")
        this.col = kwargs.get("col")
        this.line = kwargs.get("line")
        this.type = kwargs.get("type")

So I want to use a function to iterate the dict to set these instance properties. This is the function I wrote:

def initialize_properties(instance, names, kwargs):
    for name in names:
        setattr(instance, name, kwargs.get(name))

It seems that I need to store the property name list names somewhere, and I decide to store it as a class attribute, because I want my class to be human friendly (when somebody reads the class definition he knows what instance properties this class has). So I changed my class definition as follows:

class Parent(object):
    props = ("name", "col", "line", "type")

    def __init__(self, kwargs):
        initialize_properties(self, self.props, kwargs)

This works fine when inheritance is NOT considered. The problem occurs when I subclass Parent:

class Child(Parent):
    props = ("foo", "bar")

    def __init__(self, kwargs):
        super().__init__(kwargs)
        initialize_properties(self, self.props, kwargs)

I want instances of Child to inherit all the instance properties in superclass Parent, with some child-specific instance properties as well.(This is why we use inheritance, isn't it?) So I overwrite the class attribute props to define child-specific properties.

But it doesn't work.

info = {
    "name" : "Bob",
    "col" : 5,
    "line" : 10,
    "type" : "Alien",
    "foo" : 5,
    "bar" : 10
}

c = Child(info)

Instance c only has c.foo and c.bar defined and set, while c.name is not defined.

After some digging I found that when Parent.__init__(self, kwargs) is called through the super() function, the self argument passed is of class Child, so self.props evaluates to Child.props.

If I want to set the instance properties in Parent.props, I have to explicitly use Parent.props in Parent.__init__(self, kwargs), which is:

class Parent(object):
    props = ("name", "col", "line", "type")

    def __init__(self, kwargs):
        initialize_properties(self, Parent.props, kwargs)

This will solve the problem, but I think it's not very "clean" because you have to hard-code the class name Parent.

So my question is: Is there any way to detect the current class and access its class attributes, when you are calling a chain of super().__init__() to initialize an subclass instance?

like image 880
star.lit Avatar asked Feb 19 '17 03:02

star.lit


People also ask

What is super () __ Init__ in Python?

__init__() Call in Python. When you initialize a child class in Python, you can call the super(). __init__() method. This initializes the parent class object into the child class. In addition to this, you can add child-specific information to the child object as well.

What does super () do in Python?

The super() function is used to give access to methods and properties of a parent or sibling class. The super() function returns an object that represents the parent class.

How do you access super class in Python?

Using Super(): Python super() function provides us the facility to refer to the parent class explicitly. It is basically useful where we have to call superclass functions. It returns the proxy object that allows us to refer parent class by 'super'.

How do you access superclass variables in subclass in Python?

Accessing Parent Class Functions This is really simple, you just have to call the constructor of parent class inside the constructor of child class and then the object of a child class can access the methods and attributes of the parent class.


2 Answers

You could implement a method that collects the props from all supeclasses and use it in the __init__:

class Parent(object):
    props = ("name", "col", "line", "type")

    def __init__(self, **kwargs):
        def __init__(self, kwargs):
        initialize_properties(self, self.get_props(), kwargs)

    def get_props(self):
        return sum((getattr(k, 'props', ()) for k in self.__class__.mro()), ())

Now, you can simply define subclasses the way you wanted. You wouldn't even have to override the constructor:

class Child(Parent):
    props = ("foo", "bar")

That being said, you claim you want your classes human friendly. If a human reads your child class, they will be glad to read:

class Child(Parent):
    props = 'foo', 'bar', 'name', 'col', 'line', 'type'

and have all the class' props available in one place.

like image 139
user2390182 Avatar answered Sep 24 '22 11:09

user2390182


self.props first references instance attribute, then class attribute, and parents' class attribute, ...

By defining Child.props, self.props refers Child.props class attribute, stopping there, not searching in parent class.

How about make the Child.props also include parents' props?

class Child(Parent):
    props = Parent.props + ("foo", "bar")   # <---

    def __init__(self, kwargs):
        super().__init__(kwargs)
        initialize_properties(self, self.props, kwargs)
like image 29
falsetru Avatar answered Sep 24 '22 11:09

falsetru