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 randomfor _ in range(1000):    print random.choice('*#~o'),Instead, I decided to have a go at OOP and made it like this instead:# Terrain generatorfrom random import choicefrom 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 classclass 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 classclass 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 classclass 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 programif __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? 
Basic terrain generator
python;object oriented;game;python 2.7
Separation of responsibilitiesConsider the features you need and how you can group them into classes, and try to separate responsibilities as cleanly as possible. For example you need:Generate a terrain of given dimensions and set of tilesRender a generated terrainGlue code: configure the available classes/functions and do something with themHere's one possible class design to meet these requirements:WorldGenerator: generates a world from given parametersConstructor parameters: x length, y length, set of tilesPublic methods: generate (world)WorldRenderer: renders a given worldConstructor parameters: a world generatorPublic methods: render (world)main: the glue code. This could be just a method, there's no point making it a class.There's no place for an Attr class here. The attributes belong to the world generator, they can be encapsulated in it. It also makes sense to create different world generators with different attributes, instead of using constant attributes for all worlds.In your original MainGen class, you were creating a world and rendering it. Creating a world should be the responsibility of the generator class, and it makes sense to separate from rendering anyway.Suggested implementationfrom random import choiceclass WorldGenerator(object):    def __init__(self, tiles='#*~o', x_length=1000, y_length=1000):        self.world = []        self.tiles = tiles        self.x_length = x_length        self.y_length = y_length    def create_row(self):        for _ in range(self.x_length):            self.world.append(choice(self.tiles))    def create(self):        self.world = []        for _ in range(self.y_length):            self.create_row()class WorldRenderer(object):    def __init__(self, generator):        self.gen = generator    def render(self):        for tile in self.gen.world:            print tile,            # sleep(0.05)def main():    gen = WorldGenerator(x_length=30, y_length=10)    gen.create()    renderer = WorldRenderer(gen)    renderer.render()if __name__ == __main__:    main()