Skip to content
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

SubType and SubInstance #2169

Closed
sirkonst opened this issue Sep 22, 2016 · 9 comments
Closed

SubType and SubInstance #2169

sirkonst opened this issue Sep 22, 2016 · 9 comments

Comments

@sirkonst
Copy link

Hi,

I trying check with mypy following code:

import abc
from typing import Type

class Base(abc.ABC):
    @abc.abstractmethod
    def foo(self) -> None:
        pass

class Foo(Base):
    def foo(self):
        pass

def make_obj(cls: Type[Base]) -> Base:
    inst = cls()  # line 15
    return inst

make_obj(Foo)

And got errors:

subtype.py: note: In function "make_obj":
subtype.py:15: error: Cannot instantiate abstract class 'Base' with abstract attribute 'foo'

I want have some expression for define that variable type is one of subclasses of Base but not Base, somethink like this:

def make_obj(cls: SubType[Base]) -> SubInstance[Base]:
    inst = cls()
    return inst

Is it possible now?

@sirkonst
Copy link
Author

I guess I found solution:

import abc
from typing import Type, TypeVar


class BaseABC(abc.ABC):
    @abc.abstractmethod
    def foo(self) -> None:
        pass

class Foo(BaseABC):
    def foo(self):
        pass

Base = TypeVar('Base', bound=BaseABC)
def make_obj(cls: Type[Base]) -> Base:
    inst = cls()
    return inst


make_obj(Foo)

But maybe open new bugs:

def make_obj_2(cls: Type[Base]=Foo) -> Base:  # -- line 23
    inst = cls()
    return inst

class Bar:
    foo = None  # type: Base                    -- line 29
subtype.py: note: In function "make_obj_2":
subtype.py:23: error: Incompatible types in assignment (expression has type "Foo", variable has type Type[Base])
subtype.py: note: At top level:
subtype.py:29: error: Invalid type "subtype.Base"

@refi64
Copy link
Contributor

refi64 commented Sep 22, 2016

What if you do:

T = TypeVar('T', bound=Base)
def make_obj(cls: Type[T]) -> T:
    inst = cls()  # line 15
    return inst

@gvanrossum
Copy link
Member

gvanrossum commented Sep 22, 2016 via email

@aomader
Copy link

aomader commented Sep 23, 2016

I just stumbled upon this while implementing a generic parser over concrete types of an abstract base class. So, there is an actual real-world use case for this.

Basically, the following should type check flawlessly in my opinion:

import abc
import typing

class Base(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def foo(self) -> None:
        raise NotImplementedError

def Concrete(Base):
    def foo(self) -> None:
        print('bar')

def create(cls: typing.Type[Base]) -> Base:
    return cls()

@gvanrossum
Copy link
Member

Agreed.

--Guido (mobile)

On Sep 23, 2016 7:58 AM, "Oliver Mader" notifications@github.com wrote:

I just stumbled upon this while implementing a generic parser over
concrete types of an abstract base class. So, there is an actual real-world
use case for this.

Basically, the following should type check flawlessly in my opinion:

import abcimport typing
class Base(metaclass=abc.ABCMeta):
@abc.abstractmethod
def foo(self) -> None:
raise NotImplementedError
def Concrete(Base):
def foo(self) -> None:
print('bar')
def create(cls: typing.Type[Base]) -> Base:
return cls()


You are receiving this because you commented.
Reply to this email directly, view it on GitHub
#2169 (comment), or mute
the thread
https://github.com/notifications/unsubscribe-auth/ACwrMhr2uLWSKd3aTMMKsVczdEiCWsaZks5qs-kegaJpZM4KDmd3
.

@elazarg
Copy link
Contributor

elazarg commented Sep 23, 2016

I don't understand why should it typecheck. It looks unsafe to me.

@gvanrossum
Copy link
Member

@elazarg Mypy doesn't need to flag everything that looks unsafe. It must be a useful tool, and some code that is theoretically unsafe is both common and useful in practice and the unsafe scenario never occurs, so complaining about it is unhelpful. In general there is no single concrete subclass of an ABC like Base in the example, so there is no easy way to state "any concrete subclass of Base".

I would much rather have an occasional unsafe piece of code pass without error (a false negative) than require users to add extra markup to code that is almost always fine in practice (a false positive), if the annoyance of false positives outweighs the cost of false negatives. This is one of those cases. This is a different philosophy from most typed languages, and it is Python's runtime type checking that gives us this luxury.

@elazarg
Copy link
Contributor

elazarg commented Sep 24, 2016

I am not sure that in practice unsafe scenarios never occur. It is not unlikely to forget adding an override to a new abstract method, and this is the place where it can be caught.

I understand the reasoning however: it is unsafe, but not yet possible to express in a useful way, so we err towards flexibility, avoiding false positives. Fair enough.

(The reason I say "yet" relates to my pending proposal in python-ideas. An easy and clear way to write it would be

def create(cls: typing.Type[Base] and cls(...) ) -> Base:
    return cls()

It is easily understood but unfortunately not reasonable to use as things currently stand)

@gvanrossum
Copy link
Member

This is a dup of #1843.

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

5 participants