Exercise 45 trying to import another script but can get it to work

Im preparing myself to exercise 45, where I have to create my own game.
I was thinking to structure the game in a way so all my rooms are in a separate fil - Rooms.py.
However I can’t seem to get it work properly, or maybe it’s me who don’t understand it right.

I have my main code:

import Rooms

class Main(object):
    def __init__(self):
        self.Rooms = Rooms

    def mainClassTest(self):
        self.Rooms.Second.SecondClassTxt()

    def mainFunctionTest(self):
        self.Rooms.SecondFunctionTxt()

who=Main()
who.mainClassTest()
who.mainFunctionTest()

And I have my second code called Rooms.py - in same folder with the following:

class Second(object):
    def SecondClassTxt(self):
        print('SecondClassTxt')

def SecondFunctionTxt():
    print('SecondFunctionTxt')

My problem is I can get the SecondFunctionTxt() function to run, but I can’t get the method to run inside the ‘Second’ class.
I get the following error:

self.Rooms.Second.SecondClassTxt()
TypeError: SecondClassTxt() missing 1 required positional argument: ‘self’

I can see how I can just make a bunch of function for all my rooms and be done with it, but shouldn’t I also be able to access classes from my main code?

You are accessing SecondClassTxt by referencing it via the class, not via an instance of that class. In that case self is not magically filled in, so your function call lacks one argument.

class A(object):
    def func(self):
        ...

a = A()  # create an instance of A
a.func() # works, is magically the same as A.func(a)
A.func() # causes your type Error, because A is the class, not an instance

To make it work you’d have to instantiate Second in Main:

def mainClassTest(self):
    self.Rooms.Second().SecondClassTxt() # create an instance on the fly

In order to avoid making multiple instances of a single class I’d recommend that you create one instance of Second as an attribute of Main and use that for all method calls:

class Main(object):
    def __init__(self):
        self.second = Rooms.Second() # is an instance
    def mainClassTest(self):
        self.second.SecondClassTxt() # calls the method on that instance
1 Like

Thank you @florian for this.

I really like how straight forward composition is compared to inheritance.
I took it a little further, and before going down the rabbit hole i just want to make sure I understand it rigth.

If I would make a bunch of classes in my imported script and instantiate them all from Main() would I do it this way?

Main:

import Rooms

class Main(object):
    def __init__(self):
        self.Rooms = Rooms.Second() #Makes an instance of Rooms.Second From Main
        self.RoomFunction = Rooms #Access to SecondFunctionTxt() from main
        self.AllRooms = Rooms.AllRooms()

    def mainClassTest(self):
        self.Rooms.SecondClassTxt()

    def mainFunctionTest(self):
        self.RoomFunction.SecondFunctionTxt()

    def allRoomTest(self):
        self.AllRooms.Third.ThirdClassTxt()

who=Main()
who.mainClassTest()
who.mainFunctionTest()
who.allRoomTest()

Second (Rooms.py)

class First(object):
    def FirstClassTxt(self):
        print('FirstClassTxt')

class Second(object):
    def SecondClassTxt(self):
        print('SecondClassTxt')

class Third(object):
    def ThirdClassTxt(self):
        print('ThirdClassTxt')

def SecondFunctionTxt():
    print('SecondFunctionTxt')


class AllRooms(object):
    def __init__(self):
        self.First = First()
        self.Second = Second()
        self.Third = Third()

I assume self.AllRooms = Rooms.AllRooms() will make an instance of First(), Second() and Third()?
Is this the way to do it?

1 Like

Yeah, I guess you could do it that way.

But why not make it more explicit and instantiate each room in Main?

I would not bother adding Rooms as an attribute just to access stand-alone functions. You can always call Rooms.something().

1 Like

I love your enthusiasm but this will only end in disaster. :wink:

First up, just a niggling little style point:

Don’t CamelCaseThingsUnlessTheyAreAClassNames.

You should adopt the style of:

CamelCaseClasses
FULLCAPS_GLOBALS
lowercase_underscore_everything_else

Next up, you’ve got to build this up in pieces (and I think I’ve mentioned this before). First, you should write just this code:

import rooms
rooms.Second.second_class_txt()
rooms.second_function_txt()

If that doesn’t work then none of your crazy schemes are going to work. I think this kind of thinking is an art all on its own. This idea of “what’s the bare minimum first thing that has to work for my idea to work?” If you can get into this mode of thinking logistically about what do you need to do X, then it makes it easier to slowly build up working code.

Now, my code sample above still doesn’t work, and that highlights a potential error in your thinking about classes. See how you do this:

self.Rooms.Second.SecondClassTxt()

Or in my code I do this:

rooms.Second.second_class_txt()

But, hold on, Second is a class right? You can’t just call a function on a raw class like that. I mean, yeah there’s ways, but it’s not what you want. If you want that then make a module, not a class. What you actually need is this:

second = rooms.Second()
second.second_class_txt()

I think you should study this and then drop back with an explanation about why you thought:

self.Rooms.Second.SecondClassTxt()

Would work. That’ll help me figure out where your understanding went wrong and correct it.

Now, last thing is, I’m not so keen on the self.rooms part, since you already have the module imported. Attaching code to self is kind of pointless, but explain the design idea here. Maybe I just need to read your idea to understand it.

1 Like

Merry Christmas to everyone in here.

Hi @zedshaw I’ve been of the grid for a while due to family commitments. I’ve first had time to pick up on this again today.
I didn’t read your comments before I had started to lay the framework for my game. However, I think it still has the same problem because I got stuck in with the same issue and had to go back to reference this thread.
I hope it’s OK to use my new game as a reference as it has more descriptive names and I can better explain my current thinking.

What I want to do is create a game that circles through different locations, where various things happen, - like your own game - I’ve called it the “dating game” as I thought it could be funny to make something around how people find love.

I’ve sketched it out like this:


Whether it’ll end up being as advanced, I’ll have to see.

What I found annoying when I did my first initial game was that all the locations got muddled in-between the game and the map engine, so I thought it would be an excellent exercise to put all the locations in a separate .py file.
I’m currently using your suggestion to have a print out of your game in a different room to only access for inspiration when needed, so my code is very early stages - trying to get the map and engine to flow before anything else.
Currently been testing if I can access classes in my second imported .py (ex45B)

I currently have my main code (Engine):

import ex45B as DATES


class Engine(object):
    def __init__(self):
        self.map = DateLocations()


class DateLocations(object):

    Locations = {
    'date1' : DATES.Date1().play()
    }

    def play(self):
        test = self.Locations['date1']
        return test


Game1 = Engine()
test=Game1.map.play()
print(test)

My second .py (ex45B)

class Date1(object):
    def play(self):
        return 'date1'

The above code works but, I’m not sure as per your reply I’m going down the wrong path?
I think my confusion is mostly around how I best instantiate the rooms in my second file from my main .py.
Do I need to instantiate every single one of them (currently only one), or can I do it all at in one go?
Or do I need to instantiate the dictionary?

Thank you for correcting me on this.