0
class Bar:

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

class Foo:

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

x = Bar(2)

# we want x.n to change whenever y.l changes
y = Foo(x.n)

# I'd like x.n to become 3 as a result
y.l += 1

print(x.n)
>>> 2

How do I make it so that I can change x.n from within y? Is there no easy way to do this without adding more functionality to x?

Andrew Pampuch
  • 65
  • 1
  • 10
  • No, you can't do that with immutable objects. – jonrsharpe Feb 09 '16 at 23:14
  • You can do it with indirection, but chances are you shouldn't. What's the problem you're actually trying to solve here? – Chad S. Feb 09 '16 at 23:17
  • you have to use a setter function for y, so you can't say y.l += 1, you have to say y.set(x, 1). Then y's setter should take care of updating both x and y. class Foo: def set(var, val): y.l += val var.n = val – Joseph James Feb 09 '16 at 23:19
  • I'm making a buff system for a text based RPG. The way I want it to work is to have each Buff be its own class, with functions to apply and remove the buff. One attribute (the one I'm having problems with) of the Buff is nested list. Each of the inner lists (and I know tuples are better) has two values: [0] being the stat you want to change (which is an attribute of another class we'll call Stats here), and [1] being the amount you want to change [0]'s value by. At this point, I'm thinking of just writing functions to change the player's stats within Stats itself. That seems easier. – Andrew Pampuch Feb 09 '16 at 23:30

2 Answers2

1

Well, as other people have pointed out already, the problem comes down to the fact that you're dealing with imutable objects. Having realized that, you should either use a mutable type, or use a callback to manage updates. Adriano Abrantes gave an example of an alternative using mutable types, but if that's not what you're looking for, here's an example of potential callback system:

# bad habit to be using the old style class definition (i.e. `class Foo:`)
# the `@property` decorator wont work if you use the old style

class Sync(object):

    def __init__(self, name, value, *dependants):
        """Sync the attribute `name` on all `dependants` when `self.value` is updated"""
        self._name = name
        self._value = value
        self._updating = False
        self._dependants = list(dependants)
        self._update_dependants()

    @property
    def value(self):
        return self._value

    @value.setter
    def value(self, x):
        if x != self._value:
            self._value = x
            self._update_dependants()

    def _update_dependants(self):
        self._updating = True
        for d in self._dependants:
            if getattr(d, self._name) != self.value:
                if isinstance(d, Sync):
                    if not d._updating:
                        setattr(d, self._name, self.value)
                else:
                    setattr(d, self._name, self.value)
        self._updating = False

    def add_dependant(self, other):
        self._dependants.append(other)
        self._update_dependants()

    def del_dependnant(self, other):
        self._dependants.remove(other)

    def __repr__(self):
        return "Sync('"+self._name+"': "+repr(self.value)+")"

    def __eq__(self, other):
        if isinstance(other, Sync):
            return self.value == other.value
        else:
            return self.value == other

    def __ne__(self, other):
        return not self.__eq__(other)


s1 = Sync('value', 2)
s2 = Sync('value', 1)
print('setup the Sync objects:')
print('>>> ' + repr(s1) + (' == ' if s1 == s2 else ' != ') + repr(s2))

s1.add_dependant(s2)
s2.add_dependant(s1)
print('add sync objects as dependants of each other:')
print('>>> ' + repr(s1) + (' == ' if s1 == s2 else ' != ') + repr(s2))

s1.value += 1
print('check that value changes transfer from one to the other:')
print('>>> ' + repr(s1) + (' == ' if s1 == s2 else ' != ') + repr(s2))

If this still doesn't satisfy you, I'd look into something called Traitlets. It's a package from Ipython that's part of Project Jupyter which enforces type checking, streamlines callback systems, and makes app configuration easy. I've been contributing to the project for a while, so if you have any questions about Traitlets feel free to ask me here or post them on Gitter and the dev team will answer them.

Community
  • 1
  • 1
rmorshea
  • 832
  • 1
  • 7
  • 25
0

I believe what you are doing goes against many concepts of OOP, but you could use a list:

class Bar:
  def __init__(self,n):
    self.n = [n]

class Foo:
  def __init__(self,l):
    self.l = l

x = Bar(2)
print(x.n)
>>>[2]

y = Foo(x.n)
y.l[0] += 1
print(x.n)
>>>[3]
xuva
  • 144
  • 2
  • 9