3
\$\begingroup\$

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:<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?

200_success
145k22 gold badges190 silver badges478 bronze badges
asked Apr 5, 2019 at 19:50
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

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:<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:<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:<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)
answered Apr 29, 2019 at 7:25
\$\endgroup\$
2
  • \$\begingroup\$ "Separate the generation of the results from the presentation." But it means, that I should write the presentation part every time I use co_obj_fields. It is good from one point of view - I can customize presentation as needed (it is the main purpose of separation, I think), but it is bad when I need the same formatting everywhere, so I will get the duplication in the code. Or in this case it would be better to wrap the presentation part into another function and call it? \$\endgroup\$ Commented Apr 29, 2019 at 12:03
  • 1
    \$\begingroup\$ It would be better to wrap the presentation part in another function and call that. If you follow Brendan Rhodes' clean architecture you don't have that function call the co_obj_fields, but it just receives the results of that call. \$\endgroup\$ Commented Apr 29, 2019 at 13:42

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.