netrate
netrate

Reputation: 443

Python inheritance - can you have a default value in a parent?

If I had a parent class attribute that all of the child classes are going to inherit, can I set a default so that when the object is created, it automatically take the default from the parent class and no argument has to be given when creating it?

class F1(object):
   def __init__(self, sick="flu"):
       self.sick = sick

class F2(F1):
   def __init__(self, sick, cure):
       super(F2, self).__init__(sick)
       self.cure = cure

a = F2("bed rest")
print(a.sick)
print(a.cure)

this is just a sample bit of code to show what I mean. I want every child to inherit the "sick" from the parent so I do not have to send that argument in when creating the object. Is this possible? Is there a different way of doing this same thing? Would it be better to make "sick" a class attribute?

Upvotes: 4

Views: 5326

Answers (3)

vogelito
vogelito

Reputation: 48

I know this is 5 years old, but I came across it trying to figure out a related issue and saw it has 4k views. Just thought I'd suggest an alternative:

class F1(object):
   def __init__(self, sick="flu"):
       self.sick = sick

class F2(F1):
   def __init__(self, cure="bed rest", **kwargs):
       super().__init__(**kwargs)
       self.cure = cure

a = F2(cure="wait it out")
print(a.sick)   # => flu
print(a.cure)   # => wait it out

a = F2(sick="covid", cure="quarantine")
print(a.sick)  # => covid
print(a.cure)  # => quarantine

a = F2(sick="anything")
print(a.sick)  # => anything
print(a.cure)  # => bed rest

(Of course, above works without declaring default value for cure as well.)

You could also do it without calling with requiring keyword arguments like so:

class F1(object):
   def __init__(self, sick="flu"):
       self.sick = sick

class F2(F1):
   # Without keyword arguments, must call args in order F2(cure, sick)
   def __init__(self, cure="bed rest", *args, **kwargs):
       super().__init__(*args, **kwargs)
       self.cure = cure

a = F2("sleep")
print(a.sick)   # => flu
print(a.cure)   # => sleep

a = F2(sick="covid", cure="quarantine")
print(a.sick)  # => covid
print(a.cure)  # => quarantine

a = F2("sleep", "any sickness")
print(a.sick)  # => any sickness
print(a.cure)  # => sleep

Upvotes: 2

umläute
umläute

Reputation: 31284

the problem with your code is, that you are declaring F2.__init__ to have two explicit arguments, even though you only want to pass one. If you want to be able to optionally override the creation argument of F1 you need to handle that yourself (see F3)

class F1(object):
   def __init__(self, sick="flu"):
       self.sick = sick

class F2(F1):
   def __init__(self, cure):
       super(F2, self).__init__()
       self.cure = cure

class F3(F1):
   def __init__(self, cure, sick=None):
       if sick is None:
          super(F3, self).__init__()
       else:
          super(F3, self).__init__(sick)
       self.cure = cure

a = F2("bed rest")
print("%s -> %s" % (a.sick, a.cure))

b = F3("inhale")
print("%s -> %s" % (b.sick, b.cure))

c = F3(sick="curiosity", cure="none")
print("%s -> %s" % (c.sick, c.cure))

Upvotes: 5

laike9m
laike9m

Reputation: 19328

Using super is the standard way of doing this in Python. If you want to override, just override...

class F2(F1):
   def __init__(self, sick, cure):
       super(F2, self).__init__(sick)
       self.cure = cure
       self.sick = sick + 1

Adding class attribute could be an option, depending on your need. From your description, I'd say it sounds better, because the default value of sick never change, and that's probably what you need.

Using class attribute does not affect overriding, because when assigning attribute on an instance, class attribute is not touched. An example:

>>> class F:
...     a = 1
...
>>> f1, f2 = F(), F()
>>> f2.a = 2
>>> f1.a
1

Upvotes: 1

Related Questions