Some Not So Initial Python Impressions
Years ago I tried Python and instantly rejected it based on the imposed syntax rules of how whitespace was treated. To me this was a terrible idea and didn’t actually mesh at all with my sense of how my code should look. At the time I was doing a lot of Perl hacking and had a vested interest in what I had already invested in that platform.
Then I guess about a year ago I looked again at Python, this time with a new appreciation for the readability and perceived robustness of the language (not to mention a surging community). Having at this point spent time managing projects with multiple team members I had a new found respect for Python’s goal of having one obvious way to do something as opposed to the freeform nature of Perl or JavaScript (which I had a lot of experience with). I dabbled but still had no real work to do and so my foray didn’t go a lot further than to install PyDev for eclipse and create a few small test programs.
Third time’s the charm and this time around I’ve decided to make Python the back end for my new Silverlight application which I hope will assist in hosting it on Google’s app engine platform. [link to my own article]
I lost an hour or so dealing with Python terminology with “Module” vs “Package” vs “Class” so I thought I would write my thoughts. I was approaching my code thinking of classes as 1 per file where the filename and the class name match.
So I had the following structure for some initial classes:
Once I figured out how the __init__.py files worked and how to import properly then I had the following in my “__init__.py” of the GameEngine package:
__version__ = '0.0.1'
__all__ = ["Board","BoardPiece","BoardPosition","Player"]
Then in my test driver I had :
import os
import sys
from optparse import OptionParser
from SiliconTrader.GameEngine import *
#(OR)
#from SiliconTrader.GameEngine import Board
def main():
parser = OptionParser(usage="""
Testing program for the GameEngine classes, basically a throw away class for
learning some python....
Usage: %prog [options]
""")
board = Board()
board.helloBoard()
if __name__ == '__main__':
main()
And no matter what I tried in this case creating a new “Board” instance was always failing with the following error:
File "TestDriver.py", line 15, in main
board = Board()
TypeError: 'module' object is not callable
Being new to Python and without an internet connection (on holidays) I assumed my class was improperly defined, and then I assumed it was something to with reference paths. It was easy to prove the reference paths were fine as that will produce an entirely different error message. I played around with various incarnations of my class, including adding an __all__ = [‘Board’] to my Board.py file all to no effect.
Eventually I realized that I had created a module named Board and a class named Board and this is what was causing the error. Simply adding another “Board” to the reference in the above main statement fixes the problem (like so):
board = Board.Board()
board.helloBoard()
But that sucks for obvious reasons, so I re-ordered my packages and created modules that contained more than a single class so that I didn’t have to reference classes by a module name that matched the class.
So here’s an attempt to be more Python like:
Note that those green C icons are classes defined within the modules. This is a feature of the PyDev eclipse plugin and these the classes within “Model” above are all just in one text file. This isn’t terrible but it’s not ideal either, I can’t say I really like having all these classes defined in the same file, but the IDE makes things easy to navigate so until I see something easier this is how it will be. The import and creation code now looks like this by the way:
from SiliconTrader.Core import Model
from SiliconTrader.Core import Engine
#....
board = Model.Board()
board.helloBoard()
#....
I could have easily retained the * import but this is generally frowned upon and now that I am actually importing a set of classes with each line it’s much more inline with how I would want this to work.
Anyway, this is probably not something most people will get caught on, but coming from my daily C# existence I found this all a bit weird. Still easier than dealing with ruby classpaths though.