Protocols can be used. I'm documenting it here because I found this to be a difficult topic to search for; especially checking for the existence of attributes.
For ensuring the presence of an attribute:
from typing import Protocol
class HasFoo(Protocol): # Define what's required
foo: int
class Foo: # This class fulfills the protocol implicitly
def __init__(self):
self.foo = 1
class Bar:
def __init__(self): # This class fails to implicitly fulfill the protocol
self.bar = 2
def foo_func(f: HasFoo):
pass
foo_func(Foo()) # Type check succeeds
foo_func(Bar()) # Type check fails
Note the type hint after foo
. It's required for that line to be syntactically valid, and the type must match the inferred type of the checked attributes. typing.Any
can be used as a placeholder if you care about the existence of foo
, but not its type.
Similarly, the same can be done for checking methods:
class HasFoo(Protocol):
def foo(self):
pass
class Foo:
def foo(self):
pass
class Bar:
def bar(self):
pass
def func(f: HasFoo):
pass
func(Foo()) # Succeeds
func(Bar()) # Fails
Type checking was done via Pycharm 2020.2.2
.