I have a Protocol
subclass that defines objects with attributes from an external library:
class P(Protocol):
val: int
For testing purposes, I want to turn this protocol class into something I can instantiate easily. However, when I try to turn it into a dataclass, an error pops up:
import dataclasses
from typing import Protocol
class P(Protocol):
val: int
PInst = dataclasses.dataclass(P)
PInst(val=4) # TypeError: Protocols cannot be instantiated
Is there an easy solution to use P
to create a class that satifies its protocol and is instantiable, without redeclaring its attributes?
You are asking for a non-protocol class derived from the attributes in your protocol class. They are stored in the annotations, which are accessible as typing.get_type_hints(P)
.
In my first try, I dynamically created that class with builtin type
, passing it the protocol's type hints to define an equivalent non-protocol class, and pass that to dataclass
. But it needed me to manually set dunder annotations before it could correctly create the init method.
But messing with internal attributes is a red flag telling me there should be an easier way. So I looked at the dataclasses public interface and found make_dataclass
. You can just pass the annotations directly to its fields parameter.
from dataclasses import make_dataclass
from typing import Protocol, get_type_hints
class P(Protocol):
val: int
DP = make_dataclass('DP', get_type_hints(P))
instance = DP(val=4)
print(instance)
output:
DP(val=4)