Skip to main content
added 20 characters in body
Source Link
Ondrej K.
  • 9.9k
  • 11
  • 31
  • 46

OK, this won't really fit into comments any more, but there seems to be a bit of misunderstanding at play here.

For:

class C:
    def __init__(self, arg1=1):
        self.attr1 = arg1

class D(C):
    def __init__(self, arg1):
        super().__init__()

This would then yield:

d = D(2)
print(d.attr1)  # prints: 1

Because through super().__init__() we called parent's constructor, but we did not pass any argument and it ran with its default value.

Or to quote the docs for what super does:

Return a proxy object that delegates method calls to a parent or sibling class of type. This is useful for accessing inherited methods that have been overridden in a class.

I.e. you "only" get (possibly bound) resolved method to call. Not more, not less. It does not sort out any arguments you pass around. So your example would actually not work, because both parents' constructors expect an instance and two more arguments.

In your example you could say:

class A:
    def __init__(self, x1, x2):
        print(f"Entering A [x1={x1}, x2={x2}]")
        print("Leaving A")

class B:
    def __init__(self, y1, y2):
        print(f"Entering B [y1={y1}, y2={y2}]")
        print("Leaving B")

class C(A, B):
    def __init__(self, x1, y1, x2, y2):
        print(f"Entering C [x1={x1}, y1={y1}, x2={x2}, y2={y2}]")
        A.__init__(self, x1, x2)
        B.__init__(self, y1, y2)
        print("Leaving C")

Because you call each specific parent constructors with corresponding specific arguments. The documentation (same place, few paragraph down on second common use case with multiple inheritance) also contains hint regarding that:

Good design dictates that this method have the same calling signature in every case (because the order of calls is determined at runtime, because that order adapts to changes in the class hierarchy, and because that order can include sibling classes that are unknown prior to runtime).

That is, you get a proxy to call the method... and you call it using its arguments... which has to be a fixed / stable set as you cannot be sure up front which method are you reaching through that proxy.

OK, this won't really fit into comments any more, but there seems to be a bit of misunderstanding at play here.

For:

class C:
    def __init__(self, arg1=1):
        self.attr1 = arg1

class D(C):
    def __init__(self, arg1):
        super().__init__()

This would then yield:

d = D(2)
print(d.attr1)  # prints: 1

Because through super().__init__() we called parent's constructor, but we did not pass any argument and it ran with its default value.

Or to quote the docs for what super does:

Return a proxy object that delegates method calls to a parent or sibling class of type. This is useful for accessing inherited methods that have been overridden in a class.

I.e. you "only" get (possibly bound) resolved method to call. Not more, not less. It does not sort out any arguments you pass around. So your example would actually not work, because both parents' constructors expect an instance and two more arguments.

In your example you could say:

class A:
    def __init__(self, x1, x2):
        print(f"Entering A [x1={x1}, x2={x2}]")
        print("Leaving A")

class B:
    def __init__(self, y1, y2):
        print(f"Entering B [y1={y1}, y2={y2}]")
        print("Leaving B")

class C(A, B):
    def __init__(self, x1, y1, x2, y2):
        print(f"Entering C [x1={x1}, y1={y1}, x2={x2}, y2={y2}]")
        A.__init__(self, x1, x2)
        B.__init__(self, y1, y2)
        print("Leaving C")

Because you call each specific parent constructors with corresponding specific arguments. The documentation also contains hint regarding that:

Good design dictates that this method have the same calling signature in every case (because the order of calls is determined at runtime, because that order adapts to changes in the class hierarchy, and because that order can include sibling classes that are unknown prior to runtime).

OK, this won't really fit into comments any more, but there seems to be a bit of misunderstanding at play here.

For:

class C:
    def __init__(self, arg1=1):
        self.attr1 = arg1

class D(C):
    def __init__(self, arg1):
        super().__init__()

This would then yield:

d = D(2)
print(d.attr1)  # prints: 1

Because through super().__init__() we called parent's constructor, but we did not pass any argument and it ran with its default value.

Or to quote the docs for what super does:

