0

I am working on a small text adventure in python, and am attempting to use classes. I'm not very well versed in OOP and although I feel like I'm slowly gaining a greater understanding...I know that I still have a ways to go.

This is my room class

#!usr/bin/env python3

"""
A room class and a method to load room data from json files
"""




import json


class Room():
    def __init__(
        self,
        id = "0",
        name = "An empty room",
        desc = "There is nothing here",
        items = {},
        exits = {},
        ):
        self.id = id
        self.name = name
        self.desc = desc
        self.items = items
        self.exits = exits
    def __str__(self):
        return "{}\n{}\n{}\n{}".format(self.name, self.desc, self.items, self.exits)

    # Create method to verify exits
    def _exits(self, dir):
        if dir in self.exits:
            return self.exits[dir]
        else:
            return None

    def north(self):
        return self._exits('n')

    def south(self):
        return self._exits('s')

    def east(self):
        return self._exits('e')

    def west(self):
        return self._exits('w')

# Create method to get room info from json file
def get_room(id):
    ret = None
    with open("data/{}.json".format(str(id)) , "r") as f:
        jsontext = f.read()
        d = json.loads(jsontext, strict = False)
        d['id'] = id
        ret = Room(**d)
    return ret

This is my map class

#!/usr/bin/env python3

from rooms import *
"""
Map class used to build a map from all of the rooms
"""



class Map():

    def __init__(self, rooms = {}):
        self.rooms = rooms

    def __str__(self):
        return map(str, rooms)







def build_map(id, num_of_rooms):
    rooms = {}
    room_count = 0
    while room_count < num_of_rooms:
        rooms[id] = get_room(id)
        id += 1
        room_count += 1
    return rooms

a_map = Map(build_map(1, 3))

def test_map(map):
    return map.rooms

print(test_map(a_map))

I'm not understanding why test_map only returns a list of objects, and was wondering how I might be able to receive the actual list of rooms so that I can confirm that they were created properly. I'm sure that I'm just going about this the COMPLETE wrong way...which is why I've come here with the issue.

Noved
  • 68
  • 1
  • 6
  • I'd also recommend reading about [how to ask a good question](https://stackoverflow.com/help/how-to-ask). – Nathan Vērzemnieks Jan 22 '18 at 19:10
  • What is specifically wrong with this? I thought I worded it well enough and provided a decent example. In response to rejecting the edit, I read multiple sources regarding multi-line arguments and this was just the format I decided I liked the most. – Noved Jan 22 '18 at 19:15
  • It would have been a bit better if you included more detail about what you get and what you're expecting. The section on including a "minimum, complete, verifiable example" is probably most relevant. As for the formatting - hey, it's your code! but I've never seen multi-line arguments indented to the same level as the function body, and it struck me as very hard to read. In fact, the [style guide](https://www.python.org/dev/peps/pep-0008/#indentation) explicitly recommends against it. – Nathan Vērzemnieks Jan 22 '18 at 19:37
  • I also often like the one-argument-per-line style, but I indent it either one level deeper than the function body or directly below the open paren. – Nathan Vērzemnieks Jan 22 '18 at 19:38
  • 1
    Ah...I understand. I didn't clarify the actual output I was receiving. I'll also edit the code to follow PEP8. Thank you for the heads up. – Noved Jan 23 '18 at 17:39

1 Answers1

0

For general information about __str__ and __repr__, Check out this answer.

In this case, here's what's happening:

  • Your __str__ function on Map doesn't return a string, it returns a map object. __str__ must return a string.
  • That's not causing an error because the function isn't getting called, here: test_map returns the given Map's rooms attribute, which is a dictionary. If you tried to do print(a_map) you'd get an exception.
  • Dictionaries have their own __str__ method, which is getting called here but the dictionary's __str__ method calls __repr__ on its members, which you haven't defined. (See the linked answer for details on why this is so.)
  • When you haven't defined a __repr__ for your class, you get the __repr__ from object, which is why your print(test_map(a_map)) gives you output like {1: <__main__.Room instance at 0x7fca06a5b6c8>, 2: <__main__.Room instance at 0x7fca06ac3098>, 3: <__main__.Room instance at 0x7fca06ac36c8>}.

What I'd suggest:

  • Write __repr__ functions for both Map and Room.
  • Have Map.__repr__ return a string that indirectly relies on Room.__repr__, something like the following:

    def __repr__(self): return '<Map: {}>'.format(self.rooms)

This isn't maybe the best long-term approach - what if you have a map with a hundred rooms? - but it should get you going and you can experiment with what works best for you.

Nathan Vērzemnieks
  • 5,495
  • 1
  • 11
  • 23
  • Thank you for your response. I believe this is exactly what I'm looking for. Not sure what I was thinking with the __str__ method in the Map class, but thank you for clearing that up as well. But then, in which way would I actually call this to print so that I can see it? Would the function under my map class work to call it? I still don't think I fully comprehend. I actually looked over my map class and I remember what I was attempting to do in the str method. I was trying to map the list to a string. – Noved Jan 22 '18 at 19:19
  • You know, I think I may have misled you on this one. I'm going to edit my response a bit. – Nathan Vērzemnieks Jan 22 '18 at 19:43
  • Well, instead I rewrote it completely :-D Hope it's more useful now. – Nathan Vērzemnieks Jan 22 '18 at 19:55