1

I would like to use a dataclass with an invariant that should not change during the lifetime of such instantiated objects. To indicate that the instance variables of these objects are private, their names are prefixed with an underscore. These instance variables can easily be accessed with the use of properties, as demonstrated with the example code below:

from dataclasses import dataclass

@dataclass
class C():
  _x: int = 3

  @property
  def x(self) -> int:
    return self._x

  def p(self) -> None:
    print(self._x)

The problem arises when I want to call the constructor of this class with explicit keyword arguments. To do so, I now have to provide the names of the instance variables with an underscore as well. This seems really counterintuitive, since the private variables are now accessed from outside of the class.

a = C()      # sets 'a.x' to 3
a.p()        # prints 3

b = C(5)     # sets 'b.x' to 5
b.p()        # prints 5

c = C(_x=7)  # sets 'c.x' to 7
c = C(x=7)   # error: unexpected keyword argument 'x'

One way to solve this problem, is to simply provide an explicit constructor with matching arguments:

def __init__(self, x: int = 3) -> None:
  self._x = x

However, this also seems to be dreadfully counterintuitive as this approach contradicts the whole notion of a dataclass. Is there a way to use a dataclass in combination with properties that allows me to use explicit keyword arguments when constructing such objects without having to access/acknowledge instance variables intended to be private?

303
  • 2,417
  • 1
  • 11
  • 25
  • [Does this help?](https://stackoverflow.com/questions/2627002/whats-the-pythonic-way-to-use-getters-and-setters) Especially the part about property setters. – matszwecja Mar 30 '22 at 14:00
  • @matszwecja Unfortunately not, as this problem is specific to utilizing *properties* with *dataclasses*. – 303 Mar 30 '22 at 16:50

1 Answers1

1

The dataclass is essentially a handful of methods that you can attach to your class. These methods provide reusable logic that dataclass developers thought applies to certain use cases. Setting private fields via __init__ arguments is not among these use cases, so what you want is not supported by the dataclass module.

Luckily, it appears someone else has written a different module that does cover this use case: https://pypi.org/project/dataclass-property/

You could also look at some alternative frameworks, such as pydantic, to see if they meet your needs better.

Jessica
  • 191
  • 5