 Let's look now at how method calls work in Python. The goal here is when we invoke a method that say x.foo, x somehow has to be passed as an argument to the method. So assume we have some class called erin and that class has a function in an attribute named foo. If we then create an erin instance and assign it to a variable x and write x.foo, that expression x.foo is looking for the attribute foo on the object of x, which here is an erin instance, and what it returns is not the function found in the class erin, but it takes that function and it wraps it in another kind of object called a method and it returns that method bound to that object in x. This is a special behavior only for functions. In the class erin, the methods are expressed as just attributes which are functions, but then when we look them up via instances of that class, what we get back are method objects. What a method object consists of is simply a reference to the function and a reference to the instance here x. When a method object is invoked, it invokes its function and passes on any of the arguments that were supplied, but it inserts the instance as the first argument. So here when we invoke the method object with the arguments 3 and 5, that invokes the function from foo, but with x as the first argument, so the arguments to the function are x, 3, and 5. What methods are is really just an elaborate mechanism to somehow get that object before the dot, in this case x, passed as the first argument. When we create a class in Python, the convention is that any of the functions we give it for methods, the first parameter by convention expects to receive the instance. So when you see in code what looks like a method call, say x.foo with the arguments 3 and 5, what's happening is the expression x.foo first returns a method object, that method object wraps x and the function, and then the invocation of the method object causes the function to get called, with x as the first argument followed by all the remaining arguments here 3 and 5. Now it turns out that most of the operators in Python are really method calls in disguise. For example, x plus y, Python implicitly translates that into an invocation of the method add, add with two underscores on both sides. And the first operand to the plus sign becomes the instance of this method call, and the second operand gets passed into the method. So x plus y becomes x.underscore underscore add underscore underscore with the argument y. And it's the same idea for the other arithmetic operators. Subtraction becomes sub, multiplication becomes mole, division becomes div. And the reason for the underscores is that Python has a convention whereby any attribute name, which is generally not meant to be accessed directly, such names by convention start and end with two underscores. So the intention generally is that the add method here is not invoked directly, rather we invoke it by using the plus sign operator. Now usually when you use arithmetic operators, you use them on numbers. So imagine here that x is a number, it's an integer. And so the method add here is being invoked on an instance of the integer type int. If you look in the int class, you will find these attributes. You will find underscore underscore add underscore underscore, you'll find mole, you'll find div. Here's the functions of these attributes in the int class, which get invoked when we use the arithmetic operators on integers. Now you're probably wondering two things. First of all, if the addition operator ultimately invokes a function, what happens in that function to do the addition? Because that function just can't use the addition operator because the addition operator would call that function again. It doesn't make any sense, that would be circular. So the answer here is that the integer class has functions which are specially created by the interpreter. They're not regular Python functions, they're what are called built-in functions. They don't contain Python code, obviously. In the C Python implementation, for example, these functions likely contain C code. So that explains that mystery. The other very puzzling part about these operator methods is why have them at all? Why not just define say the addition operator, the plus sign, to directly do addition? That's how it's done, say, in JavaScript. So why doesn't Python do the same? Well, the idea is that if operators really invoke methods, that means when you create your own classes, you can define methods of these names, like say add, and thereby give instances of your class defined behavior with the standard operators. So if our class, Aaron, has a function in its attribute underscore underscore add underscore underscore and that function takes two arguments, then that gives Aaron instances defined behavior with the plus sign operator. So if you write x plus y, where x is an Aaron object, that'll work. It'll invoke the add method of the Aaron class. Now the question still is what the hell really is the point of this? Why is it so great to have your own classes work with the standard operators? I for one think it's really quite pointless. It's really just making a stylistic allowance, whereby in certain circumstances you may make your own data types work with the standard operators. And for whatever reason, you don't like the idea of giving that operation a standard attribute name and invoking it like any other method. A better argument for this arrangement, though, is that it gives the language pervasive polymorphism. If method calls can be polymorphic, they can change based upon the arguments and the type of the object. Well, then why shouldn't operators also be polymorphic? Whether or not you object to this feature, it does exist. And it applies not just to these arithmetic operators, it applies actually to most operators, including the subscript operator on dictionaries and lists we saw earlier. So when you get or set items, say, from a dictionary, those are really method calls in disguise. They are called getItem and setItem respectively, both surrounded in double underscores. But for now, we won't enumerate all of the different operator methods. We'll save that for the supplementary material. Now, actually, there is one last thing we need to say about these operator methods. And that is that the attribute search behavior gets slightly modified when using these operators. For example, say, again, we have a class erin and we have an instance of erin A. And then we do an attribute lookup for underscore underscore add underscore underscore. Well, because we're looking up the operator method explicitly by name, this is done like a regular attribute search. First, Python looks in the erin instance object itself, then it looks in the class erin, then in its parent, all the way up to object. However, when we use one of the operators which implicitly translates into a method call, the attribute search behavior slightly changes. So here, when we write A plus B, that's implicitly invoking A dot underscore underscore add underscore underscore with an argument of B. So the attribute search for that underscore underscore add underscore underscore, the search doesn't start as usual in the erin instance itself, rather it starts directly on the class. So the special rule for these operator methods is when invoked via the operator, the attribute search skips the instance itself. Now, 99 times out of 100 when you invoke a method, it doesn't matter if you skip the instance itself because methods are generally attached to the classes, not the instances. In the case of these operators, we know that the attribute search is looking for a method, not any other kind of attribute. So it's safe for Python to skip the instance. That's one reason Python does it, there's a small little efficiency gain and then it doesn't have to search first in the instance. The real reason for this rule, though, is that when we use an operator on a class object itself, we don't want the operator method to be found in that class itself. So for example, if erin itself had a method called underscore underscore add underscore underscore, and we invoked erin with the addition operator, don't ask me why you would do this with a class. But if you did, you wouldn't want it to be found in erin itself because when erin defines an add method, that's for instances of erin, not for the class erin itself. Now, I will say that defining behavior for standard operators with class instances is already a questionable thing to do, but it'd be really weird to do the same for classes themselves. However, there are a few operator methods defined in the type class, such that there are some operators that work with class as part of the standard behavior. We won't discuss that stuff now, though. We'll save it for the supplementary material.