# dataclasses

### `dataclasses.dataclass`

A decorator that is used to add special method to classes, including `__init__`, `__repr__` and `__eq__`.

### `dataclasses.asdict(obj) -> dict`

Convert a dataclass instance to a dictionary.

#### Source code

    def _get_annotations(cls: type):
        inherits = []
        while cls is not object:
            inherits.append(cls)
            cls = cls.__base__
        inherits.reverse()
        res = {}
        for cls in inherits:
            res.update(cls.__annotations__)
        return res.keys()

    def _wrapped__init__(self, *args, **kwargs):
        cls = type(self)
        cls_d = cls.__dict__
        fields = _get_annotations(cls)
        i = 0   # index into args
        for field in fields:
            if field in kwargs:
                setattr(self, field, kwargs.pop(field))
            else:
                if i < len(args):
                    setattr(self, field, args[i])
                    i += 1
                elif field in cls_d:    # has default value
                    setattr(self, field, cls_d[field])
                else:
                    raise TypeError(f"{cls.__name__} missing required argument {field!r}")
        if len(args) > i:
            raise TypeError(f"{cls.__name__} takes {len(fields)} positional arguments but {len(args)} were given")
        if len(kwargs) > 0:
            raise TypeError(f"{cls.__name__} got an unexpected keyword argument {next(iter(kwargs))!r}")

    def _wrapped__repr__(self):
        fields = _get_annotations(type(self))
        obj_d = self.__dict__
        args: list = [f"{field}={obj_d[field]!r}" for field in fields]
        return f"{type(self).__name__}({', '.join(args)})"

    def _wrapped__eq__(self, other):
        if type(self) is not type(other):
            return False
        fields = _get_annotations(type(self))
        for field in fields:
            if getattr(self, field) != getattr(other, field):
                return False
        return True

    def _wrapped__ne__(self, other):
        return not self.__eq__(other)

    def dataclass(cls: type):
        assert type(cls) is type
        cls_d = cls.__dict__
        if '__init__' not in cls_d:
            cls.__init__ = _wrapped__init__
        if '__repr__' not in cls_d:
            cls.__repr__ = _wrapped__repr__
        if '__eq__' not in cls_d:
            cls.__eq__ = _wrapped__eq__
        if '__ne__' not in cls_d:
            cls.__ne__ = _wrapped__ne__
        fields = _get_annotations(cls)
        has_default = False
        for field in fields:
            if field in cls_d:
                has_default = True
            else:
                if has_default:
                    raise TypeError(f"non-default argument {field!r} follows default argument")
        return cls

    def asdict(obj) -> dict:
        fields = _get_annotations(type(obj))
        obj_d = obj.__dict__
        return {field: obj_d[field] for field in fields}