Closed Valentin-Miroshnichenko closed 2 years ago
I don't think there's a general solution, because multimethod
relies on issubclass
and Protocol
explicitly forbids using issubclass
with data protocols.
One workaround would be to use overload
, which supports isinstance
.
In []: @runtime_checkable
...: class Closable(Protocol):
...: def close(self): ...
...: @property
...: def name(self): ...
...:
In []: isinstance(f, Closable)
Out[]: True
In []: issubclass(type(f), Closable)
TypeError: Protocols with non-method members don't support issubclass()
In []: @overload
...: def func(arg: Closable): ...
In []: func(f)
@Valentin-Miroshnichenko Just for completeness, I solved this by using formal interfaces with abc.ABCMeta
as metaclass. Instead of writing your Closable
as a Protocol
, you could instead write it like this:
import abc
class Closable(metaclass=abc.ABCMeta):
@classmethod
def __subclasshook__(cls, subcls):
return hasattr(subcls, "close") and callable(subcls.close) \
and hasattr(subcls, "name") and type(subcls.name) == property
It's a lil cumbersome, but it gets the work done. If you have many Protocols, you surely can code a decorator that transforms them to interfaces of this kind. Furthermore, you can explicitly register a class using Closable.register(MyClass)
.
I'm trying to use @multimethod in combination with Protocol. But at runtime I get this error
How can I fix this error? If I remove the property cancellation_token, then everything works fine. But I need a property cancellation_token.