You should definitely watch Raymond Hettinger's talk from PyCon 2015, Super is considered super!
But if not, why not add a bunch of print statements to answer your questions?
# (object) is only required in Python2
class Mom(object):
def __init__(self, *args, **kwargs):
print('Mom is initializing - args: {!r} kwargs: {!r}'.format(
args, kwargs))
def do_something(self):
print('Doing some Mom thing')
class Dad(object):
def __init__(self, *args, **kwargs):
print('Dad is initializing - args: {!r} kwargs: {!r}'.format(
args, kwargs))
def do_something(self):
print('Doing some Dad thing')
class Sister(Mom, Dad):
def __init__(self, name):
print('initializing a Sister with name: {!r}'.format(name))
parent = super(Sister, self)
print(type(parent))
print(parent)
print('Calling super __init__')
parent.__init__(name)
def do_something(self, value):
if value == 5:
print('calling method on super')
super(Sister, self).do_something()
else:
print('Sister did something')
class Brother(Mom):
def __init__(self, name):
print('initializing a Brother with name: {!r}'.format(name))
parent = super(Brother, self)
print(type(parent))
print(parent)
print('Calling super __init__')
parent.__init__(name)
def do_something(self, value):
if value == 5:
print('calling method on super')
super(Brother, self).do_something()
else:
print('Brother did something')
b = Brother('Bear')
s = Sister('Moon')
b.do_something(3)
b.do_something(5)
s.do_something(3)
s.do_something(5)
That produces the following output (with added commentary):
<type 'super'>
<super: <class 'Brother'>, <Brother object>>
Calling super __init__
Mom is initializing - args: ('Bear',) kwargs: {}
Apparently, super
returns a class of type super
. Which, according to the documentation, is a proxy object. A proxy, by definition, is a replacement for something else. In this case, the proxy is a replacement for getting Mom
. You can see that when we actually call the __init__
function, that Mom's init function is called.
initializing a Sister with name: 'Moon'
<type 'super'>
<super: <class 'Sister'>, <Sister object>>
Calling super __init__
Mom is initializing - args: ('Moon',) kwargs: {}
You'll notice here, that Dad's init function isn't called. That's because, if you watch Raymond's talk, you'll know that super
looks for the function on parents from left to right.
Brother did something
calling method on super
Doing some Mom thing
You see the same behavior repeated here
Sister did something
calling method on super
Doing some Mom thing
If you change the order on Sister, to Dad, Mom
, you'll see that those calls change:
initializing a Brother with name: 'Bear'
<type 'super'>
<super: <class 'Brother'>, <Brother object>>
Calling super __init__
Mom is initializing - args: ('Bear',) kwargs: {}
initializing a Sister with name: 'Moon'
<type 'super'>
<super: <class 'Sister'>, <Sister object>>
Calling super __init__
Dad is initializing - args: ('Moon',) kwargs: {}
Brother did something
calling method on super
Doing some Mom thing
Sister did something
calling method on super
Doing some Dad thing
To sum up:
A proxy is something that stands in for something else. In our case, super
is a proxy for both Mom & Dad, depending on which order they're inherited from.
Honestly, I couldn't get any kind of meaningful sibling thing to work. I'm not sure when you'd even need that anyway.
An instance of super
is returned.
That would really only work if you had something that inherited from type
, e.g. super(type, type)
, because you need an instance of that type. object
would also work because:
print(isinstance(object, object))
print(isinstance(type, type))
print(isinstance(object, type))
print(isinstance(type, object))
There's some pretty involved magic going on around that process.
- Are you asking how functions return things? Because new is a function - and it returns or it doesn't.
object.__new__
returns something. I'm not sure where the actual source code is for that function, or I could point you there if you were interested in the exact mechanism.