Pygame does not use any special techniques in code flow, such as callback, in rectangles - it's just a well-planned example of using properties.
Next: Python has a mechanism called a "descriptor protocol" - which allows access to attributes of an instance or class to be customized. It works if a class attribute is an object that itself has a method of __get__
, __set__
or __del__
.
To further facilitate the use of this mechanism, the language has the built-in property
, which is usually used as a decorator.
In the case of a rectangle such as pygame, it is a matter of choosing which data is to be stored internally in the rectangle, and then writing the code of the getters and setters in the form of properties for all the properties that we want to expose. For example, to keep "left, top, width, height" as real attributes, and expose the property, which can be changed "right", all that is needed is:
class Rect:
def __init__(self, seq):
self.top, self.left, self.width, self.height = seq
@property
def right(self):
return self.left + self.width
@right.setter
def right(self, value):
self.width = value - self.left
The legal of using descriptors, even with the simplifier property is precisely that the user of his object does not notice the difference of simply reading or setting an attribute.
In the specific case of pygame.Rect, the class code is written in C, and then the getters and setters are less generic to write than with a pure Python descriptor. The source code is online here:
link
The getters and settesr functions of the properties begin to be written from line 1247, are associated with the properties themselves in the struct that starts at line 1625 and are ultimately associated with the class at line 1696. You can see that the so that you can inspect multiple values of the rectangle, and change some all of them only work in the "real" properties "x, y, w, h", as I did in the example above for the "right" property.
(This Pygame rect.c file is one that could probably be rewritten in Cython and get about 1/5 the same size and the same performance - the interesting thing about it being in native code, is that so the collision check functions can easily check hundreds of rectangles per frame without "tiring" a modern CPU.