-1

I have a simple class which I decorate with @dataclass. This class has few properties, but all of them are optional.

@dataclass
class Sample:
    field_one: int
    field_two: int
    ...

So far so good. I get the fields parameterized in the __init__ function, however, they are mandatory.

I get the following error if I do not specify them when initializing Sample():

TypeError: __init__() missing 2 required positional arguments: 'field_one', 'field_two'

How can I make all the fields of Sample to be optional and default to None, but without having to specify Optional[type] on each property? I am looking for some kind of "apply-to-all-properties" solution.

This way, I could have the following:

sample = Sample()
print(sample.field_one) # Outputs None
Constantin
  • 848
  • 8
  • 23
  • Are you suggesting that you understand you could get this behvaiour using `field_one: Optional[int] = None`, etc... but you want a way to write this without explicitly doing so? – Kraigolas Jan 16 '22 at 21:52
  • Does this answer your question? [How to apply default value to python dataclass field when None was passed?](https://stackoverflow.com/questions/56665298/how-to-apply-default-value-to-python-dataclass-field-when-none-was-passed) – Yevgeniy Kosmak Jan 16 '22 at 21:52
  • Yes @Kraigolas exactly – Constantin Jan 16 '22 at 21:52
  • Side note: it would be cool if `@dataclass` decorator accepted a param for better configuration, something like `default_for_all`. Then you could pass value explicitly for all fields to default to, like `default_for_all=None`, and then work exactly how you'd envision it in this case. The main benefit here of course, is that IDEs understand that when this param is passed in to the decorator, that all fields would be optional, and IDE warnings and hints would get updated accordingly. – rv.kvetch Jan 18 '22 at 15:14

1 Answers1

3

Yes there is a way to do this. However it will mess with your IDE's ability to do autocompletion and wont enforce any constraints that a dataclass is meant to have. So basically overriding alot of dataclass' safety features.

This code will first mark everything as None but will still allow you to pass in kwargs to the init to set as the value.

from dataclasses import dataclass


@dataclass
class Sample:
    field_one: int
    field_two: int

    def __init__(self, **kwargs):
        for k in self.__dataclass_fields__:
            setattr(self, k, None)

        for k, v in kwargs.items():
            setattr(self, k, v)

Sample() # Works, everything is None

Sample(field_one=10) # Works, everything is None except for field_one which is now equal to 10

Dry method for this so you don't have to copy and paste everywhere... Please look at the dataclass decorator and passing init=False

class AllPropertiesAsNone:
    def __init__(self, **kwargs):
        for k in self.__dataclass_fields__:
            setattr(self, k, None)

        for k, v in kwargs.items():
            setattr(self, k, v)


@dataclass(init=False)
class Sample(AllPropertiesAsNone):
    field_one: int
    field_two: int
TeddyBearSuicide
  • 1,377
  • 1
  • 6
  • 11
  • 2
    I think this is bad practice, optional arguments should be specified: `field_one: Optional[int] = None`. However, this answers the question, so while I wouldn't recommend using this, +1. – Kraigolas Jan 16 '22 at 21:59
  • 3
    @Kraigolas, 100% agree with you. It avoids all the type hinting in IDE's and will not catch `None` being passed in to a value that is suppose to have a value. But it does achieve what OP wants. – TeddyBearSuicide Jan 16 '22 at 22:01
  • The solution works, +1. However is there any way to abstract this without having to copy-paste the `__init__` function for each model? – Constantin Jan 16 '22 at 22:02
  • 1
    @Constantin Yes, just make your dataclasses inherit from this. – CrazyChucky Jan 16 '22 at 22:03
  • @Constantin, edited my answer to have both – TeddyBearSuicide Jan 16 '22 at 22:05
  • @TeddyBearSuicide I tried this but it messes up autocomplete indeed, although it solves the issue :( There's no other way around to keep autocompletion aswell, right? – Constantin Jan 16 '22 at 22:07
  • @Constantin, sorry no, if you're using PyCharm it is custom functionality where it sees you decorated the class with `dataclass` so it knows its suppose to have arguments passed in for any property not labeled `Optional`. Once you do this code here, pycharm can't know what the code does exactly and so it disables the autocomplete. – TeddyBearSuicide Jan 16 '22 at 22:09
  • @TeddyBearSuicide Thanks. It's a shame py-developer community never thought of applying defaults in `@dataclass` initializer. – Constantin Jan 16 '22 at 22:12
  • @Constantin, because of how python works this is how it has to be, however you can define defaults to properties but just not to ALL properties with one value. I'm actually not aware of any language that has that feature. You have to set the default for each property in the class object either using type hinting or fields/factories. – TeddyBearSuicide Jan 16 '22 at 22:16
  • @Constantin, Also if this has answered your question, please mark my answer as the accepted answer so it is no long in the unanswered questions list. – TeddyBearSuicide Jan 16 '22 at 22:29