The course is part of this learning path
Object Oriented Development
This training course provides you with a deep dive into how to create object oriented based Python scripts.
- Understand how to write and define classes and class constructors
- Review how class methods are created
- Review how to add properties to a class
- Understand how you work with objects created from classes
- Examine and use class inheritance or subclassing
- And finally, understand how to implement so-called special methods
- A basic understanding of the Python programming language
- A basic understanding of software development
- A basic understanding of the software development life cycle
- Software developers interested in learning how to write Python code in a Pythonic way
- Python junior level developers interested in advancing their Python skills
- Anyone with an interest in Python and how to use Python to write concise and elegant scripts for general purpose tasks
About the Author
Jeremy is the DevOps Content Lead at Cloud Academy where he specializes in developing technical training documentation for DevOps.
He has a strong background in software engineering, and has been coding with various languages, frameworks, and systems for the past 20+ years. In recent times, Jeremy has been focused on DevOps, Cloud, Security, and Machine Learning.
Jeremy holds professional certifications for both the AWS and GCP cloud platforms.
- [Jeremy] Okay, to begin with, let's describe what a class is. Classes, as already mentioned, are a very powerful tool to organize code. A class is a definition that represents a thing. The thing could be a file, for example, a process, a database record, a strategy, a string, a person, or a truck. The class itself describes both data, which represents one instance of the thing, and methods, which are functions that act upon the data. There can be both class data, which is shared by all instances, and instance only data, which is only accessible from the instance itself. A class is the basic unit of object-oriented programming. When it comes to defining classes, the syntax used is, you start with the class keyword, followed by the class name. Then you can use rounded brackets and the base class, if inheritance is involved. For example, the simplest form of a class definition could look like this, class followed by ClassName, empty rounded brackets because I'm not inheriting from a base class, and then the definition of the class, we'll just use the pass keyword. Normally, the contents of a class definition will be method definitions and shared data. A class definition creates a new local namespace. All variable assignments go into this new namespace. All methods are called via the instance or the class name. As previously mentioned, a list of base classes may be specified in parenthesis after the class name. In this case, the class is then considered to extend or inherit from the space class. Object instances. An object instance is an object created from a class. Each object instance has its own private attributes, which are usually created in the _init_ initialization method. Instance attributes. An instance of a class or an object, normally contains methods and data. To access these attributes, use the dot notation, that is, object.attribute. Instance attributes are considered dynamic. They can be accessed directly from the object. You can create, update and delete attributes in this way. Attributes cannot be made private, but names that begin with an underscore are understood by convention to be for internal use only. Users of your class will not consider methods that begin with an underscore to be part of your class's API. Again, this is convention only but should be understood and applied. Okay, let's demonstrate our first class. We'll use this example here, which imply for our entail terminals. We'll start up with Python3 interpreter and for starters we use the class keyword to declare that we're creating a custom class. We'll call it Spam. Now, the convention for class names is that they begin with a capital letter. And we'll define a function called eggs. Eggs takes a parameter called self. And here we'll just call pass. The pass statement is a null operation. It's required when you need something syntactically new code but you actually don't need any code.. So we're just using it for the purposes of this demonstration. We'll then declare our second function or method beverage. Again, it takes a reference to self as a parameter. And likewise, we'll again use the pass statement. Okay, so that concludes our class definition. So that is now in the system. And we can create objects from it so we'll create s to be a spam instance, or object. And now we can call the methods on it. So we can call the public methods, which we now is eggs and has worked albeit nothing has actually happened because we declared eggs with the pass statement up there. We can add attributes so we could say s.toast is equal to buttered. We could print out that particular attribute. And we can call, although the convention is not to, the beverage method. So called that method has an arid, sorry, runtime that is acceptable. But the convention is any method named such that it begins with an underscore is considered to be private to that class. So the convention is that we shouldn't be calling underscore beverage on the east object. Okay back to the slides. Instance methods, an instance method is a function defined in a class. When a method is called from an object, the object has passed and as the implicit face parameter, this more often than not, is named self by strong conviction. Again, let's use an example within our python interpreter. will paste in the definition for our Rabbit class, like so. So a couple of things here, we define a constructor. So I mean either we create an instance of our Rabbit, this particular _init_ function or method within the Rabbit class will always be called the first parameter, and again by convention is called self and will be a reference to itself. And then on the self reference, we see attributes of _size, _ danger, to be the values size and danger that we passed on. We create a another function called threaten. Again, the fist parameter is a reference to self. And here we're simply going to print out the values of _size and _danger that have been set on the self reference. So let's see now how we would call us. So we would say r1. For r1 equals Rabbit. will say it's. The value size to be large, and we'll say it's danger to have sharp , pointy teeth enter. We can think of r1.threaten like so. In here, as expected, we print out the values that we just passed on large, large and sharp pointy teeth. sharp pointy teeth. Okay, we could create a second instance of Rabbit like so . In this case will declare it to be of size small and that is fluffy and firry. We call threaten on this instance like so, and again we get the expected result. Constructors, if a class defines a method named __inti__, it will be automatically called when object instances created. This is considered the constructor. The object being created, is implicitly past is the first parameter to this method. This parameter is named self by very strong conviction. Data attributes can be assigned to self. These attributes can then be accessed by other methods, within the same class. Again, we present this Rabbit class definition is an example here the inti method is setting the attributes size and danger on self. Again _inti_ is considered the constructor for this particular class and will always be called first, when any instance of Rabbit is created. Getters and setters. Getter and setter methods can be used to access and objects data. These are traditional in other object-oriented programming languages. A getter a retrieve a piece of data a private variable from self. A setter assigns a value to a variable or self. Again we'll use an example to see this in action. Here will create a class definition format. Back within our Python3 interpreter. Will pay some the definition for our Knight class. Here we have the constructor, setting the name. Here we have the setter for the name attribute and the getter for the name attribute. So how would we use the getter and setter the methods? Well, let's create an instance of Knight. And will pass in the name to be lancelot. And now if we call the getter. We get the name that we passed into the constructive when we created the instance of Knight. We can also now call the setter on our instance. This time will use the value Jeremy. And again if we call the getter, we can see that the name attribute on our Knight instance has actually changed as expected. Okay, so that's getters and setters. Properties, while object attributes can be accessed directly, in many cases, the class needs to demonstrate some control over the attributes. A more elegant approach is to use properties. A property is a kind of managed attribute. Properties are accessed directly, much like normal attributes or variables, but getter a setter and deleter functions are implicitly called , so that the class can control what values are stored or retrieved from the attributes. You can create get a setter into later properties. And to create the get a property apply the @property decorator to a method with the name you want. It receives no parameters other than self. To create the setter property, create a second function with the property name. Decorate this property with the name plus .setter. In other words, if the property name is spam, the decorator will be @spam.setter. The setter method will take one additional parameter other than self, which is the value assigned to the property. It is common for a setter property to rise an error if the value being assigned is invalid. While you seldom need a delete a property, creating it is the same as for setter property but instead use @propertyname.deleter Lets now we use our class Knight definition but using properties. Okay pasting back on our class Knight definition, this time it's been updated to us properties. So we can see here that we declare a name, title and color and our constructor on our Knight class. And that we're now using the @property decorator as well as in @color.setter decorator to establish properties that can have value setter on them. So it see now how we would use this. So I will use K to be an instance of Knight and will pass in Lancelot has the name. Title be Sir and color will be blue. Will now use a print function, and we use a string format. And here we'll grab the name and the color dot format. And here we can call k dot` and its property, in this case name. And then the second one, the color property. And there we go. So we get Lancelot in blue via the property names. Because we've declared a setter property on the color, we could update the color like so, k.coloris equal to red. Red needs to be a string. And again, if we call it our print statement, which is going to reference the name and color properties, you can see that the color has been updated via the color property. Class data, data can be attached to the class itself, and shared among all instances. class level data can be accessed via the class name from inside or outside of the class. Any class attribute not overwritten by an instance attribute is also available through the instance. Okay back within our Python3 interpreter we'll paste in the following definition for our Rabbit. Now, the key update in this particular definition is the declaration of location. So here LOCATION is considered to be part of the class not specific to the instance itself. However, each instance can actually refer to it as as done here. So let's try this out. So we'll do r1 is equal to an instance of Rabbit. And we'll paste in the message a nice cup of tea. And then if we call r1.display we can see that indeed, location has been referenced, here. So that's coming off the class. Likewise if we know wish to create a different instance of Rabbit pasting in the message, big pointy teeth, and we called r1.dispay again we see we have access to the location variable setter on the class. Class methods, if a method only needs class attributes, it can be made a class method via the @classmethod decorator. This alters the method so that it gets a copy of the class object rather than the instance object. This is true whether the method is called from the class or from it instance. The parameter to our class method is named cls, by strong convention. Again will provide an example of this. Will reuse and update the previous example. So we'll paste in our definition of the Rabbit class like so. And the difference here is that we're specifying the @classmethod decorator on this function, get_location. And the point here is that the get_location function is calling the cls.LOCATION variable which is the variable defined up here. And again, this is available at the class layer. So all instances of Rabbit will be able to call location via this function. So let's see how this would be called. So we'll create an instance of Rabbit, will set a message like so. And then what we can do is we can refer to the class itself, and this time called the get_location method on it. and here we can see that, indeed, we've got access to the class level location variable. Likewise, we could actually call it on the instance itself, Inheritance, any language that supports classes should support inheritance. One or more base classes may be specified as part of the class definition. All of the previous examples in this course, have used the default base class object. The base class must already be imported if necessary. If I requested attribute is not found in the class, the search looks in the base class. This role is applied recursively if the base class itself is derived from a another class. For instance, all classes inherit the implementation from object unless a class explicitly implements it. Classes may override methods of the base classes. And to extend rather than simply replace a base class method call the base class method directly. For example, BaseClassName.methodname. The Super function ,the super function can be used in a class to invite methods and base classes. It searches the base classes and their bases recursively, from left to right until the method is found. The advantage of this super function is that you don't have to specify the base class explicitly. So, if you change the base class, it automatically does the right thing. For classes they have a single inheritance trade, this works great. For classes they have a diamond-shaped tree, the super function may not do what you expect. In this case using the explicit base class name is best. Okay in this example we using visual code and we've set up two files, animal and insect. So in the animal file, we have created a class definition for animal. And in this example, this is considered the base class. And the insect file, we've created a definition for insect and it derives from animal so an insect is a type of animal, albeit specialized, is having two sets of wings and three pairs of legs. Okay, so we can now jump over to our terminal will do a directory listing, we can see that we've got our animal.py file our insect.py file. Now we can call the animal file directly like so. And what we see three print statements. Where we've created a lion and two cats, and that the zoo count is three. So we go back to visual code. You can see each time that we call the constructor on the animal class, we're increasing the animal.count and that we provide a class level method called Zoo size and that is returning the class.count value in the RL three print statements, so the lion and the two cats and then a final print statement which is calling the zoo size on animal will jump back to the terminal. And this time will call Python3 on insect. In this time we get a different set of print statements and a final Zoo count of two. So back within visual code. You can see here that when we run this file, we created two insects The first one a monarch butterfly, the second one scarab beetle. We then look through our insect. And then finally again we call the zoo_size class method from the base class. Multiple inheritance, Python classes can inherit from more than one base class. This is called multiple inheritance. Classes designed to be added to a base class are sometimes called mixin classes or just mixins. Methods are searched for in the first base class, then its parents, then in the second base class and parents and so forth. Put the extra classes before the main base class. So any methods in those classes will override methods with the same name in the base class. Note as a tip to finding extract the method resolution order or MRO. For a particular class, call the classes MRO method. Will now demonstrate multiple inheritance using the following example. Okay, in this demonstration, we using visual code again. In this time I've created a file called multiple_inheritance. In this file, at the top, I define and decrial the animal base class. This is our base layer class. Following on from there, we declare another two classes. And these are typically called mixing classes. So these describe behavior that we're going to add to our base class. Finally, we declare a Dog class, which references our combat class. So this is the mixin. And then it's followed by the base class. So this mixin basically gives extra functionality to our base class. Likewise, we're defining a Sparrow class, this users that can fly mixin and it's applied to the animal base class. Finally at the bottom, we instantiate a dog instance, followed by a sparrow instance. And then we call get_ID which is available to all classes, followed by the particular mixing methods. For dog its bark and for sparrows fly. Finally at the bottom, we're calling the MRO function on the Sparrow class to show the method resolution order. Okay, we save that. We jump over into our terminal. Do a direct listing. And this time we're going to run Python3 on our multiple inheritance file. And there we go. So you can see that our dog instance has behaved correctly. It's called the bark method. Likewise, we have a sparrow object and it's called the fly method. Finally, we can see the method resolution order and as a list of which contains the order of methods as called, which can be useful for .... Abstract based classes. The abc module provides abstract based classes. When a method and an abstract class is designated abstract, it must be implemented and any derived class. If a method is not marketed abstract, it may be overwritten or extended. To create an abstract class, import ABCMeta and abstractmethod. Create the base abstract class normally, but assign ABCMeta to the class option metaclass. Then decorated any desired abstract methods with the @abstractmetheod decorator. Now, any classes that inherit from the base class must implement any abstract methods. Non-abstract methods do not have to be implemented, but of course will be inherited. Okay, within visual code, I've created this class called abstract_base_classes to demonstrate how abstract based classes work. So, to begin with, we import ABCMeta and abstractmethod from abc. At the top of this file, I'm declaring an animal class, which contains a single abstract method called speak. So speak itself doesn't have an implementation. Following on from this, I declare a Dog class, which has animal as its base class. So dog is inheriting from animal and therefore, it has to implement the abstract speak function from your base class, which it does. Likewise, I create a Cat class which inherits from animal and therefore must also implement the abstract speak method. Finally, I create a Duck class which inherits from animal and purposely doesn't implement speak. We then create an instance of dog and call speak method on it. Likewise, we create an instance of cat and call the speak method on it. And then finally, we create an instance of duck, and we attempt to call the abstract method on it, which hasn't been implemented before. This is wrapped in a trike set statement. Okay, let's save this file. We'll jump back into our terminal. We do a directory listing. And we'll call Python3 on our abstract based classes file and there we go. So, the dog instance has worked correctly. Likewise, the cat instance has worked correctly. So the speak methods which were implemented in both of those classes have been called successfully. However, when we attempted to call the speak method on the duck. This is filed because we didn't implement the strict method within the Duck class itself. Special methods, Python has a set of special methods that can be used to make user-defined classes emulate the behavior of builtin classes. These methods can be used to define the behavior for builtin methods such as string in length. They can also be used to override mini Python operators, such as plus times and the quality operator. The following five slides itemizes each and all of the available special methods in variables, we won't go over them individually, but do take time yourself to understand that they do exist and what each of them actually accomplishes. Okay, in this example, we using visual code again, and this time I've created a file called arithmetic.specialmethods.py Now, in this example, we create a class called Special. And we create several functions that override the builtin functions. So one for add, one for multiply one for string and one for the quality operator. Then at the bottom of this file, we create various fusions or instances of Special. Then finally, at the bottom of this file, we use a bunch of print statements that show the updated behaviors for each of these operators. Okay, let's jump over into the terminal. And we'll quickly run this. We do a direst listing am going to run this guy here, Python3. There we go. So this are our print statement. So here we can see the update behavior for the operation as perl specialized implementation of the builtin function. Static methods, a static method is a utility method that is related to the class but does not need the instance or class object. Thus it has no automatic parameter. One use case of static methods is to factor some kind of logic out of the several methods, when the logic doesn't require any of the data in the class. To define a function or method as static method use the @staticmethod decorator.