5

I readily admit that I might be referring to this by the wrong name or wanting something that I shouldn't need. I'm just hung up on wanting this to be a thing: linking together object attributes so that if I change one, that I'll change the other. Always. Until I do something to disconnect them on purpose.

So let's go straight into an example:

class Example:
    def __init__(self):
        self.name = ["Thing1"]

a = Example()
b = Example()
c = Example()

a.name = b.name

b.name.append("Thing2")
b.name.remove("Thing1")

So now a.name and b.name are connected so that they both are names for the same mutable, which now reads ["Thing2"]. And further, c.name is a name of a different value ["Thing1"].

I've now accomplished what I want to do: a.name and b.name are linked. But this is tremendously kludgy.

I could make a custom list Class and create some methods to switch out the thing in the list and return an immutable when called, so that it looks more like a normal immutable. But all that seems like a hack.

Any thoughts on making this cleaner? Or am I simply wanting a bad thing?

3
  • 1
    You can wrap name in an object with reference semantics; ex. a.name = ["foo"]; b.name = ["bar"]; a.name = b.name; a.name[0] = "baz"; print(b.name[0]) # baz Commented Mar 12, 2015 at 20:44
  • 3
    However, since x has a value change, y is now 4. -- incorrect. y still references 8 -- try it. Commented Mar 12, 2015 at 20:45
  • @jedwards, I stand corrected, and will change my example as appropriate. Commented Mar 12, 2015 at 21:18

1 Answer 1

5

In general, when you do re-definition

x = 8
y = x
x = 4   # <-- Re-definition of x

x and y will no longer reference the same thing (try print id(x) == id(y)).

Consider how re-definition works:

x = 8
y = x
x = 4
print y     # Still 8

x = "foo"
y = x
x = "bar"
print y     # Still 'foo'

x = [1,2,3]
y = x
x = [4,5,6]
print y     # Still [1,2,3]

What you can do, with mutable types is to change the object "in place".

For example,

x = [1,2,3]
y = x

Now, both x and y reference the same list (print id(x) == id(y)).

Now we can replace the elements, by using only x:

x[:] = [4,5,6]

Here, we're not re-defining x, we're just modifying the values in the list.

In this case, the changes will be reflected in y:

print y     # Now [4,5,6]

Note:

print id(x) == id(y)  # True

If you want the ability to modify in-place an immutable type, you can "fake" it by wrapping it in a list:

x = ["Foo"]  # Strings are immutable
y = x
x[0] = "Bar"
print y               # ['Bar']
print id(x) == id(y)  # True

There will be a million questions on here about mutable / immutable types -- you should review them.


Edit We could accomplish what you want with your bind line with a combination of mutable types and properties:

First, we create a "fake" mutable string class:

class FakeMutableString(object):
    def __init__(self, s=""):
        self.s = [s]

    def __str__(self):
        return self.s[0]

    def get(self):
        return self.s[0]

    def set(self, new):
        self.s[0] = new

And to see how it works

x = FakeMutableString("Foo")
y = x
x.set("Bar")
print y             # Prints 'Bar' -- Change in x is reflected in y 

Note that we don't re-assign to x -- instead we use the x.set() method. If we re-assigned to x, we'd ruin everything (as we talked about above).

Then, we can modify your Example class to the following:

class Example(object):
    def __init__(self):
        self._name = FakeMutableString()

    @property
    def name(self):
        return self._name.get()

    @name.setter
    def name(self, new):
        self._name.set(new)

Example instances have an attribute _name attribute that references a FakeMutableString object.

But with properties, we can pretend we're providing direct attribute access, while hiding the actual implementation.

So we can do something like:

# Create Example instances
a = Example()
b = Example()
c = Example()

# Set their name attributes 
#   ( this actually calls <Example>._name.set() )
a.name = "ThingA"
b.name = "ThingB"
c.name = "ThingC"

# Access and print their name attributes
#   ( this actually calls <Example>._name.get() )
print a.name, b.name, c.name    # ThingA ThingB ThingC

# We can't bind like you suggested, but we can change what b._name points to
#   So here, we change b._name to point to the FakeMutableString a._name points to
b._name = a._name

# Now when we print the names, we see something different
print a.name, b.name, c.name    # ThingA ThingA ThingC

# And we can make a change in a, and have it reflected in b
a.name = "CommonName"
print a.name, b.name, c.name    # CommonName CommonName ThingC

# And vice-versa
b.name = "StillCommon"
print a.name, b.name, c.name    # StillCommon StillCommon ThingC
Sign up to request clarification or add additional context in comments.

5 Comments

OK, so my bad on not using a mutable type, such as the list in my example, but I don't think you really addressed my core question, but I'll key off your example: how do I make it so that if x = [1,2,3] and y = x, that I can mutate y to change x?
If you mutate y, it will change x -- for example y.append(4). If you re-assign / re-define y, they will then reference different things. Does this help?
Ah! Yes! And even though I've read all sorts of stuff about mutable vs. immutable types, I've for some reason still held onto the idea that y has a reference to x, not that y and x both share the same mutable value. I guess I need to massively re-frame my question.
so thanks by the way for clearing up my misunderstanding. I've been working in this language now for 6 months, and somehow I missed this vital distinction.
@raiderrobert I updated my answer to illustrate one way you could accomplish you "binding" goal -- it may be too much, but the properties language feature are worth looking into in general

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.