Python access parent object instances - python

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.

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.

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.

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.

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

Related

Passing variables between two files both ways in python

Okay, so I know this may seem stupid but I am currently making a game using multiple files, one main one that receives all variables from other files and uses them in ways. I'm using the:
from SPRITES import *
to get these variable over, however now I need a variable that can only be defined in MAIN in SPRITES (as the platform the player is standing on is located in main, and this needs to change the controls defined in sprites), however if I just do a
from MAIN import *
this seems to break the connection completely. Help please
EDIT: Okay, currently my file is probs too large to post all code on here but I'll try to post whats relevent on here (first time here). This is the start to the main 'titleMAIN' file
import pygame as pg
import random
from titleSETTING import *
from titleSPRITE import *
cont = ("green")
class Game:
def __init__(self):
# initialize game window, etc
pg.init()
and so on
calling upon the Player class in the SPRITES file from the Game class - I need to be able to use the 'cont' variable in the Player class:
def new(self):
# start a new game
cont = ("green")
...
self.player = Player(self)
self.all_sprites.add(self.player)
And here is where I tried to call upon the MAIN file from the SPRITES file:
from titleSETTING import *
...
class Player(pg.sprite.Sprite):
def __init__(self, game):
Sorry that I was vague, first time here and kinda a novice at coding, no matter how much I enjoy it. BTW by files I mean different python (.py) files in the same folder - using IDLE as an IDE, which sucks but it's what I got
EDIT 2: Thanks for the responses - I realize that it's probably better to just try to make this all one file instead of two, so to not over complicate the code, so I'll work with that mindset now
The main reason this wasn't working for you is that circular imports are problematic. If some file A imports some other file B, then you do not want B to import A (even indirectly, via some other files).
So to make it work, I split main into two.
As an aside, using global variables (and import *) tends to make programs harder to read. Instead of a bunch of globals, consider perhaps a single global that has the values you need as fields. Instead of import *, consider explicit imports (just import sprites, and then sprites.foo).
main.py:
from sprites import *
from config import *
print "Hello from main. main_value is: ", main_value
print "sprite value is: ", sprite_value
do_sprite()
sprites.py:
from config import *
sprite_value=10
def do_sprite():
print "main value is: ", main_value
config.py:
main_value=5
While technically possible (using some obscure Python features), what you're trying to achieve is neither easy, nor actually a good idea.
Note that the fact that you did from moduleX import *, doesn't make the variables defined in moduleX magically available in main (or where-ever you put the import statement). What it does, it creates new variables with the same names in your current module and make them point to the same objects as those in moduleX at the moment when the import is executed. Let's say there's A in some module named X and it was initialized to "foo". You did import * from X and now print(A) will show foo. If you now call a function from X and it changes A to bar, it won't affect what you have in main - that is still the object foo. Likewise, if you do a="baz" in main, functions from X that refer to A will still see X's copy of that variable.
If you need some data to be available to more than one module, it may be best to arrange for all that shared data to be stored in some common object and have an easily-accessible reference to that object in all the modules that need the shared data. Here are possible choices for this object, pick what suits your taste:
it can be a module that's just meant to keep common variables, let's say it is called data or common (have an empty file data.py). Import it everywhere you need to and set variables as data.A = "something" or use them as needed, e.g. print (data.A).
it can be an instance of a class that you define yourself,
e.g.:
class data_class(object):
# set initial values
var1 = 10
A = "foo"
Now, create an instance of it with data = data_class() and pass it to every module that needs it. E.g., define it in one module and import it from everywhere else.
you can also use a Python dictionary (and, like with the class instance, have a reference to it in all modules). You will then refer to your common data items as data["A"], etc.

Python class, data structure and proper architecture

I'm writing a program, which request user information from different services, puts them together in some ways. manages stuff and does some slack interaction.
All my python projects get problematic at a certain size. imports start to become recursive and handling data around becomes annoying.
A quick example of a problem I just come across can be shown with this simple example. I have a main module (here A) which creates the main objects (singletons).
These objects need to call functions from each other, so I use main as a connector. In this given example I don't understand when B is created the list that it requests from A is (None) NoneType. The getter function is not necessarily the way I go, but it helped in another situation. Do you have any tips, reads to point, how to structure middle-sized python programs. Thanks!
import B
some_list = None
b = None
def get_list():
return some_list
if __name__ == "__main__":
some_list = [1,2,3]
b = B.B()
print b.my_list
And module B
from A import get_list
class B:
def __init__(self):
self.my_list = get_list().map(lambda v : v * 2) # CRASH HERE!
You have two copies of the main module now, each a separate entry in sys.modules:
The initial Python script, started from the command-line, is always called __main__.
You imported the A.py file as the A module. This is separate from the __main__ module.
Yes, the same source file provided both modules, but Python sees them as distinct.
As a result, that second copy does not have the if __name__ == '__main__': block executed, because the __name__ variable is set to 'A' instead. As such, A.some_list and A.b remain set to None; you wanted __main__.some_list and __main__.b instead.
Don't put code in your main entry point that other modules need to import to have access to. Pass in such dependencies, or have them managed by a separate module that both the main module and other modules can import.
You could, for example, pass in the function to the B() class:
b = B.B(get_list)

How to change class object parameters from another module (python)

So I've searched around and couldn't find an answer. I'm looking to change parameters from an object created in my main file, in a module. For example, I'm testing this with a simple piece of code here:
-this is my main file, from which i create the objects and define some properties
import class_test_2
class dog():
name=''
spots=0
def add_spots(self):
self.spots+=1
def main():
fido=dog()
fido.name='Fido'
print('Fido\'s spots: ',fido.spots)
fido.add_spots()
print('Fido\'s spots: ',fido.spots)
class_test_2.class_test()
print('Fido\'s spots: ',fido.spots)
if __name__=='__main__':
main()
-this is the module, from which I want to use functions to change the attributes in the main file
from class_test_1 import dog
def class_test():
fido.add_spots()
-So my question is how can I do this/why doesn't this piece of code above work?
Running the main function on its own shows fido's spots increasing by 1 each time its printed. Running the code calling the module however gives a NameError so my module isn't recognising the class exists even though I've managed to import it. Thanks in advance for any help.
Your variable "fido" is only defined within your "main" function. You must provide your "class_test" function with the variable.
For example:
class_test_2.class_test(fido)
Then your class_test function gets an argument. You can choose the name freely. I used the_dog in the example:
def class_test(the_dog):
the_dog.add_spots()
In this case the_dog points to the same instance of your dog class as fido.

Accessing an object from another module when the class is also in a different module?

Hello i'm still kinda new to object oriented programming as well as python and i'm a little stuck.
I have three files one file contains a class, another file accesses this class and a third file that needs to access the object created in the main file.
class file
#Class.py
class myclass:
def method(self, value)
return value
Main File
#Main file
import Class
Myobj = Class.myclass
file 3.
#file 3
x= 10
#I need to access the output of myobj.method(x) from file3 but I cannot import main
#into file 3
In my program the main file will initialize the class (the class will read in a file and assigns the contents of the file to arrays when it is initialized).
the class file also preforms a mathematical operation (the method function in the class file) but this function is dependent on a value that is calculated in file 3.
So I need the value that myobj.method(x) will output in file 3, the value of x will change as the program runs so I will end up with several outputs from myobj.method(x)
The problem is the main file can be any name the user chooses, so I cant simply import main into file 3.
You can only do that if you store the output of myobj.method(3) first:
#Main file
import Class
myobj = Class.myclass()
output = myobj.method(3)
Now you can refer to Main.output after importing Main.
print doesn't store anything; all it does is turn the expressions that follow it into strings and write those strings to the standard output. Technically, you could try and replace sys.stdout, but then you'd only capture strings, not actual original values.
It sounds, however, as if you want to expose some kind of API to a 3rd party that will import both Class and your 3rd module. In that case, have the user of your API call a hook method in your 3rd library instead, or have it set a value:
import thirdmodule
thirdmodule.configure(myobj.method(3))
This neatly avoids circular imports too.
Another option is for Class to be made responsible for updating thirdmodule; at the end of myclass.method() add code to update thirdmodule:
#Class.py
import thirdmodule
class myclass:
def method(self, arg):
# Do your thing
retval = some_calculation()
thirdmodule.x = retval
return retval
Well, for starters, you're going to want to change your method definition like so:
def method(self, value):
return value
Secondly, in your main file, let's assume it's called mainfile.py, you'll have to assign the value of myobj.method(3) to some variable. Let's call it val.
So your print line then becomes a = myobj.method(3)
Then, in your third file, you import your main file with an import mainfile. The .py extension is unnecessary. Past that, it's simple: your output is stored in the variable mainfile.val; you can access that.
Oh, also, in your main file, you've called your object Myobj first and then myobj, you may want to fix that.

