3
from dataclasses import dataclass

@dataclass
class CampingEquipment:
    knife: bool
    fork: bool
    missing_flask_size: ? # removed field() per answers below.
    
kennys_stuff = {
    'knife':True,
    'fork': True
}

print(CampingEquipment(**kennys_stuff))

gives me:

TypeError: CampingEquipment.__init__() missing 1 required positional argument: 'missing_flask_size'

How do you make Optional attr's of a dataclass?

Edit

What I meant by optional wasn't clear. By Optional I mean __dict__ may contain the key "missing_flask_size" or not. If I set a default value then the key will be there and it shouldn't be in some cases. I want to check it's type if it is there. I tried moving the field(init=False) to the type location (after the colon) so I could make it more explicit as to the thing I wanted optional would be the key and not the value. That was just confusing for everyone, including me, so it's removed.

So I want this test to pass:

with pytest.raises(AttributeError):
    ce = CampingEquipment(**kennys_stuff)
    print(ce.missing_flask_size)
Back2Basics
  • 6,864
  • 1
  • 32
  • 42
  • 1
    The question will be revised. What I meant by optional didn't come through. Yes I read through the docs several times and tried other invocations besides this @rv.kvetch – Back2Basics Jan 23 '22 at 03:24
  • 1
    Got it, so it looks like a current answer already addresses this issue, in that case. Also, wondering what’s the type of the optional field? If user were to pass it in to consutector I mean. – rv.kvetch Jan 23 '22 at 12:52
  • There is a lot of good information but the existing answers don't look like they answer the question. It could be that a data class is more like sql which have column names for everything in the table or not. – Back2Basics Jan 24 '22 at 07:50

2 Answers2

3

A field object is supposed to be used with =, like a default value, not : like an annotation.

Specifying init=False for a field means the caller can't pass in a value for it at all. init=False fields are supposed to be set in __post_init__, like this:

@dataclass
class Example:
    a: int
    b: int
    c: int = field(init=False)

    def __post_init__(self):
        self.c = self.a + self.b

print(Example(1, 2).c) # prints 3

If you want to make it optional for the caller to provide a value, you can set a default value. If the caller doesn't provide a value, the default will be used instead:

@dataclass
class Example:
    a: int
    b: int
    c: int = -1

print(Example(1, 2).c) # prints -1
print(Example(1, 2, 3).c) # prints 3
user2357112
  • 235,058
  • 25
  • 372
  • 444
  • 1
    I'd also mention `field(default=...)` and in the case of mutable types, `field(default_factory=...)`, additionally as it's unclear what the type of the last field in the OP was. – rv.kvetch Jan 22 '22 at 05:26
-1

Just add a value to optional field

@dataclass
class CampingEquipment:
   knife: bool
   fork: bool
   missing_flask_size = None

# CampingEquipment(knife=True, fork=True, missing_flask_size=None)

See this post too How to create an optional field in a dataclass that is inherited?

Franz Kurt
  • 645
  • 2
  • 10
  • 11
  • 1
    That's not how `field` is supposed to be used. – user2357112 Jan 22 '22 at 02:17
  • But after edit, it’s still not right. Because dataclasses relies on type annotations to determine fields, so right now you are defining a class (static) variable, not a dataclass field. – rv.kvetch Jan 23 '22 at 12:50