Starting with Python Introspection and reflection

Introspection, called Reflection in Java is the ability to inspect an object and determine its properties. It can be used to eliminate multiple “if.... then” statements thus making code cleaner, can be used to implement dependency injection (that is deciding the class to use at run or load time) and can eliminate the need to look at source code, saving headaches as some people write source code as compactly and obscurely as possible (I cannot claim to have been free of that fault in the past but nowadays I try to avoid it).

The code here, intended to allow Python programming beginners to get started with introspection, was developed using the Eclipse PyDev plugin for OS X and python 2.6 as supplied by OS X. A basic knowledge of Object orientation and Java Reflection is assumed.

Please note this is not production code and you use it at your own risk


Simple Sample Class

# the module was called refletionexperiments

class reflector(object):
    '''
    classdocs
    '''
    params = list();

    def __init__(self,params):
        '''
        Constructor
        '''
 
    def greet(self,greeting):
        print greeting;
        return "greeting done";
    
    def times2(self,x):
        return x+x;

Code sample using introspection

'''
Created on Jun 27, 2012

@author: alex
'''
from experiments import reflectionexperiments
from inspect import *

# given a full classname returns an object that can be used as the class
# Taken from 
# http://stackoverflow.com/questions/452969/does-python-have-an-equivalent-to-java-class-forname
# Some comments added 
#
# Note this returns a class and you have to instantiate it
def get_class( kls ):
    parts = kls.split('.')
# the class name - to the left of the first dot
    module = ".".join(parts[:-1])
# import the module just discovered. 
    m = __import__( module )
# recursively create the object. 
    for comp in parts[1:]:
        m = getattr(m, comp)           
    return m

if __name__ == '__main__':
    
    print "Creating test object";
    paramlist = {"test":"a"};
    testval= reflectionexperiments.reflector(paramlist); 
    print ;
    print "listing object attributes"   
    print "\t"  + str(dir(testval));


    print "\nFinding the greet method the hard way";
# NB  methods are first class objects in Python  
    for name in dir(testval) :       
#        print name;
        if(name == "greet"):
            print  "\t\t" + getattr(testval,name)("\tthis is the greeting string followed by return value of greet");

    print "\nFinding the greet method the easy way";
    result = getattr(testval,"greet")("printed in greet method");

    print "\t  value returned from greet is:  " + str(result);
# this is a compact but inscrutable  and inflexible way of doing this
    print "\nprinting list of methods";
    methodList = [method for method in dir(testval) if callable(getattr(testval, method))]
    print "\t" + str(methodList);
    
# lets do it a sensible way

#    methods =list();
    print "\nprinting methods as found";   
    for thing in dir(testval):
        temp = getattr(testval, thing);
        if callable(temp):
            print "\t" + thing + ":-" + str(dir(temp));
            
    print "\nFinding the first meaningful parameter of the greet  method";

# get the name of the first parameter of the greet method other than "self"    
    result = getattr(testval, "greet");
    print    "\t" + result.func_code.co_varnames[1];
    
    print "\nFinding the first meaningful parameter of the greet  method using inspect";
# another way to do it but less flexible and with unwanted confusing brackets

    print  "\t" + (getargspec(result)[0])[1];
    
    
#    Now try to instantiate an object using strings
    print "\nInstantiating and calling method by name given package";
    test2 = getattr(reflectionexperiments,"reflector")("");
    test2.greet("\tInstantiating & calling method by name given package");
    
    print "\nInstantiating class by name and calling method  by name";
    test3 = get_class("reflectionexperiments.reflector")("");
    
    test3.greet("\tInstantiating class by name & calling method  by name");
  

Useful Keywords

The dir() keyword

This is where most people start. It returns a sorted list of attribute names for any object passed to it. If no object is specified, dir() returns the names in the current scope.

The programmer defined methods come after all the internal attributes which are surrounded by underscores.

It is worth experimenting with dir() on various objects including your own classes and methods

getattr

Where dir() simply lists all the attributes of an object, getattr returns an object that is an attribute of its argument.

A method is a callable object and, if retrieved by getattr() must be followed by a parameter list if you want to use it. The predicate for testing whether the code is a methods is callable(object)

These two keywords should get you through 90% of the cases where introspection helps. Sometimes however you want to know the parameters of a method and this can be done in more than one way

Finding the parameters of an object

The first method is to my mind the simplest, at least for beginners:

# get the name of the first parameter of the greet method other than "self"

result = getattr(testval, "greet");

print result.func_code.co_varnames[1];

func_code.co_varnames is a list of the method's parameter names. element zero is "self"

frominspect import *

print (getargspec(result)[0])[1];

prints out “greeting”

Here getargspec(result)[0] is the list of parameter names and as before the zeroth element is “self”. This has more parentheses than I like: any more and I may as well re-learn LISP, which for a contractor is almost useless, since almost no clients use it.

Dynamic instantiation

There is no built in method equivalent to Java's Class.forName() method and the method get_class in the sample code was taken from a stackOverflow post. The sample code shows how to use it with the class reflectionexperiments.reflector

The Wrap

I have presented a Python introspection starter kit which I think will cover most of the situations where the average developer will need it. The keyword dir(object) lets you find the attributes of an object, callable(object) tells you whether an attribute is a method, type(object) tells you the type of the object and object.func_code.co_varnames lists all a method's parameters.

Further reading

More by this Author


Comments 4 comments

AlexK2009 profile image

AlexK2009 4 years ago from Edinburgh, Scotland Author

Yes, I think I had to do something like that to get MongoDB to play nicely with Python in Eclipse. I have not heard of virtualenv.


derek gulbranson profile image

derek gulbranson 4 years ago from San Francisco

Yea, I'm not sure how well iPython plays with Eclipse. It is a setuptools install though, so you can always test things out using virtualenv and then just delete it if something screws up.


AlexK2009 profile image

AlexK2009 4 years ago from Edinburgh, Scotland Author

Thanks for that. I will have to look into it. I do not know how well the tools you mention work in Eclipse, and now I have pydev working with MongoDB I am reluctant to risk screwing up ... errr I mean changing the system.


derek gulbranson profile image

derek gulbranson 4 years ago from San Francisco

If you're really interested in getting into Python's introspection capabilities I'd suggest installing ipython and ipdb. Instead of having to type dir() around everything you can just press tab. Also you don't mention pdb.set_trace() which is my most frequently used introspection tool. I have "import ipdb; ipdb.set_trace()" as a tab trigger in TextMate so I can quickly throw a breakpoint in anywhere.

    Sign in or sign up and post using a HubPages Network account.

    0 of 8192 characters used
    Post Comment

    No HTML is allowed in comments, but URLs will be hyperlinked. Comments are not for promoting your articles or other sites.


    Click to Rate This Article
    working