Skip to content

SubType and SubInstance #2169

Closed
Closed

Description

@sirkonst

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?

Activity

  1. sirkonst commented on Sep 22, 2016

    @sirkonst
    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"
    
  2. refi64 commented on Sep 22, 2016

    @refi64
    Contributor

    What if you do:

    T = TypeVar('T', bound=Base)
    def make_obj(cls: Type[T]) -> T:
        inst = cls()  # line 15
        return inst
  3. gvanrossum commented on Sep 22, 2016

    @gvanrossum
    Member
  4. aomader commented on Sep 23, 2016

    @aomader

    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()
  5. gvanrossum commented on Sep 23, 2016

    @gvanrossum
    Member

    Agreed.

    --Guido (mobile)

    On Sep 23, 2016 7:58 AM, "Oliver Mader" [email protected] 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
    .

  6. elazarg commented on Sep 23, 2016

    @elazarg
    Contributor

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

  7. gvanrossum commented on Sep 23, 2016

    @gvanrossum
    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.

  8. elazarg commented on Sep 24, 2016

    @elazarg
    Contributor

    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)

  9. gvanrossum commented on Sep 24, 2016

    @gvanrossum
    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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions