Python object interaction

10.3k Views Asked by At

So, programming in Python 3, I've got a program with a problem whose essence I've distilled in this (non)functional piece of code:

class One:
    def __init__(self):
        self.var1 = 1

    def change(self):
        two.var2 = 5

class Two:
    def __init__(self):
        one = One()
        self.var2 = 2
        one.change()

two = Two()

The IDLE interpreter throws:

> Traceback (most recent call last):
  File "C:/-", line 15, in <module>
    two = Two()
  File "C:/-", line 12, in __init__
    one.change()
  File "C:/-", line 6, in change
    two.var2 = 5
NameError: name 'two' is not defined

Apparently, doing this instead:

class One:
    def __init__(self):
        self.var1 = 1

    def change(self):
        two.var2 = 5

class Two:
    def __init__(self):
        self.var2 = 2
        one.change()

one = One()
two = Two()

...doesn't help, as it gives me the exact same type of error. I really don't understand why this is happening... or how to structure it differently. I think the last time I had a problem like this I avoided it by nesting classes (rather messy and worked for only one level of nesting, as far as I can remember), but I'd really like to know how to make these two objects communicate with each other properly.

Edit: What I have is a program whose only line of code instantiates a main class, let's call it "Earth". In this program-object everything happens, included the instantiation of other classes; let's assume it's only one in this case, and call it "Moon". What I want to do is have this object Moon be able to change the Earth's states, its different variables.

4

There are 4 best solutions below

1
On BEST ANSWER

As I see it, you just have to pass to one.change() which object you want to change

class One:
    def __init__(self):
        self.var1 = 1

    def change(self, two):
        two.var2 = 5

class Two:
    def __init__(self):
        self.var2 = 2
        one.change(self)


one = One()
two = Two()
0
On

You have two classes whose instances want to interact with each other. Each instance could keep a reference to the other but circular references are a dodgy business with python because they can confuse the reference counter and fail to delete objects when they are no longer used.

The weakref module lets you keep these relationships without messing up garbage collection. So,

import weakref

class One:
    def __init__(self, two):
        self.var1 = 1
        self.two = weakref.ref(two)

    def change(self):
        self.two().var2 = 5

class Two:
    def __init__(self):
        self.one = One(self)
        self.var2 = 2
        self.one.change()

two = Two()
print(two.var2)
0
On

You've misunderstood how python works. Until two is assigned, you cannot call two.var2 = 5. But you call that while you are instantiating (two = Two()), so the object you are building hasn't yet been assigned to two. Furthermore, it is generally poor design for classes to be accessing global scope.

Consider using Dependency Injection, where you give the class a local copy of the data it requires. This would take the form of def __init__(self, dependency): self.dependency = dependency. You may even be able to use the class Two as the dependency, creating Composition of objects rather than inheritance.

I touch on Inheritance, and it appears to be another solution: perhaps you mean to say self.var2 = 5, and to make your class Two inherit from One?

0
On

"You've misunderstood how python works." I wouldn't have said it like that; I have a thin grasp of OOP in general, not of Python specifically. But I digress...

"Until two is assigned, you cannot call two.var2 = 5. But you call that while you are instantiating (two = Two()), so the object you are building hasn't yet been assigned to two. Furthermore, it is generally poor design for classes to be accessing global scope."

I edited my code to reflect this, or at least the way I understood it:

class One:
    def __init__(self):
        self.var1 = 1

    def change(self):
        two.var2 = 5

class Two:
    def __init__(self):
        self.var2 = 2
        self.program()

    def program(self):
        one = One()
        one.change()

two = Two()

I did this thinking the problem was that the function in the second object was called BEFORE the init of the first function concluded, but it still doesn't work. Anyway, tinkering some more, I tried this, which is probably the logical way to structure it:

class One:
    def __init__(self):
        self.var1 = 1

    def change(self):
        two.var2 = 5

class Two:
    def __init__(self):
        self.var2 = 2

one = One()
two = Two()
one.change()

...and it worked, as expected. I'll look into this and see if it can work if the last three lines of code can be part of the "program-object" I'm using and still work as well. Or perhaps I could scrap the "program-object" altogether and just have what few lines of code I had anyway in the main class be outside of it.

Regardless, what I needed was Josef's solution. Thank you! But what are you doing, exactly? By using "self" as the argument for the second object's method, what are you passing? The "object"? A reference to it...?