Is it possible to see what python's annotations expand to? E.g with dataclass related bugs, sometimes it's a bit tricky trying to figure out what code it actually generates.
E.g:
@dataclass(order = True)
class Person:
name: str
age:int = 0
class Person:
def __init__(self, name:str, age=0):
self.name = name
self.age = age
def __repr__...
def __eq__(self, other):
return (self.name, self.age) == ( other.name, other.age)
Or if not possible, how do you normally figure out what they expand to other than by inspecting the annotation source code?
E.g Racket has a powerfull mechanism to expand macros, does python have something equivalent?
It's not possible to show the code as you want it I'm afraid, since dataclass decoration is a runtime change. The best you can do is using inspect.getsource, which will show the original class, unfortunately without the dataclass changes:
>>> inspect.getsource(Person)
class Person:
name: str
age:int = 0
But while the answer to your question is "It's not possible", maybe the following can help you. Given Person
which is your dataclass, and SimplePerson
which is exactly the same as Person
without the dataclass decoration:
dir - get an object's attributes (including methods) during runtime
>>> dir(SimplePerson)
['__annotations__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__',
'__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__',
'__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__',
'__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__', 'age']
# looks just like a base object, plus 'age', which was not only declared, but also set
>>> dir(Person)
['__annotations__', '__class__', '__dataclass_fields__', '__dataclass_params__',
'__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__',
'__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__',
'__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__',
'__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__',
'age']
# has some more attributes and methods that the dataclass decoration added
dis.dis - disassemble an object's methods into bytecode
>>> import dis
>>> dis.dis(SimplePerson)
# no output, there are no methods on an object that only has two class attributes
>>> import dis
>>> dis.dis(Person)
Disassembly of __eq__:
2 0 LOAD_FAST 1 (other)
2 LOAD_ATTR 0 (__class__)
4 LOAD_FAST 0 (self)
6 LOAD_ATTR 0 (__class__)
8 COMPARE_OP 8 (is)
10 POP_JUMP_IF_FALSE 36
3 12 LOAD_FAST 0 (self)
14 LOAD_ATTR 1 (name)
16 LOAD_FAST 0 (self)
18 LOAD_ATTR 2 (age)
20 BUILD_TUPLE 2
22 LOAD_FAST 1 (other)
24 LOAD_ATTR 1 (name)
26 LOAD_FAST 1 (other)
28 LOAD_ATTR 2 (age)
30 BUILD_TUPLE 2
32 COMPARE_OP 2 (==)
34 RETURN_VALUE
4 >> 36 LOAD_GLOBAL 3 (NotImplemented)
38 RETURN_VALUE
Disassembly of __ge__:
2 0 LOAD_FAST 1 (other)
2 LOAD_ATTR 0 (__class__)
4 LOAD_FAST 0 (self)
6 LOAD_ATTR 0 (__class__)
8 COMPARE_OP 8 (is)
10 POP_JUMP_IF_FALSE 36
3 12 LOAD_FAST 0 (self)
14 LOAD_ATTR 1 (name)
16 LOAD_FAST 0 (self)
18 LOAD_ATTR 2 (age)
20 BUILD_TUPLE 2
22 LOAD_FAST 1 (other)
24 LOAD_ATTR 1 (name)
26 LOAD_FAST 1 (other)
28 LOAD_ATTR 2 (age)
30 BUILD_TUPLE 2
32 COMPARE_OP 5 (>=)
34 RETURN_VALUE
4 >> 36 LOAD_GLOBAL 3 (NotImplemented)
38 RETURN_VALUE
Disassembly of __gt__:
2 0 LOAD_FAST 1 (other)
2 LOAD_ATTR 0 (__class__)
4 LOAD_FAST 0 (self)
6 LOAD_ATTR 0 (__class__)
8 COMPARE_OP 8 (is)
10 POP_JUMP_IF_FALSE 36
3 12 LOAD_FAST 0 (self)
14 LOAD_ATTR 1 (name)
16 LOAD_FAST 0 (self)
18 LOAD_ATTR 2 (age)
20 BUILD_TUPLE 2
22 LOAD_FAST 1 (other)
24 LOAD_ATTR 1 (name)
26 LOAD_FAST 1 (other)
28 LOAD_ATTR 2 (age)
30 BUILD_TUPLE 2
32 COMPARE_OP 4 (>)
34 RETURN_VALUE
4 >> 36 LOAD_GLOBAL 3 (NotImplemented)
38 RETURN_VALUE
Disassembly of __init__:
2 0 LOAD_FAST 1 (name)
2 LOAD_FAST 0 (self)
4 STORE_ATTR 0 (name)
3 6 LOAD_FAST 2 (age)
8 LOAD_FAST 0 (self)
10 STORE_ATTR 1 (age)
12 LOAD_CONST 0 (None)
14 RETURN_VALUE
Disassembly of __le__:
2 0 LOAD_FAST 1 (other)
2 LOAD_ATTR 0 (__class__)
4 LOAD_FAST 0 (self)
6 LOAD_ATTR 0 (__class__)
8 COMPARE_OP 8 (is)
10 POP_JUMP_IF_FALSE 36
3 12 LOAD_FAST 0 (self)
14 LOAD_ATTR 1 (name)
16 LOAD_FAST 0 (self)
18 LOAD_ATTR 2 (age)
20 BUILD_TUPLE 2
22 LOAD_FAST 1 (other)
24 LOAD_ATTR 1 (name)
26 LOAD_FAST 1 (other)
28 LOAD_ATTR 2 (age)
30 BUILD_TUPLE 2
32 COMPARE_OP 1 (<=)
34 RETURN_VALUE
4 >> 36 LOAD_GLOBAL 3 (NotImplemented)
38 RETURN_VALUE
Disassembly of __lt__:
2 0 LOAD_FAST 1 (other)
2 LOAD_ATTR 0 (__class__)
4 LOAD_FAST 0 (self)
6 LOAD_ATTR 0 (__class__)
8 COMPARE_OP 8 (is)
10 POP_JUMP_IF_FALSE 36
3 12 LOAD_FAST 0 (self)
14 LOAD_ATTR 1 (name)
16 LOAD_FAST 0 (self)
18 LOAD_ATTR 2 (age)
20 BUILD_TUPLE 2
22 LOAD_FAST 1 (other)
24 LOAD_ATTR 1 (name)
26 LOAD_FAST 1 (other)
28 LOAD_ATTR 2 (age)
30 BUILD_TUPLE 2
32 COMPARE_OP 0 (<)
34 RETURN_VALUE
4 >> 36 LOAD_GLOBAL 3 (NotImplemented)
38 RETURN_VALUE
Disassembly of __repr__:
352 0 LOAD_GLOBAL 0 (id)
2 LOAD_FAST 0 (self)
4 CALL_FUNCTION 1
6 LOAD_GLOBAL 1 (_thread)
8 LOAD_METHOD 2 (get_ident)
10 CALL_METHOD 0
12 BUILD_TUPLE 2
14 STORE_FAST 1 (key)
353 16 LOAD_FAST 1 (key)
18 LOAD_DEREF 0 (repr_running)
20 COMPARE_OP 6 (in)
22 POP_JUMP_IF_FALSE 28
354 24 LOAD_CONST 1 ('...')
26 RETURN_VALUE
355 >> 28 LOAD_DEREF 0 (repr_running)
30 LOAD_METHOD 3 (add)
32 LOAD_FAST 1 (key)
34 CALL_METHOD 1
36 POP_TOP
356 38 SETUP_FINALLY 12 (to 52)
357 40 LOAD_DEREF 1 (user_function)
42 LOAD_FAST 0 (self)
44 CALL_FUNCTION 1
46 STORE_FAST 2 (result)
48 POP_BLOCK
50 LOAD_CONST 0 (None)
359 >> 52 LOAD_DEREF 0 (repr_running)
54 LOAD_METHOD 4 (discard)
56 LOAD_FAST 1 (key)
58 CALL_METHOD 1
60 POP_TOP
62 END_FINALLY
360 64 LOAD_FAST 2 (result)
66 RETURN_VALUE
# not exactly readable, but here it is.. the code that dataclass added.