-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Wrong check on multiple inheritance #14279
Comments
I don't think this is a false positive. Mypy is pointing out a legitimate type issue here. There are two base classes of I'm surprised that mypy doesn't point out a similar error for class If you are confident that this type violation is safe in your code, you can use |
@erictraut that would raise 2 issues:
Further more, using |
Mypy reports many forms of type violations that do not necessarily generate exceptions at runtime. The question here is not whether In general, when two or more classes are used as subclasses and they declare a symbol of the same name within their respective scopes, the types of those symbols must be the same. Consider the following example: class ChildA:
x: str = "hi"
def method_a(self):
print(self.x + " there!")
class ChildB:
x: int = 0
def method_b(self):
print(self.x + 1)
class ParentC(ChildA, ChildB): pass
c = ParentC()
c.method_a()
c.method_b() # Crash This leads to problems because Your code is doing something similar in that it's defining the symbol The type-safe way to do what you're doing is to define a protocol class that describes the interface that all class MProto(Protocol):
# Describe the interface that all M classes must provide
pass
class A:
class _M_A: pass
M: type[MProto] = _M_A
class B0(A):
class _M_B0(A._M_A): pass
M: type[MProto] = _M_B0
class B1(A):
class _M_B1(A._M_A):pass
M: type[MProto] = _M_B1
class C(B0, B1):
class _M_C(B0._M_B0, B1._M_B1): pass
M: type[MProto] = _M_C
class D0(B0): pass
class D1(B1): pass
class D(D0, D1, C): pass |
Why should they be the same ? To me it looks sufficient that any class deriving from both would have a type that would be compatible with the parent's. Provided that we could legally derive from both As for using protocols, my feeling right now is that:
|
Ah, I see what you mean now. Yes, I agree with your analysis. I missed the fact that in your example, the resolved version of |
Let's start with a couple of base classes in a classical diamond-inheritance shape, with member conflict explicitly solved:
All of these in real code are abstract base classes, classes
A
,B0
,B1
all add behaviors of their own; classC
is here precisely to provide this version ofM
compatible with bothB0
andB1
and deal with diamond inheritance in a "centralized" way, so all classes deriving fromB0
andB1
don't have to duplicate the same version ofM
over and over. It could, though, also add behavior of its own.More abstract classes derive from
B0
andB1
, each adding more behavior:And then comes concrete class deriving from
D0
andD1
, deriving fromC
to get the diamond inheritance solved:python
has no issue with this, and understandsD.M
as defined asC.M
.mypy
on the other hand does not take python's resolution of the problem into account, and compares all bases classes, leading to false positive:To Reproduce
See https://mypy-play.net/?gist=132dcea42366fad6d56d9c150d8e6c60
The text was updated successfully, but these errors were encountered: