Inspect the Internals of Functions

PythonPythonBeginner
Practice Now

This tutorial is from open-source community. Access the source code

Introduction

Objectives:

  • Learn how to inspect the internals of functions

Files Modified: structure.py


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("`Python`")) -.-> python/BasicConceptsGroup(["`Basic Concepts`"]) python(("`Python`")) -.-> python/ControlFlowGroup(["`Control Flow`"]) python(("`Python`")) -.-> python/DataStructuresGroup(["`Data Structures`"]) python(("`Python`")) -.-> python/FunctionsGroup(["`Functions`"]) python(("`Python`")) -.-> python/ModulesandPackagesGroup(["`Modules and Packages`"]) python(("`Python`")) -.-> python/ObjectOrientedProgrammingGroup(["`Object-Oriented Programming`"]) python(("`Python`")) -.-> python/ErrorandExceptionHandlingGroup(["`Error and Exception Handling`"]) python(("`Python`")) -.-> python/PythonStandardLibraryGroup(["`Python Standard Library`"]) python/BasicConceptsGroup -.-> python/comments("`Comments`") python/BasicConceptsGroup -.-> python/variables_data_types("`Variables and Data Types`") python/ControlFlowGroup -.-> python/conditional_statements("`Conditional Statements`") python/ControlFlowGroup -.-> python/for_loops("`For Loops`") python/DataStructuresGroup -.-> python/lists("`Lists`") python/DataStructuresGroup -.-> python/tuples("`Tuples`") python/FunctionsGroup -.-> python/function_definition("`Function Definition`") python/ModulesandPackagesGroup -.-> python/importing_modules("`Importing Modules`") python/ModulesandPackagesGroup -.-> python/using_packages("`Using Packages`") python/ModulesandPackagesGroup -.-> python/standard_libraries("`Common Standard Libraries`") python/ObjectOrientedProgrammingGroup -.-> python/classes_objects("`Classes and Objects`") python/ObjectOrientedProgrammingGroup -.-> python/constructor("`Constructor`") python/ObjectOrientedProgrammingGroup -.-> python/polymorphism("`Polymorphism`") python/ObjectOrientedProgrammingGroup -.-> python/encapsulation("`Encapsulation`") python/ErrorandExceptionHandlingGroup -.-> python/raising_exceptions("`Raising Exceptions`") python/PythonStandardLibraryGroup -.-> python/data_collections("`Data Collections`") python/PythonStandardLibraryGroup -.-> python/os_system("`Operating System and System`") python/BasicConceptsGroup -.-> python/python_shell("`Python Shell`") python/FunctionsGroup -.-> python/build_in_functions("`Build-in Functions`") subgraph Lab Skills python/comments -.-> lab-132511{{"`Inspect the Internals of Functions`"}} python/variables_data_types -.-> lab-132511{{"`Inspect the Internals of Functions`"}} python/conditional_statements -.-> lab-132511{{"`Inspect the Internals of Functions`"}} python/for_loops -.-> lab-132511{{"`Inspect the Internals of Functions`"}} python/lists -.-> lab-132511{{"`Inspect the Internals of Functions`"}} python/tuples -.-> lab-132511{{"`Inspect the Internals of Functions`"}} python/function_definition -.-> lab-132511{{"`Inspect the Internals of Functions`"}} python/importing_modules -.-> lab-132511{{"`Inspect the Internals of Functions`"}} python/using_packages -.-> lab-132511{{"`Inspect the Internals of Functions`"}} python/standard_libraries -.-> lab-132511{{"`Inspect the Internals of Functions`"}} python/classes_objects -.-> lab-132511{{"`Inspect the Internals of Functions`"}} python/constructor -.-> lab-132511{{"`Inspect the Internals of Functions`"}} python/polymorphism -.-> lab-132511{{"`Inspect the Internals of Functions`"}} python/encapsulation -.-> lab-132511{{"`Inspect the Internals of Functions`"}} python/raising_exceptions -.-> lab-132511{{"`Inspect the Internals of Functions`"}} python/data_collections -.-> lab-132511{{"`Inspect the Internals of Functions`"}} python/os_system -.-> lab-132511{{"`Inspect the Internals of Functions`"}} python/python_shell -.-> lab-132511{{"`Inspect the Internals of Functions`"}} python/build_in_functions -.-> lab-132511{{"`Inspect the Internals of Functions`"}} end

Inspecting functions

Define a simple function:

>>> def add(x,y):
       'Adds two things'
       return x+y

>>>

Do a dir() on the function to look at its attributes.

>>> dir(add)
... look at the result ...
>>>

Get some basic information such as the function name, defining module name, and documentation string.

>>> add.__name__
'add'
>>> add.__module__
'__main__'
>>> add.__doc__
'Adds two things'
>>>

The __code__ attribute of a function has low-level information about the function implementation. See if you can look at this and determine the number of required arguments and names of local variables.

Using the inspect module

Use the inspect module to get calling information about the function:

>>> import inspect
>>> sig = inspect.signature(add)
>>> sig
<Signature (x, y)>
>>> sig.parameters
mappingproxy(OrderedDict([('x', <Parameter "x">), ('y', <Parameter "y">)]))
>>> tuple(sig.parameters)
('x', 'y')
>>>

Putting it Together

In Exercise 6.1, you created a class Structure that defined a generalized __init__(), __setattr__(), and __repr__() method. That class required a user to define a _fields class variable like this:

class Stock(Structure):
    _fields = ('name','shares','price')

The problem with this class is that the __init__() function didn't have a useful argument signature for the purposes of help and keyword argument passing. In Exercise 6.2, you did a sneaky trick involving a special self._init() function. For example:

class Stock(Structure):
    _fields = ('name', 'shares', 'price')
    def __init__(self, name, shares, price):
        self._init()
    ...

This gave a useful signature, but now the class is just weird because the user has to provide both the _fields variable and the __init__() method.

Your task is to eliminate the _fields variable using some function inspection techniques. First, notice that you can get the argument signature from Stock as follows:

>>> import inspect
>>> sig = inspect.signature(Stock)
>>> tuple(sig.parameters)
('name', 'shares', 'price')
>>>

Perhaps you could set the _fields variable from the argument signature of __init__(). Add a class method set_fields(cls) to Structure that inspects the __init__() function, and sets the _fields variable appropriately. You should use your new function like this:

class Stock(Structure):
    def __init__(self, name, shares, price):
        self._init()

    ...

Stock.set_fields()

The resulting class should work the same way as before:

>>> s = Stock(name='GOOG', shares=100, price=490.1)
>>> s
Stock('GOOG',100,490.1)
>>> s.shares = 50
>>> s.share = 50
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "structure.py", line 12, in __setattr__
    raise AttributeError('No attribute %s' % name)
AttributeError: No attribute share
>>>

Verify the slightly modified Stock class with your unit tests again. There will still be failures, but nothing should change from the previous exercise.

At this point, it's all still a bit "hacky", but you're making progress. You have a Stock structure class with a useful __init__() function, there is a useful representation string, and the __setattr__() method restricts the set of attribute names. The extra step of having to invoke set_fields() is a bit odd, but we'll get back to that.

Summary

Congratulations! You have completed the Inspect the Internals of Functions lab. You can practice more labs in LabEx to improve your skills.

Other Python Tutorials you may like