narrowing does not consider overlapping union members
seo
disjoint
Description
Code sample in basedpyright playground
from typing import reveal_type
class Foo: ...
class FooBar(Foo, list[int]): ...
def f(x: Foo | list[str]):
if isinstance(x, list):
reveal_type(x[0]) # str, expect object
f(FooBar(1))
I think multiple inheritance brings a lot of issues with it. Is it worth taking away valuable narrowing from people who try to avoid multiple inheritance?
It would probably need to be a setting so users can decide for themselves, similar to what I did with strictGenericNarrowing
I think multiple inheritance brings a lot of issues with it. Is it worth taking away valuable narrowing from people who try to avoid multiple inheritance?
@beauxq if multiple inheritance were to be banned, what would that look like to you. as python does not have constructs like interface/trait, i would imagine that a change like this would result in a lot of patterns becomming unusable
I'm not sure how to describe what it would look like to me, because to me it just looks pretty normal.
Just the other day, I thought "I could use multiple inheritance for this." But because I try to avoid multiple inheritance, I thought about it a little longer, and I found a good solution that didn't involve multiple inheritance.
Yes, a lot of patterns become unusable. But the same can be said about usage of static type checking in general. For example, this is a pattern that many people have found useful: (from a real code base that I deal with)
import sys
module = sys.modules[__name__]
for name, value in arguments.__dict__.items():
setattr(module, name, value)
But there's just no way to get good static type checking around that.
You might be tempted to point at some code that uses multiple inheritance, and say "How would you do that without multiple inheritance?" But to me, that's like someone asking pointing at this code here, and saying "How would you type check that?" The answer is: I wouldn't have written anything remotely close to that to begin with.
We decide to use different patterns, so that we can get better help from our tools.
And I don't think the person who wrote this code, or the people who use multiple inheritance, are doing something wrong. It's just not worth it for me personally.
class ImARealClass:
name = "amongus
class ImAnInterface:
def do_something():
print("hi")
class ImAnImplementation(ImARealClass, ImAnInterface): # illegal multiple inheritance
pass
i haven't put the thought in to realize it, but what if we had some kind of @interface decorator or something that could be used to allow multiple inheritance while still protecting against non-disjoint cases?
sounds like my @implements decorator hack is relevant here? https://github.com/DetachHead/basedpyright/issues/775#issuecomment-2807917771
@beauxq i agree with your logic, i feel the same way when arguing against circular imports. i always tell people they can typically be avoided if you just think a bit more about what you're doing.
though in my experience avoiding multiple inheritance in python has been a lot harder than avoiding circular imports. so i'm curious if you could provide any examples of how you avoided them
Often it involves favoring composition over inheritance (which is a good idea even if you're not trying to avoid multiple inheritance).
Another common way is using Protocol, since it doesn't require inheritance (keeping in mind that @runtime_checkable isn't safe, so avoiding that).
keeping in mind that @runtime_checkable isn't safe, so avoiding that
we have an open issue somewhere to report a warning on usage of @runtine_checkable