8

I have a set of classes, Lets call them Foo and Bar, where both inherit from a base class Father that is defined outside of the current scope (not by me). I have definied a protocol class DummyProtocol that has a function do_something.

class DummyProtocol(Protocol):
   def do_something(self):
      ...
   

class Foo(Father):
   def do_something(self):
      pass

class Bar(Father):
   def do_something(self):
      pass

I have a function create_instance.

def create_dummy_and_father_instance(cls, *args, **kwargs):
    return cls(*args, **kwargs)

I want to typehint it in a way, that cls is typehinted to accept a class that is of type Father that also implements the DummyProtocol.

So I changed the function to this to indicate that cls is a type that inherit from both Father and DummyProtocol

def create_dummy_and_father_instance(
    cls: Type[tuple[Father, DummyProtocol]], *args, **kwargs
):
    return cls(*args, **kwargs)

But I get this error in mypy:

Cannot instantiate type "Type[Tuple[Father, DummyProtocol]]"
3
  • stackoverflow.com/a/62661785/14617085 is this kind of what you are looking for ??
    – basic mojo
    Commented Nov 28, 2021 at 6:29
  • @basicmojo I don't think so. I need a typehint that indicates an object is implementing arbitrary numbers of protocols alongside a baseclass in the arguments section. Is changing the class definition my only way?
    – Farhood ET
    Commented Nov 28, 2021 at 6:32
  • ask here, discord.gg/t4gsyP9EQ3, the admin, asotille, is really good at typehinting stuff
    – basic mojo
    Commented Nov 28, 2021 at 6:35

2 Answers 2

5

I came across the same issue and found this discussion on proposed Intersection types which seem to be exactly what is needed (e.g. see this comment).

Unfortunately this feature is not yet supported by the Python typing system, but there's a PEP in the making.

2

You can define a second Father class which inherits from Father and Protocol (see also mypy: how to verify a type has multiple super classes):

class DummyProtocol(Protocol):

    def do_something(self):
        ...
    
class Father:
    pass
    
class Father2(Father, DummyProtocol):
    pass
    
class Foo(Father2):

    def do_something(self):
        pass
    
class Bar(Father2):

    def do_something(self):
        pass
    
class FooNot(Father):
    pass
    
def create_dummy_and_father_instance(
    cls: Type[Father2]
):
    return cls()
    
create_dummy_and_father_instance(Foo)
create_dummy_and_father_instance(Bar)
create_dummy_and_father_instance(FooNot)  # mypy error ok
4
  • 3
    But isn't this against the usage of Protocol? Protocol is only about statice type hinting, not run time influence, isn't it?
    – Farhood ET
    Commented Nov 28, 2021 at 12:13
  • 1
    I think not. From the doc: "Note that inheriting from an existing protocol does not automatically turn the subclass into a protocol – it just creates a regular (non-protocol) class or ABC that implements the given protocol (or protocols). The Protocol base class must always be explicitly present if you are defining a protocol."
    – hussic
    Commented Nov 28, 2021 at 14:41
  • so any time I need to make sure something has implemented a base class and a protocol I have to derive a subclass? This is really not optimal.
    – Farhood ET
    Commented Nov 28, 2021 at 15:37
  • No problem with instance, but with class Protocol has some limits.
    – hussic
    Commented Nov 28, 2021 at 16:09

Not the answer you're looking for? Browse other questions tagged or ask your own question.