Basic terrain generator I've decided to try and start programming using Object-Oriented-Programming, so I built a small terrain generator that could be done using something as simple as this:

import random
for _ in range(1000):
    print random.choice('*#~o'),


Instead, I decided to have a go at OOP and made it like this instead:

# Terrain generator
from random import choice
from time import sleep


# Class containg world attr.
class Attr(object):
    def __init__(self):
        self.TILES = '#*~o'
        self.WORLD_SIZE = 1000
        self.Y_LENGTH = 1000
        self.X_LENGTH = 1000


# Generator class
class Generator(object):
    def __init__(self):
        self.world = []
        self.attr = Attr()

    # Create an empty row
    def create_row(self):
        for _ in range(self.attr.X_LENGTH):
            self.world.append(choice(self.attr.TILES))


# Main generator class
class MainGen(object):
    def __init__(self):
        self.gen = Generator()
        self.attr = Attr()

    # Create the world
    def create_world(self):
        for _ in range(self.attr.WORLD_SIZE):
            self.gen.create_row()

    # Render the world
    def render_world(self):
        for tile in self.gen.world:
            print tile,
            sleep(0.05)



# Main game class
class Game(object):
    def __init__(self):
        self.main_gen = MainGen()

    # Run the functions
    def run(self):
        self.main_gen.create_world()
        self.main_gen.render_world()


# Start the program
if __name__ == "__main__":
    game = Game()
    game.run()


All I'm really looking for are these things:


What OOP mistakes did I make?
What general mistakes did I make?
How can I improve in general?
You've gone from two easy-to-understand lines to six methods across three classes; hardly an efficient use of OOP. There is really no need to have the three classes Attr, Generator and MainGen, and they're so heavily interconnected as to suggest single class. 

If you do want to use classes, I think you should have a World class, in which generate (covering Generator and MainGen) is a class method for creating a new world and the attributes (covering Attr) are a mix of class attributes and parameters to the class method. This would look like:

import random

class World(object):
    """The world in which the game takes place.

    Args:
      map (list of list of string): a representation
        of the world, a 2-D structure of single-
        character tiles. 

    Attributes:
      map (list of list of string): the map supplied
        in the argument to __init__
      TILES (string): the characters to use for tiles. 

    """

    TILES = '#*~o'

    def __init__(self, map):
        self.map = map

    def __str__(self):
        """Display whole map as 2-D grid."""
        return "\n".join("".join(row) for row in self.map)

    def render(self):
        """Generator to provide one tile at a time."""
        for row in self.map:
            for tile in row:
                yield tile

    @classmethod
    def generate(cls, shape):
        """Generate a new, random map.

        Args:
          shape (int or tuple of int): Either a two-tuple
            (x, y) or a single integer to use for both x
            and y defining the shape of the map to create. 

        """
        try:
            x, y = shape
        except TypeError:
            x = y = shape
        return cls([[random.choice(cls.TILES)
                     for _ in range(x)]
                    for _ in range(y)])


Then when you play the game:

world = World.generate(100)
for tile in world.render():
    ...


This separates the world itself out from how the game needs it displayed. As a simple example:

&gt;&gt;&gt; world = World.generate(5)
&gt;&gt;&gt; print(str(world))
oo#~#
*oo~#
#o#o~
*o*#o
**#*o