8

I am new to python. Im trying to access the parent class variable in child class using super() method but it throws error "no arguments". Accessing class variable using class name works but i like to know whether it is possible to access them using super() method.

class Parent(object):
        __props__ = (
            ('a', str, 'a var'),
            ('b', int, 'b var')
        )

        def __init__(self):
            self.test = 'foo'

class Child(Parent):
    __props__ = super().__props__ + (
        ('c', str, 'foo'),
    ) # Parent.__props__


    def __init__(self):
        super().__init__()

Error:

    __props__ = super().__props__ + (
RuntimeError: super(): no arguments
scnerd
  • 5,836
  • 2
  • 21
  • 36
Naga
  • 363
  • 1
  • 4
  • 14
  • 1
    I can't see what you are trying to do here. `super()` is something you call in an instance method, not at class level. And you shouldn't define your own double-underscore attributes anyway. – Daniel Roseman Nov 07 '18 at 13:06
  • In python 3, classes by default derive from `object` so you can simply remove `object`; in regards to the issue; you're trying to use `super()` in a class top level definition; which can't be done. – Julian Camilleri Nov 07 '18 at 13:07
  • @DanielRoseman To make code more maintainable, for example in case i want to change the base type’s name. When using super everywhere, you just need to change it in the class line. – Naga Nov 07 '18 at 13:15

5 Answers5

4

You could define an __init_subclass__ method of the Parent class that initializes Child.__props__. This method is called every time a subclass of of Parent is created, and we can use it to modify the __props__ that class inherits with an optional __props__ argument passed as part of the class definition.

class Parent:
    __props__ = (('a', str, 'a var'), ('b', int, 'b var'))
    def __init_subclass__(cls, __props__=(), **kwargs):
        super().__init_subclass__(**kwargs)
        cls.__props__ = cls.__props__ + __props__

class Child(Parent, __props__=(('c', str, 'foo'),)):
    pass

print(Child.__props__)
# (('a', <class 'str'>, 'a var'), ('b', <class 'int'>, 'b var'), ('c', <class 'str'>, 'foo'))

class GrandChild(Child, __props__=(('d', float, 'd var'),)):
    pass

print(GrandChild.__props__)
# (('a', <class 'str'>, 'a var'), ('b', <class 'int'>, 'b var'), 
#  ('c', <class 'str'>, 'foo'), ('d', <class 'float'>, 'd var'))
Patrick Haugh
  • 59,226
  • 13
  • 88
  • 96
4

A bit late to the party but this is a job for metaclasses:

class Parent(object):
    __props__ = (
        ('a', str, 'a var'),
        ('b', int, 'b var')
    )

    def __init__(self):
        self.test = 'foo'

class AddPropsMeta(type):
    def __init__(cls, name, bases, attrs):
        cls.__props__ = sum((base.__props__ for base in bases), ()) + cls.__props__
        super().__init__(name, bases, attrs)

class Child(Parent, metaclass=AddPropsMeta):
    __props__ = (
        ('c', str, 'foo'),
    )
>>> Child.__props__
(('a', str, 'a var'), ('b', int, 'b var'), ('c', str, 'foo'))
Seb
  • 4,422
  • 14
  • 23
3

super helps you get the parent class when you have an instance of it. As far as I know, there's no easy way to do this at the class level without an instance, like you're trying to do. The only way I could think of to do this is to refer to the parent class explicitly:

class Child(Parent):
    __props__ = Parent.__props__ + ...

To clarify a bit further, there's two basic problems:

  • super() is syntactic sugar for super(Child, self), or more generally, super(type(self), self). Since there is no self where you're using it, it doesn't make sense.
  • Even the class Child doesn't exist at the point that super() is getting called. It's still in the process of being defined, and so it would be invalid syntax to even have super(Child, self) (go ahead and try it, I can wait), because Child isn't a thing yet.

As such, you'll need to explicitly refer to the parent class, like I show above.

scnerd
  • 5,836
  • 2
  • 21
  • 36
0

You can use the __new__ method to change attributes of the parent class.

class Parent(object):
    __props__ = (
        ('a', str, 'a var'),
        ('b', int, 'b var')
    )

    def __init__(self):
        self.test = 'foo'


class Child(Parent):

    def __new__(cls, *args, **kwargs):
        parent = super(Child, cls)
        cls.__props__ = parent.__props__ + (('c', str, 'foo'),)
        return super(Child, cls).__new__(cls, *args, **kwargs)

p = Parent
print(p.__props__)
c = Child()
print(c.__props__)

Output:

(('a', <type 'str'>, 'a var'), ('b', <type 'int'>, 'b var'))
(('a', <type 'str'>, 'a var'), ('b', <type 'int'>, 'b var'), ('c', <type 'str'>, 'foo'))

At the same time be aware of this:

print(p.__props__)
c = Child
print(c.__props__)
c = Child()
print(c.__props__)
c = Child
print(c.__props__)

Output:

(('a', <type 'str'>, 'a var'), ('b', <type 'int'>, 'b var'))
(('a', <type 'str'>, 'a var'), ('b', <type 'int'>, 'b var'))
(('a', <type 'str'>, 'a var'), ('b', <type 'int'>, 'b var'), ('c', <type 'str'>, 'foo'))
(('a', <type 'str'>, 'a var'), ('b', <type 'int'>, 'b var'), ('c', <type 'str'>, 'foo'))

The __props__ will change only after the first instantiation of your Child class.

onodip
  • 635
  • 7
  • 12
  • As you point out yourself, initializing class properties in the constructor gives you results that don't really fit problem. Why would you recommend this approach at all? – Silly Freak Nov 07 '18 at 13:30
  • The question was, if it is possible to access and modify class attributes with `super()`. From the example it is not clear for me, how the attribute is used, whether it has to be modified before instantiating the class. – onodip Nov 07 '18 at 13:49
0

Your mistake is you write the super you use the Parent class name only.

class Parent:
        __props__ = (
            ('a', str, 'a var'),
            ('b', int, 'b var')
        )

    def __init__(self):
        self.test = 'foo'


class Child(Parent):
    __props__ = Parent.__props__ + (
        ('c', str, 'foo'),
    ) # Parent.__props__


    def __init__(self):
        Parent.__init__()

Hope It's useful. Thank you.

chetan mahajan
  • 349
  • 4
  • 15