Search code examples
pythonprotocols

How do I annotate that a python class should implement a Protocol?


If I define a Protocol and a class that implements that protocol, I would like to document that the class does/should implement the protocol so that other developers can easily see that the class is supposed to implement the protocol, and so that static analysis tools can verify that the class implements the protocol. What is the proper way to document that in the class?

Is it okay to subclass the protocol?

Consider the protocol:

from typing import Protocol

class MyProtocol(Protocol):
    def foo() -> None:
        ...

Is it correct to define a class like this?:

from my_protocol import MyProtocol

class MyClass(MyProtocol):
    def __init__(self):
        pass
    
    def foo() -> None:
        pass

I could use an abstract base class, but then I wouldn't be able to support classes that are structural sub types, but not actual derived classes.

I saw it looked like a version of this question was asked here, but it didn't seem to have an actual answer: Correct way to hint that a class is implementing a Protocol?


Solution

  • I think the answer is on PEP-0544 :

    "To explicitly declare that a certain class implements a given protocol, it can be used as a regular base class."

    https://peps.python.org/pep-0544/#explicitly-declaring-implementation

    Personally I do subclass from protocol often because:

    • my IDE is able to automatically complete method signature when I type them (the one defined inside the Protocol)
    • my IDE can rename all method classes if I rename them from the Protocol
    • I can implement deprecated methods (which call another method) in the Protocol class and not into all subclasses:
    class DataGetter(Protocol):
        def get_data(self):
            ... 
        def get(self):
            warn('get method is deprecated use get_data instead', DeprecationWarning)
            return self.get_data()
    
    class Image(DataGetter):
        def get_data(self):
            return "some image data"