views:

106

answers:

4

I'm currently trying to write a multiple-file Python (2.6.5) game using PyGame. The problem is that one of the files, "pyconsole.py", needs to be able to call methods on instances of other objects imported by the primary file, "main.py". The problem is that I have a list in the main file to hold instances of all of the game objects (player's ship, enemy ships, stations, etc.), yet I can't seem to be able to call methods from that list within "pyconsole.py" despite the fact that I'm doing a from pyconsole import * in "main.py" before the main loop starts. Is this simply not possible, and should I instead use M4 to combine every file into 1 single file and then bytecode-compile and test/distribute that?

Example:

bash$ cat test.py
#!/usr/bin/python

import math, distancefrom00
foo = 5

class BarClass:
    def __init__(self):
        self.baz = 10
    def get(self):
        print "The BAZ is ", self.baz
    def switch(self)
        self.baz = 15
        self.get()

bar = BarClass()

def main():
    bar.switch()
    print distancefrom00.calculate([2, 4])

if __name__ == '__main__': main()

bash$ cat distancefrom00.py
#!/usr/bin/python

import math
import test

def calculate(otherpoint):
    return str(math.hypot(otherpoint[0], otherpoint[1]))+" (foo = "+str(test.foo)+"; "+test.bar.get()+")"

bash$ python test.py
The BAZ is  15
The BAZ is  10
Traceback (most recent call last):
  File "test.py", line 24, in <module>
    if __name__ == '__main__': main()
  File "test.py", line 22, in main
    print distancefrom00.calculate([2, 4])
  File "/home/archie/Development/Python/Import Test/distancefrom00.py", line 8, in calculate
    return str(math.hypot(otherpoint[0], otherpoint[1]))+" (foo = "+str(test.foo)+"; "+test.bar.get()+")"
TypeError: cannot concatenate 'str' and 'NoneType' objects

If my somewhat limited understanding of Python names, classes, and all that stuff is correct here, the NoneType means that the name test.bar.get() - and thus, test.bar - is not assigned to anything.

A: 

Are you instantiating an object in pyconsole in main.py? If you've got a class called PyConsole in pyconsole, give its __init__ method a parameter that takes the list of game objects. That way your pyConsole object will have a reference to the objects.

Hope this helps. It seems like you've just misunderstood the way Python works with imported modules.

Skilldrick
+1  A: 

The problem is that one of the files, "pyconsole.py", needs to be able to call methods on instances of other objects imported by the primary file, "main.py".

This just sounds like the dependencies are wrong. Generally nothing should be calling 'backwards' up to the main file. That main.py should be the glue that holds everything else together, and nothing should depend on it. Technically the dependencies should form a directed acyclic graph. As soon as you find a cycle in your dependency graph, move out the common aspects into a new file to break the cycle.

So, move the things in 'main.py' that are used by 'pyconsole.py' out into a new file. Then have 'main.py' and 'pyconsole.py' import that new file.

Kylotan
OK, if I'm understanding you correctly, that means that every instance of the resources (the sound/sprite objects, the list of game objects, etc.) needs to be put in a separate file, then imported by `main.py`, `pyconsole.py`, and everything else that deals with those objects? My question here is: will doing so actually cause each separate file to be working on the same "copy" of the namespace of each other file that is `import`ed, or will I end up making endless copies of every namespace, with each one being influenced by only one other file (which is not the behavior of choice, obviously)?
Bushman
It's not really about instances of resources, but about code dependency. The resources can be instantiated wherever you like. But if you have an object that exists in one file and is used in many others, that sounds like a global variable and is almost always a bad idea. As for your namespace question, within one program, each module (ie. a file you import) exists once and once only. If two different files access pyconsole.something then that name refers to the same object in both cases.
Kylotan
Ah, that's what I didn't understand. I was unsure of whether Python internally used a scheme similar to how you protect C[++] header files from being `#include` -ed more than once using constants. Thank you for clearing that up.
Bushman
To be pedantic, in C++ they're not actually constants, just preprocessor definitions. Python performs a similar check automatically, yes.
Kylotan
A: 

The problem with the submitted code is that the get method of the BarClass class returns a value of None because the body of the method contains only a print statement. Therefore, in distancefrom00.py the result of the function calculate is:

str + str + str + str + None + str

Hence, the TypeError: cannot concatenate a 'str' and 'NoneType' objects

You can solve this problem by returning a string from a call to get. For example,

def get(self):
    return "The BAZ is %s" % self.baz

Also, note that you have a circular import in your two files. test.py imports distancefrom00.py, and distancefrom00.py imports test.py. As Kylotan says cyclic dependences are bad

PreludeAndFugue
+1  A: 

In addition to the other answers, note that when you run test.py as a script it is module __main__. When you import test.py from distancefrom00.py that creates a new test module. bar in the main script and test.bar accessible from distancefrom00.py are completely unrelated. They aren't even the same class: one is a __main__.BarClass while the other is a test.BarClass instance.

That's why you get the two outputs 15 followed by 10: the main script bar has had its switch method called, but the test module bar has not been switched.

Circular imports aside, importing your main script into another module has its own level of badness.

Duncan