Avalon
Welcome!!
Class Magic Methods in Python

__getattribute__ method

This method usually implements a class's getter to get whatever you request (attributes and methods) from an object/a class.
I don't know for sure the difference with __getattr__.

A common way of implementing this method is as follows:

Question: What is the following codes' output?

The answer is 1.

And what about the results when we change the test's structure to this:

The answer is 10.

And the reason is 1. test inherits from object, so super().__getattribute__(name) is equivalent to object.__getattribute__(self, name). 2. the first class doesn't implement the case when non-built-in members (which in this case is array) are retrieved, so it automatically returns None whose size is 1.

Sometimes, there are many other complex implementation of an attribute getter.

__iadd__ and __add__ method

Methods like __iadd__are called to implement augmented arithmetic assignments. Specifically, these two methods are the implementations of the operators += and +. To overload these 2 operators, we must implement and override __iadd__ and __add__ methods of a class. If __iadd__ is not implemented, the augmented assignment += falls back to the normal methods. Originally, x += y is equivalent to x = x.__iadd__(y). When __iadd__ is not implemented, __add__ or __radd__ will be the alternative method to overload +=. We can look at some examples below

If __iadd__ is to be implemented, then there are a few things to watch out for. Typically, __iadd__ should attempt to do the operation in-place (modifying self) and return the result (which could be, but does not have to be, self). And consequently, __iadd__ is implemented for mutable objects like listand dict. For immutable objects like int and string, __iadd__ simply uses __add__ or __radd__ as alternatives to maintain the immutability. Let's look at a few examples to get familiar with this idea.

The above example clearly displays the difference between __add__ and __iadd__ on mutable lists. a += [2] changes what a is referencing but a + [3] simply returns a new list without changing a or [3]. The assignment = only makes a reference the new list but doesn't change what b references. Also, for lists, a += [3] is just like a.append(ele) because they both mutate the list in-place and maintain a's reference while a = a + [3] returns a new list and performs an assignment and finally changing a's reference.

For data of int types, __iadd__ is the same as __add__. Then let's take a look at an example with unexpected errors concerning __iadd__.

As we can see, += operations to a mutable list fails because it is at the same time an element of a immutable tuple. But if we take a look at a_tuple[0] and we will find a_tuple[0] has become [1, 2, 3] successfully. Why? If we recreate this process with intermediate results, we will get

Simply speaking, the object a_tuple[0] references (which is [1, 2]) has been smoothly mutated to [1, 2, 3] because of __iadd__. However, a_tuple[0] = result serves to mutate the reference of a_tuple's first element while it is prohibited by Python to maintain tuple's immutability. However, we can make some minor changes to make this happen

This actually doesn't change any reference within that tuple because the only different thing is the list instead of the reference. And this can be proved as below

In a summary, you cannot make the tuple reference different things but you can change what is inside the list as you leave the reference alone. For more info, please refer to the official docs Python Assignment Statements.