Use isinstance only with runtime-checkable Protocols

Use isinstance only with runtime-checkable Protocols

A Protocol normally helps static type checkers describe an interface without requiring inheritance. It is not automatically a valid second argument to isinstance. Add @runtime_checkable when the program genuinely needs a runtime structural check, such as accepting an object supplied by a plug-in boundary. This example requires both a label attribute and a render() method. Ticket conforms without inheriting from LabeledRenderable, while a plain object does not.

The output is produced only after assertions confirm those two outcomes. The check is deliberately narrow: it establishes attribute presence, not that label is a str or that render() has the annotated signature. Code that needs those guarantees must validate values separately, or call the method and handle its documented failures. Runtime protocol checks can also be slower than ordinary class checks, so they are not a substitute for a carefully designed hot-path API. runtime_checkable was added in Python 3.8.

See the official typing documentation. AI assistance disclosure: this article was drafted with AI assistance and checked using a synthetic example.

from typing import Protocol, runtime_checkable


@runtime_checkable
class LabeledRenderable(Protocol):
    label: str

    def render(self) -> str: ...


class Ticket:
    label = "ticket"

    def render(self) -> str:
        return f"{self.label}:7"


ticket = Ticket()
assert isinstance(ticket, LabeledRenderable)
assert not isinstance(object(), LabeledRenderable)
print(ticket.render())
ticket:7

Comments

Popular posts from this blog

Compare Two Text Files in Python Without Modifying Them

Seven CSV Quality Checks to Run Before Importing Data

Explain why SQLite transactions cannot make an HTTP call atomic