Return a proxy object that delegates method calls to a parent or sibling class of type. This is useful for accessing inherited methods that have been overridden in a class.

I.e. you "only" get (possibly bound) resolved method to call. Not more, not less. It does not sort out any arguments you pass around. So your example would actually not work, because both parents' constructors expect an instance and two more arguments.

In your example you could say:

class A:
    def __init__(self, x1, x2):
        print(f"Entering A [x1={x1}, x2={x2}]")
        print("Leaving A")

class B:
    def __init__(self, y1, y2):
        print(f"Entering B [y1={y1}, y2={y2}]")
        print("Leaving B")

class C(A, B):
    def __init__(self, x1, y1, x2, y2):
        print(f"Entering C [x1={x1}, y1={y1}, x2={x2}, y2={y2}]")
        A.__init__(self, x1, x2)
        B.__init__(self, y1, y2)
        print("Leaving C")

Because you call each specific parent constructors with corresponding specific arguments. The documentation (same place, few paragraph down on second common use case with multiple inheritance) also contains hint regarding that:

Good design dictates that this method have the same calling signature in every case (because the order of calls is determined at runtime, because that order adapts to changes in the class hierarchy, and because that order can include sibling classes that are unknown prior to runtime).

That is, you get a proxy to call the method... and you call it using its arguments... which has to be a fixed / stable set as you cannot be sure up front which method are you reaching through that proxy.

added 20 characters in body
Source Link
Ondrej K.
  • 9.9k
  • 11
  • 31
  • 46

OK, this won't really fit into comments any more, but there seems to be a bit of misunderstanding at play here.

For:

class C:
    def __init__(self, arg1=1):
        self.attr1 = arg1

class D(C):
    def __init__(self, arg1):
        super().__init__()

Then thisThis would then yield:

d = D(2)
print(d.attr1)  # prints: 1

Because through super().__init__() we called parent's constructor, but we did not pass any argument and it ran with theits default value.

Or to quote the docs for what docssuper does:

Return a proxy object that delegates method calls to a parent or sibling class of type. This is useful for accessing inherited methods that have been overridden in a class.

I.e. you "only" get (possibly bound) resolved method to call. Not more, not less. It does not sort out any arguments you pass around. So youyour example would actually not work, because both parents' constructors expect an instance and two more arguments.

In particular in your example you'd need toyou could say:

class A:
    def __init__(self, x1, x2):
        print(f"Entering A [x1={x1}, x2={x2}]")
        print("Leaving A")

class B:
    def __init__(self, y1, y2):
        print(f"Entering B [y1={y1}, y2={y2}]")
        print("Leaving B")

class C(A, B):
    def __init__(self, x1, y1, x2, y2):
        print(f"Entering C [x1={x1}, y1={y1}, x2={x2}, y2={y2}]")
        A.__init__(self, x1, x2)
        B.__init__(self, y1, y2)
        print("Leaving C")

Because you call each specific parent constructors with corresponding specific arguments. The documentation also contains hint regarding that:

Good design dictates that this method have the same calling signature in every case (because the order of calls is determined at runtime, because that order adapts to changes in the class hierarchy, and because that order can include sibling classes that are unknown prior to runtime).

OK, this won't really fit into comments any more, but there seems to be a bit of misunderstanding at play here.

For:

class C:
    def __init__(self, arg1=1):
        self.attr1 = arg1

class D(C):
    def __init__(self, arg1):
        super().__init__()

Then this would yield:

d = D(2)
print(d.attr1)  # prints: 1

Because through super().__init__() we called parent's constructor, but we did not pass any argument and it ran with the default value.

Or to quote the docs:

Return a proxy object that delegates method calls to a parent or sibling class of type. This is useful for accessing inherited methods that have been overridden in a class.

I.e. you "only" get (possibly bound) resolved method to call. Not more, not less. It does not sort out any arguments you pass around. So you example would actually not work, because both parents' constructors expect an instance and two more arguments.

In particular in your example you'd need to say:

class A:
    def __init__(self, x1, x2):
        print(f"Entering A [x1={x1}, x2={x2}]")
        print("Leaving A")

