1

I have the following code:

class B:

    def __init__(self, msg):
        self.msg = msg

class A:

    def __init__(self, msg):
        self.msg = msg
        self.b = B(self.msg)

    def update(self, msg):
        self.msg = msg

a = A('hello')

print a.msg
print a.b.msg

a.update('bye')

print a.msg
print a.b.msg

Which prints:

hello
hello
bye
hello

This is not what I want. What I need is that a.b.msg is "linked" to a.msg: a.b.msg must always refer to the same object as a.msg, even after rebinding a.msg. Is this possible? How can I do this?

Theolodis
  • 4,977
  • 3
  • 34
  • 53
blueFast
  • 41,341
  • 63
  • 198
  • 344
  • Do they have to be strings? – wnnmaw Jun 11 '14 at 16:49
  • 2
    Add `self.b.msg = msg` in the `update` method? – Ismail Badawi Jun 11 '14 at 16:50
  • @IsmailBadawi: this is what I want to avoid. I want that to happen automagically, not forcing it by hand. The background is that I can have lots of sub-objects using the parent's data, as cached properties, which I can not even enumerate. – blueFast Jun 11 '14 at 16:56
  • @wnnmaw: they *can* be strings, but can also be other things. The solution should support any kind of object – blueFast Jun 11 '14 at 16:58
  • strings are immutable. Just use a mutable object to store the message and then mutate it. – Bakuriu Jun 11 '14 at 17:05
  • I want all references to be linked, so that updating one reference updates all references. I do not want to update the object that the references are referring to: I am going to use new objects when updating. This is not a matter of mutable or immutable, since I am not going to mutate any object. – blueFast Jun 11 '14 at 17:10

3 Answers3

1

Your actual problem is, that you want a behaviour similar to a call by reference in C++. To achieve this, what you could do is to work with a little hack to emulate a memory location. Found here The reason for that to work is found in this questions accepted answer, it is related to Pythons mutable and immutable objects.

class ref:
    def __init__(self, obj): self.obj = obj
    def get(self):    return self.obj
    def set(self, obj):      self.obj = obj

class B:
    def __init__(self, msg):
        self.msg = msg

class A:
    def __init__(self, msg):
        self.msg = ref(msg)
        self.b = B(self.msg)

    def update(self, msg):
        self.msg.set(msg)

a = A('hello')

print a.msg.get() #hello
print a.b.msg.get() #hello

a.update('bye')

print a.msg.get() #bye
print a.b.msg.get() #bye

If however this means too many changes to your code, what you could also do is to define a method msg(self) in every function with a msg, renaming msg to message or whatever. That way you would get the String by calling a.msg() for example, which would represent less change to your existing code. But I fear that there is no way of solving your problem without any change to your code.

Sample two, with a msg() method:

class B:
    def __init__(self, msg):
        self.message = msg

    def msg(self):
        return self.message.get()

class A:
    def __init__(self, msg):
        self.message = ref(msg)
        self.b = B(self.message)

    def update(self, msg):
        self.message.set(msg)

    def msg(self):
        return self.message.get()

a = A('hello')

print a.msg() #hello
print a.b.msg() #hello

A third solution I see would be to use a List instead of a reference class:

class B:
    def __init__(self, msg):
        self.msg = msg

class A:
    def __init__(self, msg):
        self.msg = [msg]
        self.b = B(self.msg)

    def update(self, msg):
        self.msg[0] = msg

a = A('hello')

print a.msg[0] #hello
print a.b.msg[0] #hello

a.update('bye')

print a.msg[0] #bye
print a.b.msg[0] #bye

In general you could use any other mutable object for this purpose, but take care of being aware that you can not reassign the mutable Object in some "child Class" like B is in your example.

Community
  • 1
  • 1
Theolodis
  • 4,977
  • 3
  • 34
  • 53
1

Why can't b keep track of a and use a.msg to get its msg?

class MockA:
    def __init__(self, msg):
        self.msg = msg

class B:

    def __init__(self, a):
        if isinstance(a, A):
            self._a = a
        else:
            self._a = MockA(a)

    @property
    def msg(self):
        return self._a.msg

class A:

    def __init__(self, msg):
        self.msg = msg
        self.b = B(self)

    def update(self, msg):
        self.msg = msg

a = A('hello')

print a.msg
print a.b.msg

a.update('bye')

print a.msg
print a.b.msg

b = B('here')

print b.msg

Prints:

hello
hello
bye
bye
here
Dan D.
  • 73,243
  • 15
  • 104
  • 123
0

You could use a property:

class A(object):
  def __init__(self, msg):
    self.b = B(msg)

  @property
  def msg(self):
    return self.b.msg

  @property.setter
  def msg(self, value):
    self.b.msg = value

I have this a little backwards compared to what you want. Here I always link a.msg to a.b.msg instead of the other way around, but it's really mostly semantics at that point. The changes should appear to propagate both directions so there should be little practical difference.

mgilson
  • 300,191
  • 65
  • 633
  • 696
  • I do not want to update self.b.msg: there is also self.c.msg, ..., an indeterminate number: the objects themselves are cached properties, which I can not enumerate. – blueFast Jun 11 '14 at 16:57