-
Notifications
You must be signed in to change notification settings - Fork 0
/
meta_dataclass.py
67 lines (56 loc) · 2.47 KB
/
meta_dataclass.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
from functools import reduce
import itertools
class dataclass(type):
def __new__(cls, name, bases, dict):
annots = cls.parse_annotations(bases, dict)
init = cls.create_init(annots)
repr = cls.create_repr(annots)
eq = cls.create_eq(annots)
it = cls.create_iter(annots)
hsh = cls.create_hash(annots)
return super().__new__(cls, name, bases, {
**dict, '__init__': init, '__repr__': repr, '__eq__': eq,
'__hash__': hsh, '__iter__': it,
})
@classmethod
def create_function(cls, code_str):
exec(code_str, globals(), d:={})
return d.popitem()[1]
@classmethod
def create_init(cls, annots):
args = "self" + "".join([f", {i}" for i in annots])
bodylines = "".join([f" self.{i} = {i}\n" for i in annots])
code_str = f"def __init__({args}):\n{bodylines}"
return cls.create_function(code_str)
@classmethod
def create_repr(cls, annots):
repr_args = ", ".join([f"{i}={{repr(self.{i})}}" for i in annots])
bodylines = f"return f\"{{type(self).__name__}}({repr_args})\""
code_str = f"def __repr__(self):\n {bodylines}"
return cls.create_function(code_str)
@classmethod
def create_eq(cls, annots):
own_vals = ",".join(f"self.{a}" for a in annots)
other_vals = ",".join(f"other.{a}" for a in annots)
code_str = f"def __eq__(self, other):\n if self.__class__ is other.__class__:\n" \
f" return ({own_vals},) == ({other_vals},)\n return False"
return cls.create_function(code_str)
@classmethod
def create_hash(cls, annots):
own_vals = ", ".join(f"self.{a}" for a in annots)
code_str = f"def __hash__(self):\n return hash(({own_vals},))"
return cls.create_function(code_str)
@classmethod
def create_iter(cls, annots):
own_vals = ", ".join(f"self.{a}" for a in annots)
code_str = f"def __iter__(self):\n return (i for i in ({own_vals},))"
return cls.create_function(code_str)
@classmethod
def parse_annotations(cls, bases, dict):
reversed_mro = itertools.chain.from_iterable([b.__mro__[-2::-1] for b in bases[::-1]])
annotations_dict = reduce(lambda x, y: x | getattr(y, '__annotations__', {}), reversed_mro, {}) | dict['__annotations__']
return annotations_dict
if __name__ == '__main__':
class Name(metaclass=dataclass):
first: str
last: str