Exploring code objects The first version of my solution was wrong (the usage of compile() function), for details see Stack Overflow.

The solution has been fixed and now it works correctly. Another approach is used: my_add.__code__. The questions stayed the same.

I am reading the book: Obi Ike-Nwosu. Inside The Python Virtual Machine and have reached the "Code Objects" chapter. This chapter has all code object fields printed out:


co_argcount = 1
co_cellvars = ()
co_code = b'|\x00d\x01\x16\x00d\x02k\x02r\x1e|\x00d\x03\x16\x00d\x02k\x02r\x1ed\\
x04S\x00n,|\x00d\x01\x16\x00d\x02k\x02r0d\x05S\x00n\x1a|\x00d\x03\x16\x00d\x02k\x02r\
Bd\x06S\x00n\x08t\x00|\x00\x83\x01S\x00d\x00S\x00'
co_consts = (None, 3, 0, 5, 'FizzBuzz', 'Fizz', 'Buzz')
co_filename = /Users/c4obi/projects/python_source/cpython/fizzbuzz.py
co_firstlineno = 6
co_flags = 67
etc...



but there is no explanation how this can be done programatically, so I wrote my own code:

# The explored function
def my_add(a,b):
    first = a
    second = b

    return first + second

def print_co_obj_fields(code_obj):
    # Iterating through all instance attributes
    # and calling all having the 'co_' prefix
    for name in dir(code_obj):
        if name.startswith('co_'):
            co_field = getattr(code_obj, name)
            print(f'{name:&lt;20} = {co_field}')

# The function code object (__code__) usage
print_co_obj_fields(my_add.__code__)


Output



co_argcount          = 2
co_cellvars          = ()
co_code              = b'|\x00}\x02|\x01}\x03|\x02|\x03\x17\x00S\x00'
co_consts            = (None,)
co_filename          = ./source.py
co_firstlineno       = 17
co_flags             = 67
co_freevars          = ()
co_kwonlyargcount    = 0
co_lnotab            = b'\x00\x01\x04\x01\x04\x02'
co_name              = my_add
co_names             = ()
co_nlocals           = 4
co_stacksize         = 2
co_varnames          = ('a', 'b', 'first', 'second')


Have I done this the optimal way? How would you solve this problem?
main guard

Put the parts of your code that are the ones calling for execution behind a if __name__ == "__main__": guard

if __name__ == "__main__":
    def my_add(a, b):
        first = a
        second = b

        return first + second
    print_co_obj_fields(my_add.__code__)


This way you can import this python module from other places if you ever want to

Documentation

There is a python convention (docstring) to document the behaviour of a method: PEP-257

def print_co_obj_fields(code_obj):
    """prints all the instance variables of the code object that start with 'co_'"""


Indentation

To decrease the levels of indentation, you can reverse the test of startswith, and continue when it fails:

def print_co_obj_fields(code_obj):
    # Iterating through all instance attributes
    # and calling all having the 'co_' prefix
    for name in dir(code_obj):
        if not name.startswith('co_'):
            continue
        co_field = getattr(code_obj, name)
        print(f'{name:&lt;20} = {co_field}')


An alternative if using the builtin filter:

def print_co_obj_fields(code_obj):
    # Iterating through all instance attributes
    # and calling all having the 'co_' prefix
    for name in filter(lambda field: field.startswith("co_"), dir(code_obj)):
        co_field = getattr(code_obj, name)
        print(f'{name:&lt;20} = {co_field}')


Presentation

Separate the generation of the results from the presentation. The easiest way to do this is to have print_co_obj_fields yield the name and co_field instead of printing them

def co_obj_fields(code_obj):
    """generator that yields the fields in a code_object and their value"""
    for name in filter(lambda field: field.startswith("co_"), dir(code_obj)):
        co_field = getattr(code_obj, name)
        yield name, co_field

def print_co_obj_fields(fields):
    for name, co_field in fields:
        print(f"{name:&lt;20} = {co_field}")


The main part then changes to:

if __name__ == "__main__":

    def my_add(a, b):
        first = a
        second = b

        return first + second

    fields = co_obj_fields(my_add.__code__)
    print_co_obj_fields(fields)