Python, update cached property when field of attribute changes - python

I'll try to take a dummy example to explain my goal: updating a cache (defined with #cached_property) computed from an instance attribute.
Let's say I have an object AllCircles that is composed of a tuple of Circles. Each Circle is defined with its radius. I want to cache the value of the biggest radius as a property of AllCircles. Here's what I did:
from functools import cached_property
class Circle():
def __init__(self, radius: float):
self.radius = radius
class AllCircles():
def __init__(self, circles: (Circle)):
self._circles = tuple(circles)
#property
def circles(self):
print("Call getter")
return self._circles
#circles.setter
def circles(self, value):
print("Call setter")
self.__dict__.pop('max_radius', None)
self._circles = value
#cached_property
def max_radius(self):
print("Compute cached property radius")
return max([_.radius for _ in self.circles])
I can define an object AllCircles from a set of Circles, and call max_radius to get the biggest value.
> my_circles = AllCircles([Circle(4), Circle(3)])
> my_circles.max_radius
Compute cached property radius
Call getter
4
Which is ok. If I call the function again, I get:
> my_circles.max_radius
4
as nothing's printed, I guess the cache is called, that's ok.
If I set the circles:
> my_circles.circles = [Circle(5), Circle(0)]
> my_circles.max_radius
Call setter
Compute cached property radius
Call getter
5
I again get the right value, because setting a new object circles calls the setter, and its self.__dict__.pop()method empties the cache.
However, if I directly modify the radius of a circle, I don't empty my cache:
> my_circles.circles[0].radius = 9
> my_circles.max_radius
Call getter
5 # instead of 9
How can I change my code to update my cache when an attribute is modified directly ?

