4.1 KiB
[ Index | Exercise 6.2 | Exercise 6.4 ]
Exercise 6.3
Objectives:
- Learn how to inspect the internals of functions
Files Modified: structure.py
(a) 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.
(b) 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
'x', <Parameter "x">), ('y', <Parameter "y">)]))
mappingproxy(OrderedDict([(>>> tuple(sig.parameters)
'x', 'y')
(>>>
(c) 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):
= ('name','shares','price') _fields
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):
= ('name', 'shares', 'price')
_fields 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
'GOOG',100,490.1)
Stock(>>> s.shares = 50
>>> s.share = 50
Traceback (most recent call last):"<stdin>", line 1, in <module>
File "structure.py", line 12, in __setattr__
File 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.
[ Solution | Index | Exercise 6.2 | Exercise 6.4 ]
>>>
Advanced Python Mastery
...
A course by dabeaz
...
Copyright 2007-2023
.
This work is licensed under a Creative Commons
Attribution-ShareAlike 4.0 International License