Dataclasses - Sentinel to Stop creating "Field" instances

I feel like all the discussion over using with blocks or @ in type hints is tangential to actually getting something like what the goal was in the beginning. What I originally intended as a short comment on not liking what you needed to do to implement the with block ballooned into a more off-topic discussion on internals and language features.

I think any further discussion on either form would belong in a new thread as they are now broader in scope than the original topic.


As far as I can tell the best statement of the goals given by the thread creator was this:

So I’ll approach this piece by piece:

Not part of __init__ or __post_init__

‘Declaring’ an attribute that is ignored by __init__ and isn’t a ClassVar doesn’t make much sense to me - considering the original PEP that introduced variable annotations specifically notes:

In particular, the value-less notation a: int allows one to annotate instance variables that should be initialized in __init__ or __new__ .

So I would expect any attributes declared as instance variables in this way to be initialized in the process of __init__ (including what happens in __post_init__ for dataclasses) and, as such, be accessible on an instance as soon as it has been created.

Not having to use field(...)

Outside of the exceptions of ClassVar and InitVar, field(...) is the way dataclasses knows how to handle declared attributes.

Additional discussion of `ClassVar` and `InitVar` meanings

ClassVar predates dataclasses and indicates that an attribute belongs to the class, rather than an instance and as such should be ignored by the dataclass machinery.

InitVar appears to be a related instruction, it also should not be defined on instances[1], but is an instruction to dataclasses to add an additional parameter to __init__ to be provided to __post_init__ in order to allow additional information to be given to a class that does not need to be kept.

In neither case is the value intended to be stored as an instance variable so they are not ‘fields’ in this way.

A change to the way dataclasses works to use additional type hints or with statements as an alternative to field(...) declarations is more disruptive and likely to need support from at least some core developers along with needing a PEP, as was requested for an earlier proposal to use Annotated.

Not present in fields

In theory you could have a Field attribute that causes dataclasses to discard the value from its internal list of fields after generation. I’m not sure it’s ideal to not keep the information about how the class is generated around though.

Not present in asdict and astuple

This, I think is the feature most likely to have a chance of being accepted. Currently you can exclude a field from __init__, __repr__, __eq__ and __hash__ but not from the included asdict and astuple function output.

It might be worth noting that the cattrs library will already exclude init=False fields from serialization to dicts with its unstructure method.

from dataclasses import dataclass, field, asdict
from cattrs import unstructure

@dataclass
class Foo:
    bar: int = field(default=42, init=False)

print(asdict(Foo()))
print(unstructure(Foo()))

Output:

{'x': 12}
{}

[Edited to add an example of cattrs]


  1. No slot is created if slots=True is used, and the example in the docs does not store the value. ↩︎