plowcow
plowcow

Reputation: 43

Is there a way to assign a reference to an object attribute in Python?

I would like to link an object's attribute permanently to the attribute from another object such that whenever the latter is changed, the former is updated automatically.

I've tried using simple assignment using = but this only assigns the value of the attribute at the time of assignment whereas I'd like to assign a reference to the attribute itself.

class firstObj:

    def __init__(self,n):
        self.name=n
        self.inA=1

    @property
    def outA(self):
        return self.inA+2

class secondObj:

    def __init__(self,n):
        self.name=n
        self.inA=1

    @property
    def outA(self):
        return self.inA*2




fo=firstObj("FO")
so=secondObj("SO")
fo.inA=so.outA

I'm trying to link the output of so to the input of fo

i.e.

so.inA=2
-->so.outA becomes 4
-->fo.inA becomes 4
-->fo.outA becomes 6

(and more generally is there a way in python to assign a reference to a variable to another variable rather than the variable's value so that a=b does not assign the value of b to a but rather a reference to b itself)

Upvotes: 2

Views: 2738

Answers (3)

gmds
gmds

Reputation: 19885

In native Python, there is no direct support for this. However, you can emulate this functionality with callbacks:

from functools import partial

class Teller:

    def __init__(self, name):
        super().__setattr__('_callbacks', {})
        self.name = name

    def register(self, attr, callback):
        if attr in self._callbacks:
             self._callbacks[attr].append(callback)

        else:
            self._callbacks[attr] = [callback]

    def __setattr__(self, attr, value):
        if attr in self._callbacks:
            for callback in self._callbacks[attr]:
                callback(value)

        else:
            self._callbacks[attr] = []

        super().__setattr__(attr, value)

class Asker:

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

    def bind(self, self_attr, other, other_attr):
        other.register(other_attr, partial(setattr, self, self_attr))
        setattr(self, self_attr, getattr(other, other_attr))

teller = Teller('Teller')
teller.teller_value = 5
asker = Asker('Asker')
asker.bind('asker_value', teller, 'teller_value')

print(f'teller_value is now {teller.teller_value}')
print(f'asker_value is now {asker.asker_value}')

teller.teller_value = 10

print(f'teller_value is now {teller.teller_value}')
print(f'asker_value is now {asker.asker_value}')

Output:

teller_value is now 5
asker_value is now 5
teller_value is now 10
asker_value is now 10

Basically, this requires an instance of Asker to bind one of its attributes to one of an instance of Teller; then, whenever that instance of Teller modifies that attribute, the corresponding attribute of the Asker instance will also change.

Upvotes: 1

Mahmoud Elshahat
Mahmoud Elshahat

Reputation: 1959

variables in python are just a dictionary mapping var name = value, you will notice this if you try print(globals()) it will print a dictionary mapping for all your module variables

if you need to achieve what you want you can do this in your classes definition as below:

class firstObj:
    def __init__(self,n, obj=None):
        self.name=n
        self._inA=1 
        self.obj = obj

    @property
    def inA(self):
        if self.obj is not None:
            self._inA = self.obj.outA
        return self._inA

    @property
    def outA(self):
        return self.inA+2 


class secondObj:
    def __init__(self,n):
        self.name=n
        self.inA=1

    @property
    def outA(self):
        return self.inA*2


so=secondObj("SO")
fo=firstObj("FO", so)
# fo.inA=so.outA

so.inA=2
print(so.outA) # becomes 4
print(fo.inA) # becomes 4
print(fo.outA) # becomes 6

output:

4
4
6

Upvotes: 1

Ned Batchelder
Ned Batchelder

Reputation: 375574

You cannot do this. Python does not have a way for a name to refer to another name, which is what you want (you want fo.inA to refer to so.outA). Names can only refer to values. This talk has more details: Python Names and Values.

Upvotes: 0

Related Questions