I'm writing a library that works a lot like dataclass
but with some additional functionality. Obviously I want to reuse dataclass
as much as possible, but for my added functionality, I'd like to extend dataclass.Field
. That is, I'd like to define:
class MyField(dataclasses.Field):
... # additional functionality
And then do something so that when the user does:
@mydataclass
class UserDefinedClass:
foo: int
y: Bar
(Where mydataclass
is a decorator that eventually calls dataclass
...)
The actual field objects inside UserDefinedClass
will be of type MyField
rather than dataclasses.Field
.
I can't find anything in the dataclass docs that allows me to do this, but I think I might be missing something.
(I am aware of the metadata
keyword argument to field
, and I will probably use that if the answer to this question is "that's impossible". But it'll be more work.)
It certainly doesn't exactly look as though dataclasses.Field
is supposed to be subclassed, and convincing the dataclass
decorator to use a custom field is, well, a bit grotesque, but this works – even if I'd advise against this.
It would certainly be nicer if you could tell dataclass()
to use a custom field
class, but alas...
import dataclasses
from unittest import mock
def mydataclass(**kwargs):
def deco(cls):
with mock.patch("dataclasses.Field", MyField): # NB: not thread-safe
return dataclasses.dataclass(**kwargs)(cls)
return deco
class MyField(dataclasses.Field):
def honk(self):
print(
f"You hear {self.name!r} the {self.type.__name__} emitting a loud HONK!"
)
@mydataclass()
class MyData:
a: int
b: str
d = MyData(1, "hello")
for f in dataclasses.fields(d):
f.honk()
This prints out
You hear 'a' the int emitting a loud HONK!
You hear 'b' the str emitting a loud HONK!
as you might imagine.