|Programmer's Python - Objects Become Classes|
|Written by Mike James|
|Monday, 09 November 2020|
Page 2 of 3
We can think of the attributes that the class provides the instance as class attributes. Notice that all instances of the class share the same attributes as they are provided by the class object.
How can the instance acquire its own attributes?
The answer is that instance attributes are created when you assign to an instance.
In this case the usual rule in Python that assignment creates an attribute or a variable is followed.
This means that after assignment the instance has its own attribute.
myObject=myClass() print(myClass.myAttribute) myObject.myAttribute=3 myClass.myAttribute=2 print(myClass.myAttribute) print(myObject.myAttribute)
In this case after assignment to myObject.myAttribute the instance no longer uses the class attribute and so you see 1, 2, 3 printed.
Assigning to the class attribute has no effect on the instance attribute and vice versa.
The rule is that changes to an instance’s dictionary never update the class dictionary. For example, you can delete an instance’s attribute and after this the class attribute of the same name will be used.
Also notice that you can add attributes to an instance that are not defined in the class.
That is, you are not restricted to simply overriding the class attributes. However, it is a good idea not to add attributes to an instance that are not defined in the class because this spoils the idea that the class is in some way a definition of all of the instances it creates. It is ad-hoc programming and likely to result in a very messy program.
These ideas are discussed more in Chapter 7.
What is a Method?
Attributes can be any object but when attributes are functions things are slightly different. A function that is an attribute of an object created using a class is converted into a method.
Before we look at how this works it is worth spending a few minutes looking at the general problem and exactly what a method is.
When we first started writing programs in higher-level languages, best practice was to write a function for whatever you needed to do.
For example, if you needed to sort an array, you would write a sort function which accepted a few parameters that determined the data and the operation:
where myArray is the data that you want to sort, and order is a parameter that sets the sort order to be used.
Later on we moved over to object-oriented programming where data and the functions that process the data are grouped together into entities called objects. In this case the functions like sort became methods of the data that they were to operate on.
So an Array object would have a sort method and you would write the sort operation as:
You can see that this is a small change from the use of myArray as a parameter to the sort function to myArray as an object and sort method. You could say that the whole of the shift from functions to object-oriented programming is all about the shift of a parameter from inside the function to outside.
Looking a little deeper the simplification that this shift brings about is well worth it.
The sort function can now use the data in myArray more or less by default and this makes it possible to create an isolation from the rest of the program. It also brings about a complete change in the way that we think about functions.
For example, you could say that myArray “knows how” to sort itself. Another object myList say may also “know how” to sort itself using its own sort function which isn’t the same as the Array sort function.
This means that each data structure can have its own sort function and we can avoid having to have an arraySort function and a listSort function and so on.
This is a limited form of polymorphism and it is one of the huge advantages of object-oriented programming. Yet it is strange that even the most enthusiastic object-oriented programmers don’t think that there is any problem in having overloaded functions to deal with different data types which is a problem much better solved by polymorphism. It is much better to have the data determine which function to use rather than the signature of a detached function.
So the key point is that:
is a function that accepts the data it is going to work with as its first parameter and:
is a method that belongs to the myArray object.
How Functions Become Methods
You can see that if you want to use a function as a method all you have to do is convert that call to the:
method into a call to the:
In Python this transformation from method to function works in a very direct way. Within the class you create an attribute as a function attribute and the first parameter, usually called self, is the instance that the function is to be a method of.
class myClass: myAttribute=1 def myFunc(self): print(self,myAttribute)
myFunc is just a standard function object. The first parameter is only named self by convention – you can use any name you like but be prepared to confuse everyone if you do.
If you call myFunc as an attribute of the myClass object then there is nothing new:
and it prints 1 as you would expect. Notice that you have to explicitly pass a value for self and this is just the object you want the function to operate on.
Things are different if we create an instance of the class and call myFunc:
If you try this out, calling myFunc as if it was a function as before, you will find that you get an error message:
print(myObject.myFunc(myObject)) TypeError: myFunc() takes 1 positional argument but 2 were given
which, like most error messages, doesn’t actually tell you what the problem is.
More to the point when you look at it then you have to come to the conclusion that the error message lies! It is quite clear that myFunc takes one argument but “2 were given” is clearly wrong. Only one argument was given so what is going on?
The answer is that when you call a function defined on a class from an instance, the first thing that happens is that the attribute is looked up in the instance’s dictionary as usual; when it isn’t found it is accessed via the class object’s dictionary.
However, instead of returning the function object, the attribute access creates a method object as a wrapper for the function object which is something like:
def method(*args,**kwargs): return myClass.myFunc(myObject,*args,**kwargs)
In other words, the function is converted to a method by an object which calls it with the first parameter set to the object that the function is now bound to. Now you can see why the call has two arguments – one is automatically provided by the system and the second, the one you supplied, is superfluous.
The correct way to call the instance’s method is:
The system supplies the self parameter in the call – myObject in this case.
Notice that you don’t have to call the method to create it; accessing the attribute is enough to wrap the function object as a method object.
|Last Updated ( Monday, 09 November 2020 )|