How can classes work in the same file but if they are separated then imported properly there are errors?

I am new to programming, I have made a little program just to learn how to use python and I made this.
It works just fine if I have it in one file, but when I separate it into three files calles Atlas.py , Robot.py and Test.py.
I get an error: "Undefined Variable: Atlas" in the robot class on the init line.
I have commented which file it is inside in a comment right above.
#Atlas.py
class Atlas:
def __init__(self):
self.robots = []
self.currently_occupied = {}
def add_robot(self, robot):
self.robots.append(robot)
self.currently_occupied = {robot:[]}
#Robot.py
class Robot():
def __init__(self, rbt, atlas = Atlas): #This is the main error:"Undefined Variable: Atlas" This happens after i separate the file
self.xpos = 0
self.ypos = 0
self.atlas = atlas()
self.atlas.add_robot(rbt)
self.name = rbt
def walk(self, axis, steps=2):
....
#Test.py
robot1 = Robot("robot1")
I put these classes into the corresponding files and Test.py looks like this now:
#Test.py
import Robot
robot1 = Robot("robot1")
In Robot.py your first line should be (assuming the file is called Atlas.py):
from Atlas import Atlas
meaning "from the Atlas.py file (also known as the Atlas module) import the Atlas class", so the Atlas variable becomes available in the Robot.py file (also known as the Robot module).
IOW, if you need the Robot class in another file (and it still lives in the Robot module at Robot.py), you'll need to add "from Robot import Robot" to that new file.
I suggest you read as much of the Python Tutorial as you can. Otherwise, your current problems are more directly about modules, so read that section.
Python is cool in this way, as the understanding will lead you why Python gets simpler as you learn more.
Python just executes scripts from top to bottom, in a namespace dictionary.
In the first example, your code looks like:
a 10 line class statement adds Atlas to the default namespace
a 12 line class statement adds Robot to the default namespace
robot1 = Robot("robot1")
and that last line is really rewritten as a function call:
robot1 = default_namespace.Robot.init("robot1")
# plus a bit of magic to create an instance and grab the return value.
which works because the Robot class is its own namespace of type Class.
When you separate the files, and hit this code:
import Robot
it means:
if we haven't already imported the module Robot:
find the file named Robot.py
execute it, line by line, and keep the kept_namespace
default_namespace.Robot = kept_namespace
So, in our main line test:
>>> print type(Robot), dir(Robot)
<type 'module'>, [..., 'Robot']
>>> print type(Robot.Robot), dir(Robot.Robot)
<type 'classobj'>, [..., '__init__', 'walk']
>>> robot1 = Robot.Robot("robot1")
To make it simpler, or more confusing, the default namespace, modules, and classes are all dict objects with a little typing magic to cut down on coding errors.
With the import, you're importing a different namespace into your current running script namespace.
Either access the class as Robot.Robot("robot 1") or use an import robots as robots.

Categories