Skip to content

Using class-typed variable as first argument to map #3092

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
pkch opened this issue Mar 30, 2017 · 4 comments
Closed

Using class-typed variable as first argument to map #3092

pkch opened this issue Mar 30, 2017 · 4 comments

Comments

@pkch
Copy link
Contributor

pkch commented Mar 30, 2017

How do I annotate a function that takes a class argument and uses it as the first argument in map?

A structural type would fit perfectly, but those aren't part of mypy yet.

Using Type[X] doesn't work:

class X:
    def __init__(self, x: int) -> None:
        pass

def f(cls: Type[X]) -> None:
    map(cls, [1, 2, 3]) # No overload variant of "map" matches argument types [Type[b.X], builtins.list[builtins.int*]]

Not sure if it's intentional or not, but it's somewhat understandable since Type[X] may be a subclass of X rather than X itself, and so there's no guarantee its __init__ accepts an int argument.

I tried to fall back to just type, but it still failed because even though cls can be called with an integer argument and returns Any, type isn't accepted as a subtype of Callable[[T], S]:

def g(cls: type) -> None:
    reveal_type(cls(1)) # Any
    map(cls, [1, 2, 3]) # No overload variant of "map" matches argument types [builtins.type, builtins.list[builtins.int*]]

Edit: after I fixed a typo, using Callable ended up working:

def h(cls: Callable[[int], S]) -> None:
    map(cls, [1, 2, 3])

h(X)

Is this the right way to deal with it?

@gvanrossum
Copy link
Member

I think there may already be an existing issue requesting that Type[A] be considered a callable in all contexts. If you can't find such an issue, maybe this issue can serve as such.

Maybe this particular example is related to overload resolution not checking for TypeType.

However you're also right that for this particular definition of f(), using Callable instead of Type for the argument makes more sense (since it does nothing that requires the argument to be a subclass of A or even a class).

@pkch
Copy link
Contributor Author

pkch commented Mar 31, 2017

I think there may already be an existing issue requesting that Type[A] be considered a callable in all contexts. If you can't find such an issue, maybe this issue can serve as such.

@ilevkivskyi explained to me they are not quite the same: Type[A] doesn't know the signature of A. I think it is by design, since subclasses of A may change __init__ signature.

But is it worth fixing the behavior with type?

@gvanrossum
Copy link
Member

Yeah, but... We allow calling the constructor of a class in a classmethod, which has the same weakness, and we allow that intentionally (because we have valid real-world use cases). E.g. this is valid:

class A: 
    def __init__(self, a: int) -> None:
        self.a = a
def f(a: Type[A]) -> A:
    return a(0)

(The eventual solution for this would probably be a decorator constraining __init__ per Liskov.)

@ilevkivskyi
Copy link
Member

Both original examples now pass correctly.

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

No branches or pull requests

3 participants