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?
1 Answer 1
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)
-
\$\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\$MiniMax– MiniMax2019年04月29日 12:03:01 +00:00Commented 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\$Maarten Fabré– Maarten Fabré2019年04月29日 13:42:36 +00:00Commented Apr 29, 2019 at 13:42