Introduction
Objectives:
- Learn how to inspect the internals of functions
Files Modified: structure.py
This tutorial is from open-source community. Access the source code
Objectives:
Files Modified: structure.py
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.
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')
>>>
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.
Congratulations! You have completed the Inspect the Internals of Functions lab. You can practice more labs in LabEx to improve your skills.