Reputation: 8010
I have two abstract classes with the following definition:
from abc import ABC, abstractmethod
class A(ABC):
def __init__(self, lst):
self.List = lst
@abstractmethod
def __enter__(self) -> 'Component':
return self
@abstractmethod
def __exit__(self, *exc):
pass
class B(ABC):
def __init__(self, lst):
self.List = lst
@abstractmethod
def run(self):
pass
Now, I have a class that inherits from these:
class C(A, B):
def __init__(self, lst):
A.__init__(self, lst)
B.__init__(self, lst)
def __enter__(self) -> 'C':
self.List.append('C.__enter__')
self.run()
return self
def __exit__(self, *exc):
self.List.append('C.__exit__')
def run(self):
self.List.append('C.run')
Finally, I have a class that inherits from C
:
class D(C):
def __init__(self, lst):
super().__init__(lst)
def __enter__(self) -> 'D':
self.List.append('D.__enter__')
super().__enter__()
return self
def __exit__(self, *exc):
super().__exit__()
self.List.append('D.__exit__')
def run(self):
self.List.append('D.run')
super().run()
Now, my code looks like this:
my_d = D( ['start'] )
with my_d:
pass
print(my_d)
From my understanding of how super()
works this should produce the following:
[ start,
D.__enter__,
C.__enter__,
C.run,
C.__exit__,
D.__exit__ ]
but what actually happens is:
[ start,
D.__enter__,
C.__enter__,
D.run,
C.run,
C.__exit__,
D.__exit__ ]
Nowhere do I explicitly call D.run
and yet D.run
is called.
This doesn't really make sense to me unless, when I call super().__enter__
in D, self
somehow thinks it's still inside D
when it's actually in C
. Can anyone enlighten me on this?
Upvotes: 1
Views: 74
Reputation: 4532
def run()
in D overrides the def run()
in C. So when you call run()
in C.__enter__
, it actually calls D.run()
.
When D.run()
is called the super().run()
calls C.run()
.
It was just as confusing to me the first time I learned about python class inheritance but that's just how it works.
Upvotes: 1
Reputation: 46921
you call C.run
when you invoke D.run
:
class D(C):
def run(self):
self.List.append('D.run')
super().run() # this will add 'C.run' to self.List
just the same as with __exit__
and __enter__
.
D.run
is called from the chain D.__enter__
-> C.__enter__
which now calls self.run()
(and as self
has type
D
this will call D.run
-> C.run
).
self
does not think it is 'inside D
'; self
is of the type D
.
if you want to have your desired output you could just not override run
in D
; that way only C.run
would be called.
Upvotes: 3