6

I am trying to write a function to log dataclasses I would like to get the name of all fields in the dataclass and print the value to each (similar to how you might write a function to print a dictionary)

i.e.

@dataclasses.dataclass
class Test:
    a: str = "a value"
    b: str = "b value"


test = Test()
def print_data_class(dataclass_instance):
   fields = # get dataclass fileds
   for field in fields:
       print(f{field}: {dataclass.field})
print_data_class(test)

-->
"a"  : "a value"
"b"  : "b value"

However I haven't been able to find how to get the fields of a dataclass, does anyone know how this could be done?

Thanks

Pioneer_11
  • 670
  • 4
  • 19

2 Answers2

11

This example shows only a name, type and value, however, __dataclass_fields__ is a dict of Field objects, each containing information such as name, type, default value, etc.

Using dataclasses.fields()

Using dataclasses.fields() you can access fields you defined in your dataclass.

fields = dataclasses.fields(dataclass_instance)

Using inspect.getmembers()

Using inspect.getmembers() you can access all fields in your dataclass.

members = inspect.getmembers(type(dataclass_instance))
fields = list(list(filter(lambda x: x[0] == '__dataclass_fields__', members))[0][1].values())

Complete code solution

import dataclasses
import inspect


@dataclasses.dataclass
class Test:
    a: str = "a value"
    b: str = "b value"


def print_data_class(dataclass_instance):

    # option 1: fields
    fields = dataclasses.fields(dataclass_instance)

    # option 2: inspect
    members = inspect.getmembers(type(dataclass_instance))
    fields = list(list(filter(lambda x: x[0] == '__dataclass_fields__', members))[0][1].values())

    for v in fields:
      print(f'{v.name}: ({v.type.__name__}) = {getattr(dataclass_instance, k)}')


print_data_class(Test())
# a: (str) = a value
# b: (str) = b value

print_data_class(Test(a="1", b="2"))
# a: (str) = 1
# b: (str) = 2
CrazyChucky
  • 3,263
  • 4
  • 11
  • 25
K.Mat
  • 1,341
  • 11
  • 17
  • That unholy string of filter/lambda/indices seems unnecessary when you've already demonstrated the nice, clean method that's available. Is there any kind of advantage to it? – CrazyChucky Jul 28 '22 at 10:19
  • @CrazyChucky the only difference is that `inspect` gives you access to more information other than these fields you defined (e.g. `__annotations__`, `__dataclass_params__`, `__doc__`, etc.) – K.Mat Jul 28 '22 at 10:21
2

use the __dict__ property

def print_data_class(dataclass_instance):
   fields = [(attribute, value) for attribute, value in dataclass_instance.__dict__.items()]
   for field in fields:
       print("{}: {}".format(*field))
airdas
  • 872
  • 2
  • 11
  • 19
Dean Van Greunen
  • 5,060
  • 2
  • 14
  • 28
  • 1
    I think you may have meant ```dataclass_instance.__dict__.items()``` and ```print("f{field[0]}: {field[1]}"``` but other than that works great thanks – Pioneer_11 Jul 28 '22 at 09:53
  • 1
    I had assumed that as dataclasses aren't normal classes that wouldn't work – Pioneer_11 Jul 28 '22 at 09:54
  • 1
    Note that this will also get other attributes set at runtime, not just fields defined in the dataclass. – CrazyChucky Jul 28 '22 at 10:10