How can I improve my A* algorithm in Python? I'm trying to implement my own A* pathfinder, but the voices in my head scream of better ways to do this.  Probably because I'm exceeding Python's default maximum recursion depth of 1000.

class PathFinder():

    ## grid is a list of 8 sub-lists, each containing 8 nodes.
    ## start and goal are tuples representing the grid indexes of my start and goal.

    def __init__(self, start, goal, grid):
        ## I don't do anything with these yet, 
        ## just thought they would be good to have.
        self.goal = goal
        self.start = start
        self.grid = grid

    def find_path(self, start, goal, path=[]):

        ## My seemingly hackish method of looping through 
        ## the 8 cells surrounding the one I'm on.
        for row in (-1,0,1):
            for col in (-1,0,1):
                if row == 0 and col == 0:
                    continue
                cell = [start[0]+row,start[1]+col]


                ## Catching the inevitable exception when I try to examine
                ## the surroundings of cells that are already on the edge.
                try:
                   ## Skip if the cell is blocked ("X").
                    if self.grid[cell[0]][cell[1]] == "X":
                        continue
                except IndexError:
                    continue


                path.append(cell)
                if cell == goal:
                    return path
                self.find_path(cell, goal, path)

def main():
    import Gridder2
    grid = Gridder2.board()
    start = (0,0)
    goal = (0,3)
    pt = PathFinder(start, goal, grid)
    print(pt.find_path(start, goal))

    return 0

if __name__ == '__main__':
    main()


My find_path function particularly seems like it needs work.  Seems like I should be able to calculate a path through an 8x8 grid with a recursion depth of less than 1000.

How can I improve this?
class PathFinder():


Either but object in the () or drop them. Having them there empty makes me feel hollow inside.

    ## grid is a list of 8 sub-lists, each containing 8 nodes.
    ## start and goal are tuples representing the grid indexes of my start and goal.

    def __init__(self, start, goal, grid):
        ## I don't do anything with these yet, 
        ## just thought they would be good to have.
        self.goal = goal
        self.start = start
        self.grid = grid

    def find_path(self, start, goal, path=[]):


Don't put mutable values like list as your default parameters. Python will not create a new list everytime this function is called, rather it will keep reusing the same list which is probably not what you want

        ## My seemingly hackish method of looping through 
        ## the 8 cells surrounding the one I'm on.
        for row in (-1,0,1):
            for col in (-1,0,1):
                if row == 0 and col == 0:
                    continue


I suggest creating a global constant with the 8 directions and doing: for row, col in DIRECTIONS:

                cell = [start[0]+row,start[1]+col]


Don't use lists unless they are actually lists. This should be a tuple. 

                ## Catching the inevitable exception when I try to examine
                ## the surroundings of cells that are already on the edge.
                try:
                   ## Skip if the cell is blocked ("X").
                    if self.grid[cell[0]][cell[1]] == "X":
                        continue
                except IndexError:
                    continue


I suggest a function get_blocked that you pass cell to which check for 'X' and also returns True if the cell is off the side of the map. Also catching IndexError may not be quite what you want because negative numbers are valid indexes, but probably not want you want. I suggest checking in the get_blocked function for inboundedness.

                path.append(cell)
                if cell == goal:
                    return path
                self.find_path(cell, goal, path)

def main():
    import Gridder2


Usually not a good idea to import anywhere but at the start of a file

    grid = Gridder2.board()
    start = (0,0)
    goal = (0,3)
    pt = PathFinder(start, goal, grid)


pt?

    print(pt.find_path(start, goal))


Decide whether the start/goal get passed to the constructor or the find method. Don't do both.

    return 0

if __name__ == '__main__':
    main()


If you are going to return a value from your main function, you should pass it to sys.exit() so that it actually functions as a return code.

And everything Thomas said.