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.
Related
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.
I've written a script that solves sudoku problems.
To model each slot of a grid, I have in a first time defined Slot and Grid classes like this (complete code elipsed for the sake of simplicity :
class Slot():
def __init__(self,grid):
self.grid = grid
self.values = list(range(9))
def pos(self):
return self.grid.index(self)
class Grid(list):
def __init__(self):
for i in range(9*9):
self.append(Slot(self))
Like this, I can define method for my Slot class using self.pos() and self.values(). For example :
g = Grid()
g[5].pos() -> returns 5, OK !
Now that my full script works just fine, I want to refactor it, and, as a Slot is basically a list belonging to a Grid, I decided it would be great for my Slot to subclass list, like this :
class Slot(list):
def __init__(self,grid):
self.grid = grid
self.append(list(range(9)))
def pos(self):
return self.grid.index(self)
class Grid(list):
def __init__(self):
for i in range(9*9):
self.append(Slot(self))
g = Grid()
g.index(g[5]) -> returns 0, KO !
I've tried to init the list first ie: super().init(list(range(9)), and also a few variations, but nothing seems to work.
What am I missing ?
PS : the g.index(g[5]) is just to describe, I'm aware it's pointless. I'm using this logic in methods inside my objects (self.pos(), etc.)
By making Slot a subclass of list you also make the comparison between Slot instances use the logic defined for lists (since you haven't overridden that).
Since all Slots contain the same value:
self.append(list(range(9)))
g.index() will simply match the first entry the grid yielding 0 as the result.
When you inherited from object (as Slot did in your first example) all instances compared unequal to themselves as is defined in the Python Reference (unless logic is implemented that dictates otherwise).
In short, you'll need to redefine the comparison methods if you need the slots with similar items to be treated differently when compared. In addition to that, you might want to reconsider sub classing from list and, instead, opt for UserList from collections.
I read multiple article about OOP in python but I didn't find the answer.
here is my sample code as a example :
class Point(object):
"""basic point"""
def __init__(self, x, y):
self.x = x
self.y = y
class Circle(object):
"""basic circle object"""
def __init__(self,center,radius):
self.center = center #should be a point object
self.radius = radius
coord = Point(1, 2)
a = Circle(coord, 4)
b = Circle(4, 5)
If I understand correctly this is valid python code but the circle "b" doesn't have a Point object as the center. if there is a method in the circle object who use the center object to do a calculation (to calculate the circle's area for example) it will fail for the b object. Do I have to enforce type or is it the programmer responsibility to give a expected object type at the instantiation?
As others have said, it is up to you to enforce typing.
However, Python widely uses the concept of duck typing, which means in your case, you don't necessarily need a Point object for the center, you just need something that behaves the same as your Point class. In this simple example, Point doesn't provide any methods; it's simply a class whose objects will have x and y attributes. That means your Circle could accept any object for its center as long as it provides x and y attributes, that is, provides the same interface as Point.
This means that the most important thing to do is document what interface your class provides, and what each function or method expects from its arguments.
It is up to you to enforce types, and up to the caller to provide the proper data.
One of the underlying philosophies of the python community is that we're all responsible programmers. If it is critical that the type is enforced against accidental or malicious mistakes, you must build that into your objects.
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 = []
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.