class B:
    def __init__(self, y1, y2):
        print(f"Entering B [y1={y1}, y2={y2}]")
        print("Leaving B")

class C(A, B):
    def __init__(self, x1, y1, x2, y2):
        print(f"Entering C [x1={x1}, y1={y1}, x2={x2}, y2={y2}]")
        A.__init__(self, x1, x2)
        B.__init__(self, y1, y2)
        print("Leaving C")

Because you call specific parent constructors with specific arguments. The documentation also contains hint regarding that:

Good design dictates that this method have the same calling signature in every case (because the order of calls is determined at runtime, because that order adapts to changes in the class hierarchy, and because that order can include sibling classes that are unknown prior to runtime).

OK, this won't really fit into comments any more, but there seems to be a bit of misunderstanding at play here.

For:

class C:
    def __init__(self, arg1=1):
        self.attr1 = arg1

class D(C):
    def __init__(self, arg1):
        super().__init__()

This would then yield:

d = D(2)
print(d.attr1)  # prints: 1

Because through super().__init__() we called parent's constructor, but we did not pass any argument and it ran with its default value.

Or to quote the docs for what super does:

Return a proxy object that delegates method calls to a parent or sibling class of type. This is useful for accessing inherited methods that have been overridden in a class.

I.e. you "only" get (possibly bound) resolved method to call. Not more, not less. It does not sort out any arguments you pass around. So your example would actually not work, because both parents' constructors expect an instance and two more arguments.

In your example you could say:

class A:
    def __init__(self, x1, x2):
        print(f"Entering A [x1={x1}, x2={x2}]")
        print("Leaving A")

class B:
    def __init__(self, y1, y2):
        print(f"Entering B [y1={y1}, y2={y2}]")
        print("Leaving B")

class C(A, B):
    def __init__(self, x1, y1, x2, y2):
        print(f"Entering C [x1={x1}, y1={y1}, x2={x2}, y2={y2}]")
        A.__init__(self, x1, x2)
        B.__init__(self, y1, y2)
        print("Leaving C")

Because you call each specific parent constructors with corresponding specific arguments. The documentation also contains hint regarding that:

Good design dictates that this method have the same calling signature in every case (because the order of calls is determined at runtime, because that order adapts to changes in the class hierarchy, and because that order can include sibling classes that are unknown prior to runtime).

Source Link
Ondrej K.
  • 9.9k
  • 11
  • 31
  • 46

OK, this won't really fit into comments any more, but there seems to be a bit of misunderstanding at play here.

For:

class C:
    def __init__(self, arg1=1):
        self.attr1 = arg1

class D(C):
    def __init__(self, arg1):
        super().__init__()

Then this would yield:

d = D(2)
print(d.attr1)  # prints: 1

Because through super().__init__() we called parent's constructor, but we did not pass any argument and it ran with the default value.

Or to quote the docs:

Return a proxy object that delegates method calls to a parent or sibling class of type. This is useful for accessing inherited methods that have been overridden in a class.

I.e. you "only" get (possibly bound) resolved method to call. Not more, not less. It does not sort out any arguments you pass around. So you example would actually not work, because both parents' constructors expect an instance and two more arguments.

In particular in your example you'd need to say:

class A:
    def __init__(self, x1, x2):
        print(f"Entering A [x1={x1}, x2={x2}]")
        print("Leaving A")

class B:
    def __init__(self, y1, y2):
        print(f"Entering B [y1={y1}, y2={y2}]")
        print("Leaving B")

class C(A, B):
    def __init__(self, x1, y1, x2, y2):
        print(f"Entering C [x1={x1}, y1={y1}, x2={x2}, y2={y2}]")
        A.__init__(self, x1, x2)
        B.__init__(self, y1, y2)
        print("Leaving C")

Because you call specific parent constructors with specific arguments. The documentation also contains hint regarding that:

Good design dictates that this method have the same calling signature in every case (because the order of calls is determined at runtime, because that order adapts to changes in the class hierarchy, and because that order can include sibling classes that are unknown prior to runtime).