Skip to content

isinstance with generic type #949

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

Closed
jhance opened this issue Oct 17, 2015 · 10 comments
Closed

isinstance with generic type #949

jhance opened this issue Oct 17, 2015 · 10 comments

Comments

@jhance
Copy link
Collaborator

jhance commented Oct 17, 2015

For example

class A(Generic[T]):
   pass

class B(Generic[T], A[T]):
   pass

def f(x: A[T]):
    if isinstance(x, B[T]):
        ...

is apparently illegal and I'm not sure what replacement I'm supposed to use. It tells me to use a # type comment which isn't really applicable here.

Removing the [T] causes mypy to be unable to infer that x is in fact a B[T].

@jhance jhance changed the title Unable to use IsInstance with generic type Unable to use isinstance with generic type Oct 17, 2015
@JukkaL JukkaL added the bug mypy got something wrong label Oct 17, 2015
@JukkaL
Copy link
Collaborator

JukkaL commented Oct 17, 2015

Hmm this looks like a mypy bug. It should work like this:

class A(Generic[T]):
   pass

class B(Generic[T], A[T]):
   pass

def f(x: A[T]):
    if isinstance(x, B):
        # type of B is B[T] here
        ...

@JukkaL
Copy link
Collaborator

JukkaL commented Oct 17, 2015

And if you try to use isinstance(x, B[T]), mypy should give a useful error message.

@jhance
Copy link
Collaborator Author

jhance commented Oct 18, 2015

That seems kind of limited but I guess its the best we can do given we can't modify the behavior of isinstance itself to respect generics...

I don't like it because it could be both an A[T] and a B[U] with multiinheritance...

@JukkaL
Copy link
Collaborator

JukkaL commented Oct 18, 2015

We are stuck with isinstance semantics here. However, isinstance doesn't seem to be used that often with generic types, at least in the code I've looked at.

@jhance
Copy link
Collaborator Author

jhance commented Oct 19, 2015

The issue is there logically isn't anything we can infer here, as far as I can tell. Since isinstance does not respect generics, we can't either. The best we can do is give an error message instructing the user that they need to cast, or to assume that they want to use the same type variables (but this could be very misleading or, even worse, give an incorrect type).

The use-case I can think of for isinstance with generic types is if you have an AST (something like Node) but you want the option to parameterize the AST (so that different passes can add different data - this type of data wold be the annotation, and then the type-checker would enforce whether or not that pass has occurred yet, which could potentially be quite useful).

@elazarg
Copy link
Contributor

elazarg commented Apr 3, 2017

Related #3037

@ilevkivskyi
Copy link
Member

On current master it behaves like this:

class A(Generic[T]):
   pass

class B(A[T]):
   pass

x: A[int]
if isinstance(x, B):
    reveal_type(x)  # Revealed type is '__main__.B[Any]'
isinstance(x, B[int])  # Parameterized generics cannot be used with class or instance checks

It looks like the issue is fixed only partially. I think the revealed type should be B[int] (even if it is not 100% safe).

@JukkaL JukkaL changed the title Unable to use isinstance with generic type isinstance with generic type May 18, 2018
@sobolevn
Copy link
Member

sobolevn commented Oct 26, 2019

Are there any known work-arounds?

I mean: are there any reliable ways to ensure that x is instance of List[int] or MyCustomGeneric[str]?

Things I have tried:

Is there anything I am missing?

@Stewori
Copy link

Stewori commented Oct 26, 2019

For the record, typechecking of generics does work in pytypes (Python 3.7+ support is almost done but not released as of this writing). The issue linked above rather required type inference which is beyond the scope of pytypes. See the specific example that was requested and the answers for clarification.

@AlexWaygood
Copy link
Member

In 2022, the way to achieve "slightly unsafe" type-narrowing of generics in this fashion is to use typing.TypeGuard (or typing_extensions.TypeGuard on Python <=3.9).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants