Skip to main content

Less verbose dataclasses

Project description

Module dataclass_baseclass

DataClass - inheritable contagious base class.

Instead of (endless?) @dataclass decorating.

Usage

class A(DataClass):  # it's a dataclass

class B(A):  # it's a dataclass too

as opposed to:

@dataclass
class A(): ...  # it's a dataclass

class B(A): ...  # it's *not* a dataclass, needs decorating

@dataclass
class B(A): ...  # now it's a dataclass

Also:

class B(DataClass, A): ...  # all attributes from A become fields

as opposed to:

class A():  ...

@dataclass
class B(A): ...  # no attributes from A are fields

Instantiation

class C(DataClass):
    a: str
    b: str

defaults: Data = {"a": "A", "b": "B"}

c = C(defaults, a="a", b="b")

or just:

c = C(a="a", b="b")

It kind of supports freezing/unfreezing on the fly, but it is besto keep all classes in the chain either frozen or not.

frozen attribute is dominant, ie as soon as you have one frozen parent class in the mix, class becomes frozen. If you want it unfrozen you need to specify dataclass_params.frozen as False:

class Unfrozen(Frozen, dataclass_params={"frozen": False}

Gotchas and features

Turning field into property in subclass

This does not work:

class C(DataClass):
    s: str

class CC(C):
    @property
    def s(self) -> str:
        return "S"

One way to fix:

class C(DataClass):
    s: str = DataClass._field(init=False)

class CC(C): ...

Loaders

Tested with following dataclass loaders:

dataclasses-json

Works with DataClassJsonMixin and from_dict() (actually _decode_dataclass()). Unfortunately we turn dataclass_json_config into an attribute.

I could not get it to work with @dataclass_json decorator, probably did not try hard enough.

Documentation and examples

Documentation

Tests should give a good idea of how to use it.

Test report

Name                     Stmts   Miss  Cover
--------------------------------------------
dataclass_baseclass.py     144      0   100%
--------------------------------------------
TOTAL                      144      0   100%

Notes / FAQ

And Pydantic?

Pydantic is OK if you want to enter that world, stay there and comply. Some limitations with inheritance:

Straight multiple inheritance

class A(BaseModel): ...

class B(BaseModel): ...

class C(A, B): ...

The official stance on this (at least what I could figure out at the time of writing) is:

It will probably work, but not guaranteed, not officially supported

It could be argued, of course, that multiple inheritance is an anti-pattern and it is good that it is not supported. I have no strong opinion on that. But:

Protocols (or mix-ins, or whatever)

class A(BaseModel): ...

class P(Protocol): ...

class C(A, P): ...

That is a no-go.

With DataClass, we aim to enable all that.

Why not from scratch, why wrapping dataclasses?

Considering the effort that was put into dataclasses my conclusion is that dataclasses is the recommended way to standardise directly accessible class/instance properties in the standard library.

Has this been tested in real life?

I am using it in my personal (hobby?) projects. But nothing of a decent size in business environment.

A rant

Metaclasses. A quote from the official docs:

The potential uses for metaclasses are boundless. Some ideas that have been explored include enum, logging, interface checking, automatic delegation, automatic property creation, proxies, frameworks, and automatic resource locking/synchronization.

One could be easily forgiven to think that creating custom metaclasses is a valid thing to do, at least not discouraged. Some official examples of how to roll out your own metaclass, how to subclass type? I could not find it. type (meta)class is implementyed in C, and it is not light reading. Quite frustrating if one is after "what methods are available for overriding and what are their footprints".

One is condemned to trawling the internet, which comes up with the venerable "Let's make a singleton" example in 99.98% of the cases. That gives you a clue that you need to override __new__(), which has the same footprint as type(). Then you look in some corners of the internet, or much better ask ChatGPT, which gives you a hint that you could also play with __call__() method.

To sum it up, a laborious process. Why not documenting some examples and make life a tad easier...

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dataclass_baseclass-0.1.0.tar.gz (25.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dataclass_baseclass-0.1.0-py3-none-any.whl (11.3 kB view details)

Uploaded Python 3

File details

Details for the file dataclass_baseclass-0.1.0.tar.gz.

File metadata

  • Download URL: dataclass_baseclass-0.1.0.tar.gz
  • Upload date:
  • Size: 25.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.1

File hashes

Hashes for dataclass_baseclass-0.1.0.tar.gz
Algorithm Hash digest
SHA256 ddbcd42c13e74ab54a3a877145dac8d39b7fce699693863224ebef2389a957d1
MD5 7d4854f1a8f47ff4593a65e0dd36e02a
BLAKE2b-256 9ec2884ef325c42293ec427f908b8c2b8d25b49f0889e3086938ac96aca5b89e

See more details on using hashes here.

File details

Details for the file dataclass_baseclass-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for dataclass_baseclass-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 47942866d041256a98dcf49540dc5b6dca018bc9c8d5de01f8ae3b8e8f33836e
MD5 5beca29d3fedda83c4774f6a01270112
BLAKE2b-256 c62672fb1448044bf1493aa26d02daa30eacc4272835dfe65d33743c46e7b8c0

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page