In a way, what you're asking for seems a bit unnatural to me. Indeed, when you set my_circles.circles[0].radius = 9, you don't modify anything about your instance my_circles, so there is no natural way for it to know it should behave differently. Furthermore, the poor little Circles have no idea that they're part of something bigger, so they couldn't tell my_circles.
I guess one way you could solve your problem would be to make the radius part of the information contained in my_circles (or more generally, directly inform the Circles that they're not alone anymore by connecting them to my_circles).
Another way would be to force some checks from my_circles upon every max_radius request. Here it would be silly because it'd be equivalent to recomputing the max, but on more complex operations, it might just to check some hash for example.
I don't know if that helps, maybe there are other and better ways to think about this.

Related

Add __hash__ and __eq__ to imported type (tkinter DoubleVar) to put it in a dictionary

I imported some class from other modules (in my application it is a tkinter DoubleVar), and would like to use them as a key in a dictionary. But the class does not have __hash__ and __eq__ defined, so it does not work.
Is there any way to add those methods? I come from C#, where there are extension methods, but I just looked it up and I did not find a way to replicate that in python.
Example:
import tkinter as t
root = t.Tk()
var_A = t.DoubleVar(root)
var_A.set(10)
bounds = {} #I need to store the bounds for some optimization algorithm
bounds.update({var_A: [-10, 10]}) #Throws exception : TypeError: unhashable type: 'DoubleVar'
Can I somehow implement hash functionality in an imported type?
Or do I have to write a wrapper class that allows me access to the .get() and .set() methods while implementing __hash__?
Edit: I will try to explain why I needed to store the bounds: I have an interactive model with fit functionality. The user is able to chose between different algorithms. All my old algorithms (from scipy.optimize.minimize) did not need bounds to work, but since I wanted to implement black box optimization (using this repo) and the algorithm needs bounds, I needed some way to get the bounds of the parameters. But since there are about 40 different parameters and over 1500 lines of code, I was searching for a way to implement this without too much effort. Since all variables (that are parameters for the fit) are stored in self, I wanted to create a kind of "lookup-table" for the bounds. You are right, this is not the proper way to do things, but I found a better solution now anyways. Thank you
You can assign methods (including __hash__ and similar) to existing classes:
# a class without explicit __hash__
class A:
def __init__(self,i):
self.i = i
a1 = A(1)
print(hash(a1)) # default hash
# define a "free" function ...
def A_hash(self):
return hash(self.i)
# ... and "binds" it to class A
A.__hash__ = A_hash
a2 = A(1)
print(hash(a2)) # new it has user defined hash
EDIT: updated code with initially unhashable object and dictionary test:
# a class without explicit __hash__
class A(list):
def __init__(self,i):
self.i = i
a1 = A(1)
# print(hash(a1)) # would be TypeError: unhashable type: 'A'
# define a "free" function ...
def A_hash(self):
return hash(self.i)
# ... and "binds" it to class A
A.__hash__ = A_hash
a2 = A(1)
print(hash(a2)) # new it has user defined hash
d = {a2:1}
print(d)
I fixed my issue by writing a wrapper class that implements __hash__ and __eq__. The class also stored the variable and provided the .get() and .set() so I did not need to change all of my code.
Also, I later got rid of the idea of storing the vars in a dict with the bounds, and just stored from_ and to in the wrapper, which makes much more sense anyways

What happens when we give a function definition in a class in Python

I'm learning python on my own out of interest, but I face a lot of questions when I get into "class".
In this post:
What happens when you initialise instance variables outside of __init__
The answer by Dietrich Epp gave out a detailed explanation of what really happens in a class.
Dietrich said that the three cases in the editor are the same.
However, when I type the following things in command line:
# Set inside __init__
class MyClassA:
def __init__(self):
self.x = 0
objA = MyClassA()
# Set inside other method
class MyClassB:
def my_initialize(self):
self.x = 0
objB = MyClassB()
objB.my_initialize()
# Set from outside any method, no self
class MyClassC:
pass
objC = MyClassC()
objC.x = 0
print(objA.x)
print(objB.my_initialize())
print(objC.x)
It turns out that the middle of the printing result is 'none' which means that instead of giving out 0, objB.my_initialize() return to none.
Can anyone help explain this?
It seems that the three cases are somehow different to me...
For objB, you need to call the function first, then get the value of x. So your code goes from
print(objB.my_initialize())
to
objB.my_initialize()
print(objB.x)
This is because objB.my_initialize() only sets the value of x, and doesn't return anything, hence why you are getting None.

Getter/setter but for nested attributes?

Suppose I have two classes. The simple Square class:
class Square:
def __init__(self, side):
self.side = side
And the slightly more complex MyClass class:
class MyClass:
def __init__(self, square=None):
if square is None:
self.square = Square()
else:
self.square = square
self.rounded_side = round(self.square.side)
I instantiate a MyClass object like so:
myObj = MyClass()
In this situation, how can one achieve the following behavior?
Changing myObj.rounded_side to X, automatically changes myObj.square.side also to X.
Changing myObj.square.side to X, automatically changes myObj.rounded_side to round(X).
If possible, in a way that doesn't require any modifications to the Square class (this is a simplified version of the problem I'm currently facing; in the original version, I don't have access to the code for Square).
What I tried so far:
My first attempt was to transform rounded_side into a property. That makes it possible to obtain behavior 1. However, I fail to see how I can transform square also into a property, in a way that makes it possible to obtain behavior 2.
I also thought about making MyClass inherit from Square, so that both attributes are in the same depth, but then I'd lose some the desired structure of my class (I rather have the user access myObj.square.side, than myObj.side)
If someone is interested, the actual problem I'm facing:
I'm writing a game in pygame, and my Player class has an attribute for its position, which is an array with two floats (2D position). This is used for determining where the player is, and for deciding how to update it's position in the next update step of the game's physics.
However, I also want to have a Rect attribute in the Player class (which holds the information about a rectangle around the player's image), to be used when displaying the player in the screen, and when inferring collisions. The Rect class uses integers to store the position of the rectangle (pixel coordinates).
So as to be able to store the position information of the player in a float, but also use the Rect class for convenience, I thought about having this dependency between them, where changing one alters also the other accordingly.
As you've said, make rounded_side a property, but have it access the value in self.square for both getting and setting.
#property
def rounded_side(self):
return self.square.side
#rounded_side.setter
def rounded_side(self, side):
self.square.side = side
Now, setting rounded_side will use the setter which sets the value in square; setting the value on square directly will mean that it would be looked up from there by the property getter.

Updating Class Instance Attributes via SuperClass update() method

I'm in the midst of coding a simulation for studying UAV interaction in swarms and obstacle avoidance scenarios. The issue I'm having currently is in getting the vehicles to update their positions. Basically, I have a base class which contains the update() method which does the calculation to arrive at the new position and velocity. The actual objects in the sim code are instances of a subclass of this, and in the subclass's update() method, all I do is update the acceleration vector and call super().update(). However, the values are retained after the function call. I assume this is just a lack of knowledge of Python on my part, as I'm just starting with it (coming from C++ for many years). The searches I've done for pass by reference and such are giving me good information, but so far I can't get an answer to this specific problem. Here's the code:
[EDIT] Per jonrsharpe's request, I've written out a minimal example that encapsulates the problem I'm having. Here's the minimal code:
class UpdateTester:
x = [0,0]
def update(self):
for elem in self.x:
elem += 1
class SubClassTester(UpdateTester):
def update(self):
super(SubClassTester,self).update()
a = SubClassTester()
for i in range(1,5):
a.update()
print(a.x)
So basically, per my(admittedly limited) understanding, I should get an output which shows increments to the list a.x. However, my output from running this example shows repeated [0,0]'s as output.
Integers are immutable in Python therefore for elem in self.x: elem += 1 does nothing. It doesn't change self.x. If you want to change self.x:
for i, value in enumerate(self.x):
self.x[i] = value + 1
Also UpdateTester.x is a class variable. It is the same for all instances. Lists are mutable in Python therefore if you call .update() on any instance of UpdateTester class then you change the list for all of them. To create per instance list instead, put it in __init__():
class UpdateTester(object):
def __init__(self, **kwargs):
super(UpdateTester, self).__init__(**kwargs) # for multiple inheritence
self.x = []

Replacing variable with function/class indicating dynamic value

In my program, I draw some quads. I want to add the functionality for them to scale up, then down, then go back to being static (to draw attention). In the quads I have:
self.scale = 10
Making scale change according to sin would be nice. But adding frequency, amplitude and logic to my already bloated quad class is something I take as a challenge to avoid.
Something like this:
class mysin:
def __init__(self):
self.tick = 0.0
self.freq = 1.0
self.ampl = 1.0
def update(self, amount):
self.tick += amount
def value(self):
return math.sin(self.tick)
That class would also add itself to the logic system (getting update calls every frame). I would then do:
quad.scale = 10 # for static quad
quad.scale = mysin() # for cool scaling quad
The problem is that some calculations expect scale to hold a value. I could of course add another class where value() returns a (previously saved) constant value and adapt all the calculations.
What I want to know now is... does this have a name, is it a valid technique? I read the wiki article on functional programming and this idea sprung to mind as a wacky implementation (although Im not sure it qualifies as FP). I could very well have been driven mad by that article. Put me back in line fellow coders.
The distinction between
quad.scale= 10
and
quad.scale= MySin()
Is minor. Within the Quad class definition the "scale" attribute can be a property with proper getter and setter functions.
class Quad( object ):
#property
def scale( self ):
return self._scale
#scale.setter
def set_scale( self, value ):
# handle numeric and MySin() values appropriately.
Alternate version with the explicit property function (which I prefer).
class Quad( object ):
def get_scale( self ):
return self._scale
def set_scale( self, value )
# Handle numeric and MySin() values
scale = property( get_scale, set_scale )
Any other class should NOT know or care what type of value scale has. If some client does this
quad.scale * 2
Then you have design issues. You haven't properly encapsulated your design and Quad's client classes are too friendly with Quad.
If you absolutely must do this -- because you can't write a method function of Quad to encapsulate this -- then you have to make MySin a proper numeric class so it can respond to quad.scale * 2 requests properly.
It sounds like you want your quads to be dumb, and to have an animator class which is smart. So,here are some suggestions:
Give the quads an attribute which indicates how to animate them (in addition to the scale and whatever else).
In an Animator class, on a frame update, iterate over your quads and decide how to treat each one, based on that attribute.
In the treatment of a quad, update the scale property of each dynamically changing quad to the appropriate float value. For static quads it never changes, for dynamic ones it changes based on any algorithm you like.
One advantage this approach is that it allows you to vary different attributes (scale, opacity, fill colour ... you name it) while keeping the logic in the animator.
It's sort of like lazy-evaluation. It is definitely a valid tecnique when used properly, but I don't think this is the right place to use it. It makes the code kind of confusing.
It sure is a valid technique, but a name? Having an object.value() instead of an int? Uhm. Object orientation? :)
If the methods that use this value requires an integer, and won't call any method on it, you could in fact create your own integer class, that behaves exactly like an integer, but changes the value.

Categories

Resources