Well that seems super unfortunate. You can opt out of the auto generate constructor and do it yourself:

  @dataclass(init=False)
  class Foo:
      foo: str
      bar: str = None
      baz: str

      def __init__(self, *, foo, bar = None, baz):
          self.foo = foo
          self.bar = bar
          self.baz = baz
    

  Foo(foo='a', bar='b', baz='c')

but this seems to take away from the utility of dataclasses. One could imagine there being a new argument to @dataclass that would make this work. Something like:


@dataclass(init_kwargs_only=True)
  class Foo:
      foo: str
      bar: str = None
      baz: str

where you would then get an auto generated constructor like with keyword only arguments. Personally I think this should have been the default, but it's at least a nice addition now.

/ Anders


On 24 Oct 2018, at 05:13, Philip Martin <philip.martin2007@gmail.com> wrote:

Hi, I just started to use the new dataclasses module. My initial use case boils down to somewhere between a namedtuple and a class where I want a record with a few methods.

Mainly, I am trying to build a specific record from various sources, and then have the class handle validation and serialization. One roadblock I have found is that I can't use the field order I define for the class to say write out a csv file if any of the fields have default value. I know this bucks Python's args, kwargs ordering, but I think having the ability define optional arguments and required arguments in any order helps improve the classes intended usability. For instance, imagine "account_description_2" is None for most sources, but must appear before "other_required_field" in a CSV exported file. I think it would be useful to be able to do the following:

import csv
from datetime import date
from dataclasses import dataclass, fields
from typing import List

OBJECT_SERIALIZERS = {date: date.isoformat}

@dataclass
class Account:
    account_id: str
    account_description: str
    account_description_2: str = None

    # INVALID
    other_required_field: str

    def serialize(self):
        for field in fields(self):
            value = getattr(self, field.name)
            serializer = OBJECT_SERIALIZERS.get(field.type, None)

            if serializer:
                value = serializer(value)
            yield value

    @property
    def field_names(self):
        return [field.name for field in fields(self)]

    @classmethod
    def from_source_a(cls, record):
        return cls(account_id=record['account_code'],
                   account_description=record['account_name'],
                   other_required_field=record['other_field'])

@dataclass
class AccountList:
    accounts: List[Account]

    @property
    def field_names(self):
        return [
            field.name for field in fields(fields(self)[0].type.__args__[0])
        ]

    @property
    def record_field(self):
        return fields(self)[0].name

    def to_csv(self, path):
        with open(path, 'w') as file:
            self.write_file(file)

    def write_file(self, file):
        records_field = self.record_field

        writer = csv.writer(file)
        writer.writerow(self.field_names)
        writer.writerows(
            record.serialize() for record in getattr(self, records_field)
        )

_______________________________________________
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/