Time for Some Introspection
March 7, 2011 5 Comments
“…The illusion is complete; it is reality, the reality is illusion and the ambiguity is the only truth. But is the truth, as Hitchcock observes, in the box? No there isn’t room, the ambiguity has put on weight. The point is taken, the elk is dead, the beast stops at Swindon, Chabrol stops at nothing, I’m having treatment and La Fontaine can get knotted.”
Did you notice some errors in the previous tutorial? One was fatal. The fact that no one commented on them indicates to me that no one is actually typing in the code – naughty naughty! Type it in. It’s important.
The errors have been corrected now, but they were:
(the order of the arguments is wrong, the object to dump goes first, and the file object to dump it into goes next); and
there was a stray full stop at the end of one line.
If you typed in the previous tutorial you should have received the following error:
>>> pickle.dump(fileObject,triviaQuestions) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/lib64/python2.6/pickle.py", line 1362, in dump Pickler(file, protocol).dump(obj) File "/usr/lib64/python2.6/pickle.py", line 203, in __init__ self.write = file.write AttributeError: 'list' object has no attribute 'write'
Or something like it – the exact error may be different depending on what version of python you are running.
If you receive an error like this you can always use the interpreter’s built in help function to assist:
>>> help(pickle.dump) Help on function dump in module pickle: dump(obj, file, protocol=None)
This is not entirely enlightening, but it does tell you that the order of the arguments – the object first, followed by the file second, followed by a third, optional, argument (protocol). We know it is optional because it is assigned a default value.
The object itself is also able to tell you about itself. This is called “introspection”. In English introspection means looking inward. People who are introspective spend time thinking about themselves. In Python, introspection is the ability of the program to examine, or give information about, itself. For example, try this:
>>> print pickle.__doc__ Create portable serialized representations of Python objects.
See module cPickle for a (much) faster implementation. See module copy_reg for a mechanism for registering custom picklers. See module pickletools source for extensive comments.
dump(object, file) dumps(object) -> string load(file) -> object loads(string) -> object
__version__ format_version compatible_formats
This shows the “docstring” for the pickle module. Docstring is a string which holds documentation about the object. We have learnt from the docstring that pickle has methods for dumping object to strings as well as files. Any object can have a docstring, for example, our triviaQuestions list had one [if you redo the previous tute to reconstruct it, since we haven’t instantiated it this time]:
>>> triviaQuestions.__doc__ "list() -> new empty list\nlist(iterable) -> new list initialized from iterable's items"
In this case, the docstring is the same for all lists (try .__doc__). However, some objects, particularly classes (which we haven’t met yet) and functions, are able to have their own docstrings which are particular to that object. A docstring can be created for an object by adding a comment in triple single quotes (”’) at the start of the object’s definition (other comment forms like single quotes work, but triple single quotes are the convention so that you can include apostrophes etc in the docstring):
>>> def square(x): ... '''Given a number x return the square of x (ie x times x)''' ... return x*x ... >>> square(2) 4 >>> square.__doc__ 'Given a number x return the square of x (ie x times x)'
When you write code you should also write docstrings which explain what the code does. While you may think you’ll remember what it does in the future, the reality is that you won’t!
How did I know that pickle had it’s own docstring? Well, I read it somewhere, like you read it here. However, if you ever find yourself needing to work out what forms part of an object Python has a function to do it – it’s called dir(). You can use it on any object. Let’s have a look at it on the square() function we just made up:
[‘__call__’, ‘__class__’, ‘__closure__’, ‘__code__’, ‘__defaults__’, ‘__delattr__’, ‘__dict__’, ‘__doc__’, ‘__format__’, ‘__get__’, ‘__getattribute__’, ‘__globals__’, ‘__hash__’, ‘__init__’, ‘__module__’, ‘__name__’, ‘__new__’, ‘__reduce__’, ‘__reduce_ex__’, ‘__repr__’, ‘__setattr__’, ‘__sizeof__’, ‘__str__’, ‘__subclasshook__’, ‘func_closure’, ‘func_code’, ‘func_defaults’, ‘func_dict’, ‘func_doc’, ‘func_globals’, ‘func_name’]
I bet you didn’t realise that the function we just defined now had so many attributes/methods!! You can see that __doc__ is one of them. Where an attribute starts with two underscores ‘__’ it’s got a special meaning in Python. You can pronounce the two underscores in a number of different ways including: “underscore underscore”, “under under”, “double underscore”, “double under” and, my favourite, “dunder”.
To tell whether these are methods (think functions) rather than attributes (think values) you can use the callable() function:
>>> callable(square.__repr__) True >>> callable(square.__doc__) False
If it is callable, then you can add parentheses to it and treat it like a function (sometimes you will need to know what arguments the callable takes):
>>> square.__repr__() '<function square at 0x7f0b977fab90>'
The __repr__() method of an object gives a printable version of the object.
When something goes wrong with your program you can use Python’s introspection capabilities to get more information about what might have gone wrong and why. Also, don’t forget to check the Python docs!
- go over previous tutes and identify 3 objects
- for each of these objects:
- re-do the relevant tute to instantiate (ie create) each of these objects;
- look at the docstring for the object (print objectName.__doc__); and
- look at the directory listing for the object (print dir(objectName)).
- Extra marks:
- find some callable methods in one listing and call them.