Search code examples
pythonpython-dataclasses

Can python's annotation (e.g dataclass) be expanded into the code it produces?


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?


Solution

  • 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.