• Advertisement
Sign in to follow this  

[Python] Module Organization

This topic is 4316 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I've recently started playing around with Python. I'm very attracted to its syntax, the availability of libraries like PyGame and PyUI, and its platform independence. My problem is that I am unable to figure out how to organize my .py files. My past experience is C++ and C#, so I need some pointers on how to get into the Python mindset. Here's what I have so far... main.py
import pygame, sys, state
from pygame import *

pygame.init()
displaySurface = pygame.display.set_mode((800, 600))

states = {
	"MainMenu" : state.MenuState(),
	"Action" : state.ActionState() }

curState = state.MenuState()

while True:
	for e in pygame.event.get():
		if e.type == QUIT:
			sys.exit()
		else:
			curState.event(e)

	displaySurface.fill((0,0,0))
	curState.paint()
	pygame.display.flip()


state.py
from pygame import *
	
class State:
	def event(self, event):
		pass
	def paint(self):
		pass

class MenuState(State):
	def event(self, event):
		if event.type == KEYDOWN and event.key == K_RETURN:
			main.curState = main.states["Action"]
			
	def paint(self):
		# draw stuff onto the screen (GUI, demo background, etc.)
		main.displaySurface.blit(...)

class ActionState(State):
	def event(self, event):
		if event.type == KEYDOWN and event.key == K_RETURN:
			main.curState = main.states["MainMenu"]

	def paint(self):
		main.displaySurface.blit(...)


The important bits are in the state.py file, where the main module is referenced. Of course, this doesn't work. Running the code as is, I get a "global name 'main' is not defined" error when the RETURN key is pressed. I can see a few ways to fix this: 1) Add "import main" immediately before "main.curState = ...". This solution works great, but would require me to sprinkle this statement in every method that accesses the "global" objects. 2) Remove all "global" objects from the design. If an object/method needs a reference to an object, the reference will be passed to it. In my case, the State.event() methods could take a reference to a SetState() method in main.py, which could be used to set the current state based on a given state name. The State.paint() methods could require a pygame.Surface object reference for rendering. 3) Is it possible to import an object reference from a module without actually executing the entire module? All forms of the "import" keyword seem to execute the entire module, even if it has already been executed elsewhere in the application. Which solution would be the cleanest and/or most "pythonic"? Is there a fourth solution that would work any better? Thanks in advance, - Mike

Share this post


Link to post
Share on other sites
Advertisement
2 is a good choice, and as for 3, not quite, but:


def main():
displaySurface = pygame.display.set_mode((800, 600))

states = {
"MainMenu" : state.MenuState(),
"Action" : state.ActionState() }

curState = state.MenuState()

while True:
for e in pygame.event.get():
if e.type == QUIT:
sys.exit()
else:
curState.event(e)

displaySurface.fill((0,0,0))
curState.paint()
pygame.display.flip()

if __name__ == "__main__":
pygame.init()
try:
main()
except:
pygame.quit()
raise





__name__ receives the name of the module, or "__main__" if it is the "main" module that was used to start the application. You can thus fence off bootstrap code (or module test code!). Beware circular imports, though.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Rather

if __name__ == "__main__":
pygame.init()
try:
main()
finally:
pygame.quit()

Share this post


Link to post
Share on other sites
Importing Fruny's main.py would not provide the subsystem references to the importing module, would it? Since displaySurface, for example, is only declared if the main() method is executed, the state.py module would not be able to access it.

Going with option #2 would increase the complexity, but reduce the coupling between my modules, right? I don't know if I will like passing subsystem object references in and out of a lot of methods, but I suppose that it will encourage me to think more about the design.

So is option #2 essentially the most recommended choice?

Share this post


Link to post
Share on other sites
Quote:
Original post by Anonymous Poster
Rather
*** Source Snippet Removed ***


Duh, that's what I meant to do. Brain fart.


doctorsixstring - Alternatively, think about writing an Application class rather than just a main() function.

Share this post


Link to post
Share on other sites
I would still need a seperate statement to create an instance of the App class, right? If so, wouldn't that statement get executed everytime I "import main" from another module? Adding "if __name__ == '__main__':" would simply prevent the modules from accessing the application object, as far as I can tell.

I'll probably lean towards using option #2, but it would be nice to understand how the "global" references could work.

Share this post


Link to post
Share on other sites
Quote:
Original post by doctorsixstring
3) Is it possible to import an object reference from a module without actually executing the entire module? All forms of the "import" keyword seem to execute the entire module, even if it has already been executed elsewhere in the application.


I just finished Dive Into Python, and Practical Python, and IIRC the call to the second import shouldn't actually execute the entire module... but if you tested this, then its likely to be true... however the text said that Python just performs a lookup to see if it is loaded already.

If I'm wrong, I would be interested in findout, thanks,

L-

Share this post


Link to post
Share on other sites
Quote:
Original post by Lucidquiet
...IIRC the call to the second import shouldn't actually execute the entire module...


As far as I can tell, this only applies to multiple imports in the same module. If I import module A from B and C, the code in module A will get executed twice (once when B is executed, and once when C is executed).

When I get home from work, I'll post a little example code. In the meantime, if anyone else has a counter-example, please post it.

Share this post


Link to post
Share on other sites
foo.py

print "foo"

bar.py

import foo
print "bar"

quux.py

import foo
import bar
print "quux"



prints out "foo" "bar" "quux", and not "foo" "foo" "bar" "quux"

Share this post


Link to post
Share on other sites
Ah, that makes sense. My example falls under the "circular dependency" problem:

foo.py
import bar
print "foo"

bar.py
import foo
print "bar"

The output here is: "foo" "bar" "foo" (when running foo.py).

Share this post


Link to post
Share on other sites
Quote:
Original post by doctorsixstring
I would still need a seperate statement to create an instance of the App class, right? If so, wouldn't that statement get executed everytime I "import main" from another module? Adding "if __name__ == '__main__':" would simply prevent the modules from accessing the application object, as far as I can tell.


Any thoughts? Is it impossible to import the reference without re-executing main() every time?

Share this post


Link to post
Share on other sites
It has been a few weeks since this thread has been updated, but I thought I'd post my thoughts from my recent development. My "engine" is used in a way that is similar to PyGame (i.e. "pygame.graphics.set_mode(...)"). Rather than typing out a long-winded explanation, I'll post some sample/pseudo-code:

engine.py

from graphics import Graphics

graphics = None
input = None
sound = None
timer = None
currentState = None

def Init(...):
global graphics, input, sound, timer, currentState
graphics = Graphics(...)
# etc...

def Run():
global graphics, input, sound, timer, currentState
while True:
engine.timer.Update()
engine.input.Update()
engine.sound.Update()
engine.graphics.Render()
currentState.Update()



graphics.py

class Graphics:
def __init__(self, width, height, bpp, ...)
# initialize PyGame and PyOpenGL
def Render():
# draw stuff to the screen



main.py

import engine, actionState

engine.Init(...)
engine.currentState = actionState.ActionState()
engine.Run()



actionState.py

import engine

class ActionState:
def Update(self):
# here we access the "global" timer and get the current system time
time = engine.timer.GetSystemTime()
# update the game world or something



I like this solution better than passing the "engine" reference back and forth between every object in my game, since it is far simpler, which means less time spent on maintenance and initial development.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement