|
def make_dataclass(cls_name, fields, *, bases=(), namespace=None, init=True, |
|
repr=True, eq=True, order=False, unsafe_hash=False, |
|
frozen=False, match_args=True, kw_only=False, slots=False, |
|
weakref_slot=False, module=None): |
|
"""Return a new dynamically created dataclass. |
|
|
|
The dataclass name will be 'cls_name'. 'fields' is an iterable |
|
of either (name), (name, type) or (name, type, Field) objects. If type is |
|
omitted, use the string 'typing.Any'. Field objects are created by |
|
the equivalent of calling 'field(name, type [, Field-info])'.:: |
|
|
|
C = make_dataclass('C', ['x', ('y', int), ('z', int, field(init=False))], bases=(Base,)) |
|
|
|
is equivalent to:: |
|
|
|
@dataclass |
|
class C(Base): |
|
x: 'typing.Any' |
|
y: int |
|
z: int = field(init=False) |
|
|
|
For the bases and namespace parameters, see the builtin type() function. |
|
|
|
The parameters init, repr, eq, order, unsafe_hash, frozen, match_args, kw_only, |
|
slots, and weakref_slot are passed to dataclass(). |
|
|
|
If module parameter is defined, the '__module__' attribute of the dataclass is |
|
set to that value. |
|
""" |
|
|
|
if namespace is None: |
|
namespace = {} |
|
|
|
# While we're looking through the field names, validate that they |
|
# are identifiers, are not keywords, and not duplicates. |
|
seen = set() |
|
annotations = {} |
|
defaults = {} |
|
for item in fields: |
|
if isinstance(item, str): |
|
name = item |
|
tp = 'typing.Any' |
|
elif len(item) == 2: |
|
name, tp, = item |
|
elif len(item) == 3: |
|
name, tp, spec = item |
|
defaults[name] = spec |
|
else: |
|
raise TypeError(f'Invalid field: {item!r}') |
|
|
|
if not isinstance(name, str) or not name.isidentifier(): |
|
raise TypeError(f'Field names must be valid identifiers: {name!r}') |
|
if keyword.iskeyword(name): |
|
raise TypeError(f'Field names must not be keywords: {name!r}') |
|
if name in seen: |
|
raise TypeError(f'Field name duplicated: {name!r}') |
|
|
|
seen.add(name) |
|
annotations[name] = tp |
|
|
|
# Update 'ns' with the user-supplied namespace plus our calculated values. |
|
def exec_body_callback(ns): |
|
ns.update(namespace) |
|
ns.update(defaults) |
|
ns['__annotations__'] = annotations |
|
|
|
# We use `types.new_class()` instead of simply `type()` to allow dynamic creation |
|
# of generic dataclasses. |
|
cls = types.new_class(cls_name, bases, {}, exec_body_callback) |
|
|
|
# For pickling to work, the __module__ variable needs to be set to the frame |
|
# where the dataclass is created. |
|
if module is None: |
|
try: |
|
module = sys._getframemodulename(1) or '__main__' |
|
except AttributeError: |
|
try: |
|
module = sys._getframe(1).f_globals.get('__name__', '__main__') |
|
except (AttributeError, ValueError): |
|
pass |
|
if module is not None: |
|
cls.__module__ = module |
|
|
|
# Apply the normal decorator. |
|
return dataclass(cls, init=init, repr=repr, eq=eq, order=order, |
|
unsafe_hash=unsafe_hash, frozen=frozen, |
|
match_args=match_args, kw_only=kw_only, slots=slots, |
|
weakref_slot=weakref_slot) |
Feature or enhancement
Proposal:
typing.dataclass_transform(PEP 681 – Data Class Transforms) allows users define their owndataclassdecorator that can be recognized by the type checker.Here is a real-world example use case:
flax.struct.dataclassAlso,
dataclasses.asdictanddataclasses.astupleallow users pass an extra argument for the factory of the returned instance.cpython/Lib/dataclasses.py
Lines 1299 to 1317 in 0fb18b0
cpython/Lib/dataclasses.py
Lines 1380 to 1397 in 0fb18b0
However, the
make_dataclassfunction does not support third-partydataclassfactory (e.g.,flax.struct.dataclass):cpython/Lib/dataclasses.py
Lines 1441 to 1528 in 0fb18b0
It can only apply
dataclasses.dataclass(see thereturnstatement above).This feature request issue will discuss the possibility of adding a new
dataclass_factoryargument to thedataclasses.make_dataclassto support third-party dataclasss transformation, similar todict_factoryfordataclasses.asdict.Has this already been discussed elsewhere?
https://discuss.python.org/t/add-dataclass-factory-argument-to-dataclasses-make-dataclass-for-custom-dataclass-transformation-support/53188
Links to previous discussion of this feature:
No response
Linked PRs
decoratorargument tomake_dataclass#122723