Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Python, can I bind a variable to a function/expression so that it automatically updates?

Tags:

python

Let's say I've got a variable A that is the result of a function/expression F. F in it's turn has a number of other variables in it, let's say X,Y and Z.

Is it possible to bind A to F so that whenever X,Y or Z changes, A will be updated automatically?

What I want to avoid is that everytime X,Y and Z changes, I have to remember to update A explicitly in the code. I also don't want to call the function everytime I want to use the A.

Example (as per requested): I've got the following function:

def calcHits():
    return sum(hitDiceRolls,level*modList['con']) 

and in my program (outside of the function), I've got a variable called hitPoints (yes, it's a roleplaying game program). Whenever the variables that's used in the function is changed, I want hitPoints to change as well.

like image 608
McWolfe Avatar asked May 27 '26 14:05

McWolfe


2 Answers

The typical way to do this in Python would be to use a class:

class ExpressionBinder:
    def __init__(self, f):
        self.f = f
        self.x = 0
        self.y = 0
        self.z = 0

    @property
    def result(self):
        return self.f(self.x, self.y, self.z)

You can use it like this:

def f(x, y, z):
    return x**3 + y**2 + z

b = ExpressionBinder(f)
b.x = 1
b.y = 2
b.z = 3
print(b.result)
like image 148
Greg Hewgill Avatar answered May 30 '26 14:05

Greg Hewgill


There is no way in Python to automatically rebind a name in global or local scope in response to other names being rebound. However, it should be possible to make a class that can keep track of some values and have a member function that returns the value you called A. And, as @Alok pointed out, you can use property descriptors to make a member name that implicitly calls a function to return its value, so you can hide the function and treat the name like a plain old name.

class Trk(object):
    """Track some values and compute a function if any change"""
    def __init__(self, name, fn, **objects_to_track):
        def _trk_fn(self):
            if any(self.__dict__[x] != self.original_objects[x] for x in self.original_objects):
                self.value = self.saved_fn(self.__dict___)
                # now that self.value is updated, also update self.original_objects
                for x in self.original_objects:
                    self.original_objects[x] = self.__dict__[x]
            return self.value

        self.original_objects = objects_to_track  # make reference copy
        self.__dict__.update(objects_to_track)
        self.name = name
        self.saved_fn = fn
        self.fn = self._trk_fn()
        self.value = self.fn()

I'm sorry but I am very tired right now, and I canot finish this example. I didn't test it either. But this shows one way to track values, and if they are different, do something different. You use it like this:

# want to track x, y, z
trk = Trk(x, y, z)
trk.fn() # returns up-to-date value

trk.x = new_value
trk.fn() #detects that trk.x changed and computes new trk.value

If the above works, you can use the property descriptor stuff to bind a name such that an attempt to read a value from the name will call self.fn()

EDIT: Oh, it's important that when self.value is updated, self.original_objects should be updated. I've added code to do that.

And now I'm going to sleep!

like image 21
steveha Avatar answered May 30 '26 12:05

steveha



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!