Understanding the difference is critical for metaprogramming:
__getattribute__(self, name): Called unconditionally every single time an attribute is accessed (obj.x). Overriding this is extremely dangerous because if you try to accessself.datainside it, it will trigger itself, instantly causing aRecursionError.__getattr__(self, name): The safety net. It is called only if the attribute is not found by normal means (i.e., it's not an instance variable and not in the class tree). It is perfectly safe to use for building dynamic wrappers because accessingself.datainside it will hit the normal lookup first.
Coined by Bertrand Meyer: “All services offered by a module should be available through a uniform notation, which does not betray whether they are implemented through storage or through computation.”
In Python, this is achieved using the @property decorator. You can start by exposing a simple raw attribute obj.balance. Later, if you need to add validation, you can change it to @property def balance(self) without breaking any code that uses your class.
__new__(cls, *args)is the true constructor. It allocates the memory and returns the newly minted object.__init__(self, *args)is the initializer. It receives the object returned by__new__and attaches data to it. When building dynamic factory classes (where you might want to return an instance of a different class depending on the input), you must override__new__.