Skip to content
Home » Google Career Certificates » Google IT Automation with Python Professional Certificate » Crash Course on Python » Week 5: Object Oriented Programming

Week 5: Object Oriented Programming

In this module, you’ll be introduced to the concept of object-oriented programming! You’ll learn how to build your own classes with unique attributes and methods. You’ll get a chance to write documentation for your classes and methods using docstrings. You’ll learn all about object instances and object inheritance, as well as how to import and use Python modules to make use of powerful classes and methods. To round things out, you’ll also be introduced to Jupyter notebooks, which we’ll use to write and execute more complex code.

Learning Objectives

  • Demonstrate object-oriented programming using classes and objects
  • Implement classes with custom attributes and methods
  • Write docstrings to document classes and methods
  • Leverage inheritance to reduce code duplication
  • Import and use Python modules to access powerful classes and methods

Object-oriented Programming (Optional)


Video: OOP Introduction (Optional)

In this video, the speaker reviews what the viewer has learned so far in the Python course, including the basic syntax of Python and the most common data structures: strings, lists, and dictionaries. The speaker then previews the upcoming videos, in which the viewer will learn about object-oriented programming (OOP), including how to create their own objects and use many of Python’s interesting capabilities. The speaker also mentions that there will be cheat sheets in the references and readings for the viewer to refer to as needed.

The speaker encourages the viewer to review the content and do the practice exercises as many times as needed if something is not clear right away. Finally, the speaker asks the viewer if they are ready for their orientation on OOP, and then invites them to jump right in.

Welcome back and congrats
on making it this far. Our journey together is getting more and more interesting,
don’t you think? Let’s take a second to review what you’ve
accomplished so far. We’ve now gone over all
the basic syntax of Python and then checked out the most common data structures, strings, lists, and dictionaries. These letter scripts
do a bunch of cool things like processing text, iterating through elements
to do an operation on each, finding out the frequency of an element and a whole lot more. In the next videos, we’re going to focus on
a bunch of new concepts. We’re going to dive into
object-oriented programming which is a way of thinking about and implementing our code. We’ll discuss how to create
our own objects and how to use many of Python’s
interesting capabilities. We’re going to learn a lot
of new terminology too. As usual, we’ll include cheat sheets in
the references and in the readings for you to refer to whenever you need
a quick refresher. As always, if something
isn’t clear right away, remember that you can
review the content and do the practice exercises as
many times as you need. Okay. Ready for your orientation on
object-oriented programming? There’s a lot to cover,
so let’s jump right in.

Video: What is Object-oriented programming?

Object-oriented programming (OOP) is a programming paradigm that models concepts using classes and objects. A class is a blueprint that defines the characteristics of a type, while an object is an instance of a class.

Everything in Python is an object, including numbers, strings, lists, and dictionaries. Attributes are the characteristics associated with a type, and methods are the functions associated with a type.

For example, a file object has attributes such as name, size, date created, and permissions. It also has methods to read and modify the file’s contents.

The next video will explore how to apply OOP concepts to classes and objects that have already been used in Python.

Object-Oriented Programming (OOP) in Python

Object-oriented programming (OOP) is a programming paradigm that uses objects and their interactions to design applications and computer programs. OOP is a powerful tool that can help developers create reusable code, modular applications, and maintainable software.

Classes and Objects

The two main components of OOP are classes and objects. A class is a blueprint that defines the characteristics of a type. An object is an instance of a class.

For example, we could have a class called Apple that defines the characteristics of an apple. This class could have attributes such as color, flavor, and size. We could then create objects of the Apple class, such as red_apple and green_apple.

Attributes and Methods

Objects have attributes and methods. Attributes are the characteristics of an object. Methods are the actions that an object can perform.

For example, the red_apple object could have the following attributes:

Python

color = "red"
flavor = "sweet"
size = "large"

The red_apple object could also have the following methods:

Python

def eat():
  size -= 1

def cut():
  return two_apple_halves

Inheritance

Inheritance is a feature of OOP that allows us to create new classes that are based on existing classes. This allows us to reuse code and create more complex classes.

For example, we could create a class called Fruit that defines the basic characteristics of a fruit. We could then create a class called Apple that inherits from the Fruit class. This would allow us to reuse the code in the Fruit class and add new attributes and methods to the Apple class.

Polymorphism

Polymorphism is another feature of OOP that allows us to write code that is more flexible and reusable. Polymorphism allows us to define different behaviors for the same method in different classes.

For example, we could have a eat() method in both the Fruit class and the Apple class. The eat() method in the Fruit class could simply reduce the size of the fruit. The eat() method in the Apple class could reduce the size of the apple and also display a message that says “Eating an apple…”

Benefits of OOP

OOP offers a number of benefits, including:

  • Reusability: OOP allows us to reuse code by creating classes and objects. This can save us time and effort when developing new applications.
  • Modularity: OOP allows us to create modular applications. This means that we can break down our applications into smaller, independent modules. This makes our applications easier to develop, maintain, and test.
  • Maintainability: OOP code is generally more maintainable than other types of code. This is because OOP code is well-organized and easy to understand.

Conclusion

OOP is a powerful programming paradigm that can help developers create reusable, modular, and maintainable software. Python is a great language for OOP development, and it offers a number of features that make it easy to write object-oriented code.

Examples of OOP in Python

Here are a few examples of OOP in Python:

  • The str class represents strings in Python.
  • The list class represents lists in Python.
  • The dict class represents dictionaries in Python.
  • The file class represents files in Python.

These are just a few examples, and there are many other classes and objects in Python.

Learning More About OOP

If you are interested in learning more about OOP, there are many resources available online. There are also many books and tutorials that can teach you about OOP in Python.

[MUSIC] Imagine you have to describe an apple
to someone who’s never seen one before, how would you do it? And what would you say,
besides that it’s delicious? You might start off by saying
that an apple is a type of fruit. You might talk about how there
are lots of different kinds of apples, each with its own color, flavor, and name. Well, when you’re explaining
concepts to your computer, it’s a good idea to approach
it in a similar way. Your computer has no idea what an apple
is, or even what a fruit can be. If you want your computer
to understand these things, you have to describe them in
your programs and scripts. Up to now, we’ve discussed elements of
syntax, like variables, functions, loops, and some more complex data structures,
like lists and dictionaries. These are powerful tools in
an IT specialist’s toolbox, but it can still be difficult to
translate real-world concepts, like what’s an apple, or
what’s a user account into programs. To make it easier for computers
to understand these new concepts, Python uses a programming pattern
called object-oriented programming, which models concepts using classes and
objects. This is a flexible, powerful
paradigm where classes represent and define concepts,
while objects are instances of classes. In our apple example, we can have a class called apple that
defines the characteristics of an apple. We could then have a bunch of
instances of that apple class, which are the individual
objects of that class. The idea of object-oriented
programming might sound abstract and complex, but you’ve actually been using
objects already without even realizing it. Almost everything in Python is an object, all of the numbers, strings, lists, and
dictionaries we’ve seen so far, and have used in our exercises and
quizzes, have been objects. And each of them was an instance
of a class representing a concept. The core, apple pun intended,
concept of object-oriented programming comes down to attributes and
methods associated with a type. The attributes are the characteristics
associated to a type, and the methods are the functions
associated to a type. In the apple example,
the attributes are the color and flavor. What would the methods be? Well, it depends on what
we’re going to do with apple. We could maybe have a cut method that
turns one whole apple into four slices, or we could have an eat method
that reduces the amount of apple available with every bite. Let’s think about a more IT focused
example, like a file in our computer. A file has lots of attributes, it has
a name, a size, the date it was created, permissions to access it,
its contents, and a whole lot more. There are actually so
many different file attributes, that Python has multiple
classes to deal with files. The typical file object focuses
on the file’s contents, and so this object has a bunch of methods to
read and modify what’s inside the file. Hopefully, these examples help make
object-oriented programming a little clearer, but don’t worry if you haven’t
fully wrapped your head around it. In our next video, we’ll explore how to
apply these concepts to some classes and objects we’ve already used in Python, which will help us dig a little
deeper into how this all works.

Attributes are characteristics associated with a type. Methods are:

Functions associated with a type

Right on! Remember, a method defines what you do with an object.

Video: Classes and Objects in Python

In the last video, we learned about the concept of classes and objects in Python. We learned that every object in Python belongs to a class, and that classes have attributes and methods.

We also learned how to use the dir() function to list all of the attributes and methods of a class, and how to use the help() function to get documentation for a class.

In this video, we will learn how to write our own class definitions with our own attributes and methods. This is a powerful feature of Python that allows us to create reusable, modular, and maintainable code.

To define a class, we use the class keyword followed by the name of the class. Then, we indent the code that defines the class’s attributes and methods. To define an attribute, we simply declare the variable name. To define a method, we use the def keyword followed by the name of the method.

Here is an example of a simple class definition:

Python

class Person:
  """A class to represent a person."""

  def __init__(self, name, age):
    """Initializes the person with the given name and age."""
    self.name = name
    self.age = age

  def greet(self):
    """Returns a greeting from the person."""
    return f"Hello, my name is {self.name} and I am {self.age} years old."

This class has two attributes: name and age. It also has one method: greet().

To create an instance of the Person class, we use the new keyword followed by the name of the class. Then, we pass in the arguments for the class’s constructor.

Here is an example of how to create an instance of the Person class:

Python

person1 = Person("Alice", 25)

This creates a new instance of the Person class with the name “Alice” and the age 25.

To access an attribute of an object, we use the dot notation. For example, to get the name of the person1 object, we would use the following code:

Python

person1.name

To call a method on an object, we also use the dot notation. For example, to call the greet() method on the person1 object, we would use the following code:

Python

person1.greet()

This would return the following greeting:

Hello, my name is Alice and I am 25 years old.

We can also use class inheritance to create new classes that are based on existing classes. This allows us to reuse code and create more complex classes.

For example, we could create a Student class that inherits from the Person class. The Student class could have additional attributes, such as major and GPA. It could also have additional methods, such as study() and take_exam().

Class inheritance is a powerful feature of object-oriented programming that allows us to create reusable, modular, and maintainable code.

In conclusion, writing our own class definitions is a powerful feature of Python that allows us to create reusable, modular, and maintainable code. By using classes, we can encapsulate our data and logic into reusable objects. We can also use class inheritance to create new classes that are based on existing classes. This allows us to reuse code and create more complex classes.

Classes and Objects in Python

Classes and objects are two of the most important concepts in object-oriented programming (OOP). OOP is a programming paradigm that uses objects and their interactions to design applications and computer programs.

Classes

A class is a blueprint for creating objects. It defines the characteristics and behaviors of a particular type of object. For example, we could have a class called Person that defines the characteristics and behaviors of a person.

Objects

An object is an instance of a class. It is a concrete representation of a particular type of thing. For example, we could create an object of the Person class called alice. This object would have the characteristics and behaviors of a person, such as a name, age, and the ability to greet others.

Attributes and Methods

Classes have attributes and methods. Attributes are the characteristics of a class. Methods are the actions that a class can perform.

For example, the Person class could have the following attributes:

Python

name
age

The Person class could also have the following methods:

Python

greet()

Creating Objects

To create an object, we use the new keyword followed by the name of the class. Then, we pass in the arguments for the class’s constructor.

For example, to create an object of the Person class called alice, we would use the following code:

Python

alice = Person("Alice", 25)

This creates a new instance of the Person class with the name “Alice” and the age 25.

Accessing Attributes and Methods

To access an attribute of an object, we use the dot notation. For example, to get the name of the alice object, we would use the following code:

Python

alice.name

To call a method on an object, we also use the dot notation. For example, to call the greet() method on the alice object, we would use the following code:

Python

alice.greet()

This would return the following greeting:

Hello, my name is Alice and I am 25 years old.

Class Inheritance

Class inheritance is a feature of OOP that allows us to create new classes that are based on existing classes. This allows us to reuse code and create more complex classes.

For example, we could create a Student class that inherits from the Person class. The Student class could have additional attributes, such as major and GPA. It could also have additional methods, such as study() and take_exam().

Benefits of Classes and Objects

Classes and objects offer a number of benefits, including:

  • Reusability: Classes and objects allow us to reuse code. This can save us time and effort when developing new applications.
  • Modularity: Classes and objects allow us to create modular applications. This means that we can break down our applications into smaller, independent modules. This makes our applications easier to develop, maintain, and test.
  • Maintainability: Classes and objects code is generally more maintainable than other types of code. This is because classes and objects code is well-organized and easy to understand.

Conclusion

Classes and objects are two of the most important concepts in object-oriented programming (OOP). OOP is a powerful programming paradigm that can help us to create reusable, modular, and maintainable software.

Python is a great language for OOP development, and it offers a number of features that make it easy to write object-oriented code.

I hope this tutorial has been helpful. If you have any questions, please feel free to leave a comment below.

[MUSIC] Remember how we use the type function when
checking what type a certain variable was? Let’s do that again now. When we use the type function
as we just did here, Python tells us which class the value or
variable belongs to. And since this is a class,
it has a bunch of attributes and methods associated with it. Let’s take the string class for
an example. In this case, the only attribute
is the content of the string. What about the methods? Well, in earlier videos, we looked at
a bunch of methods provided by the string class, like upper() to create
an uppercase version of the string. Or isnumeric() which checks whether or
not the contents are all numeric. Each string we’ve used in Python up to now has been a different instance
of the string class. They all had the same methods,
but the contents were different. This meant that the result of calling
those methods was different alsol. You can get your computer to list all
the attributes and methods in a class. To do that Just use the dir function. This gets the Interpreter to print to the
screen a list of all the attributes and methods. Well, that’s a lot of items. Let’s break this down a little bit,
so we all understand what’s going on. The first bunch here are special
methods that begin and end with double underscores. These methods aren’t usually
called by those weird names. Instead, they’re called by some
of the internal Python functions. For example, the len method
is called by the len function that we’ve used before to find
out the length of a string. Or the ge method is used to compare
if one string is greater than or equal to another, when using
the greater than or equal to operator. After the special methods, we see a lot of string methods
that we’ve already come across. This list gives the names
of all the methods, but it doesn’t tell us how we can use them. There’s a different function to
tell us that, which is called help. Let’s give that one a go. When we use the help function
on any variable or value, we’re showing all the documentation for
the corresponding class. In this case, we’re looking at
the documentation for the str class, the class of the string object. As before,
it starts with the special method. If we scroll down,
we reach the ones we’ve already seen. We can see the documentation for
a bunch of methods, and it tells us the parameters that method
receives and the type of return value. It also includes an explanation
of what the method does. For the count method we can see
that it receives the sub string that will be counted, and
it has optional start and end arguments to indicate which slice
of the string would be looked at. We know they’re optional because they’re
written between square brackets. In general, being able to read and
understand a method’s documentation is super important when you’re
writing your own code. Using the dir and help functions puts all the documentation
right at your fingertips. This makes it so much easier to figure out
how to use something for the first time. When you’re done looking
at documentation, you can just type q to quit. Python comes with a lot of classes already
predefined for us, which is super useful. But the power of object-oriented
programming is that we can also define our own classes with their own attributes and
methods. While you might not need to do
this when writing a simple script, as your programs grow in complexity
object-oriented programming will help you get the most out of the language. And that includes being able
to define your own classes. Up next, we’ll dive into how to write
our own class definitions with their own attributes and methods. Let’s get to it.

You want to find more information about the integer (int) class. What’s the best way to do this?

Use the command help(int)

Nailed it! Using the help command can be useful for finding quick documentation about the methods in a class.

Video: Defining New Classes

Sure, here is a summary of the video:

The video discusses how to define a class in Python. A class is a blueprint for creating objects. Objects are instances of a class.

To define a class, you use the class keyword followed by the name of the class. For example, to define a class called Apple, you would use the following code:

Python

class Apple:
  pass

The body of the class is indented to the right. The pass keyword is used to show that the body of the class is empty.

To add attributes to a class, you simply declare the variable name. For example, to add the attributes color and flavor to the Apple class, you would use the following code:

Python

class Apple:
  color = ""
  flavor = ""

To create an instance of a class, you use the new keyword followed by the name of the class. For example, to create an instance of the Apple class called jonagold, you would use the following code:

Python

jonagold = Apple()

To access the attributes of an object, you use the dot notation. For example, to access the color attribute of the jonagold object, you would use the following code:

Python

jonagold.color

To set the value of an attribute, you simply assign a value to it. For example, to set the color attribute of the jonagold object to “red”, you would use the following code:

Python

jonagold.color = "red"

To call a method on an object, you also use the dot notation. For example, to call the upper() method on the color attribute of the jonagold object, you would use the following code:

Python

jonagold.color.upper()

In the next video, you will learn how to define methods for a class.

Defining New Classes in Python

Classes are the foundation of object-oriented programming (OOP) in Python. They provide a blueprint for creating objects, which are instances of a class. Classes encapsulate data (attributes) and behavior (methods), allowing you to model real-world concepts in a structured and reusable way.

Creating a Class

To define a class, you use the class keyword followed by the class name and a colon (:). The class body is indented and contains the class’s attributes and methods.

Python

class Person:
    pass

This code defines a simple class called Person with no attributes or methods.

Adding Attributes

Attributes are the characteristics of a class. They represent the data associated with an object. To define attributes, simply declare the variable names within the class body.

Python

class Person:
    name = ""
    age = 0

This code adds two attributes to the Person class: name and age.

Adding Methods

Methods are the actions that a class can perform. They define the behavior of an object. To define methods, use the def keyword followed by the method name, parentheses, and a colon (:). The method body is indented.

Python

class Person:
    name = ""
    age = 0

    def greet(self):
        print(f"Hello, my name is {self.name} and I am {self.age} years old.")

This code adds a method called greet() to the Person class. The self parameter refers to the current object upon which the method is called.

Creating Objects

To create an object, use the class name followed by parentheses. The object is assigned to a variable.

Python

person1 = Person()
person1.name = "John"
person1.age = 30
person1.greet()

This code creates an instance of the Person class and assigns it to the variable person1. Then, it sets the name and age attributes of person1 and calls the greet() method.

Accessing Attributes and Methods

To access an attribute of an object, use the dot notation. To call a method, also use the dot notation.

Python

print(person1.name)  # Outputs: John
person1.greet()  # Outputs: Hello, my name is John and I am 30 years old.

Class Inheritance

Class inheritance allows you to create new classes that inherit the attributes and methods of existing classes. This promotes code reuse and helps organize complex class hierarchies.

Python

class Student(Person):
    major = ""
    gpa = 0

student1 = Student()
student1.name = "Alice"
student1.age = 20
student1.major = "Computer Science"
student1.gpa = 3.8
student1.greet()

This code creates a class called Student that inherits from the Person class. It adds two attributes: major and gpa. It also creates an instance of the Student class and sets its attributes.

Benefits of Classes

Classes offer several benefits, including:

  • Reusability: Classes allow you to reuse code by creating objects that encapsulate data and behavior.
  • Modularization: Classes help organize code into modular units, making it easier to develop, maintain, and test.
  • Maintainability: Class-based code is generally easier to maintain due to its well-structured nature.

Conclusion

Defining new classes is a fundamental aspect of object-oriented programming in Python. Classes allow you to model real-world concepts in a structured and reusable way, leading to more efficient and maintainable code.

We called out earlier
that the point of object oriented
programming is to help define a real-world concept in a way that the
computer understands. Defining a real-world concept
and code can be tricky. So let’s look at how
we might go about representing a concept
in Python code. We’ll take it step-by-step
and keep it simple. Let’s take our apple
example from earlier. We could use this code to
define a basic Apple class. Class Apple: pass. Sure, it doesn’t look
like much but with these two lines we’ve
defined our first-class. Let’s check out the syntax. In Python, we use
the class reserved keyword to tell the computer that we’re starting a new class. We follow this with the name
of the class and a colon. The Python style
guidelines recommend that class names should
start with a capital letter. So we’ll be using
that convention. In this case, our
class is called Apple. Class definitions
follow the same pattern of other blocks we’ve seen
before like functions, loops or conditional branches. After the line with the class definition comes
the body of the class, which is indented to the right. In this case, we haven’t added
anything to the body yet, so we use the pass keyword, to show that the body is empty. We can also use the
same keyword as a placeholder in any
empty Python block. So how might we expand our
definition of the apple class? Well, it would probably have the same attributes
that represent the information we
want to associate with an apple like color and flavor. We can add that
information like this. Class Apple: color, we’ll
set that to an empty string. Same with flavor. We’ll set that to an
empty string for now. So here we’re defining two
attributes: color and flavor. We define them as
strings because that’s what we expect these
attributes to be. At the moment, they’re
empty strings, since we don’t know what values these attributes will have. See how we don’t need
the pass keyword anymore now that we’ve got an
actual body for the class. All right. Now that we’ve got an Apple class and
some attributes, let’s see our Apple in action. Here, we’re creating
a new instance of our Apple class and assigning it to a variable called jonagold. Check out the syntax. To create a new
instance of any class, we call the name of the class
as if it were a function. Now that we’ve got our
shiny new apple object, let’s set the values
of the attributes. All right. We’ve just set the color and the flavor
as string values. To check that it worked, let’s try retrieving them both and printing
them to the screen. Print (jonagold.color). Print jonagold.flavor). The syntax used to access
the attributes is called dot notation because of the
dot used in the expression. Dot notation lets
you access any of the abilities that the
object might have, called methods or
information that it might store called
attributes, like flavor. The attributes and methods
of some objects can be other objects and can have attributes and
methods of their own. For example, we could
use the upper method to turn the string of the
color attribute to uppercase. So print
(jonagold.color.upper()). So far we’ve created one
instance of the Apple class and set its attributes and checked that they are
now correctly set. Now, we could create a new instance of the Apple class with different attributes. Golden equals Apple. Golden.color, we’ll set that to yellow and golden.flavor
equals soft. Both golden and jonagold are instances of the Apple class. They have the same
attributes, color and flavor. But those attributes
have different values. Congrats. You’ve learned how
to create your own classes. Let’s check that we’ve got all this down with a quick quiz. After that, we’re
going to learn how to define new methods for a class.

Reading: Defining Classes

Reading

Practice Quiz: Practice Quiz: Object-oriented Programming (Optional)

Let’s test your knowledge of using dot notation to access methods and attributes in an object. Let’s say we have a class called Birds. Birds has two attributes: color and number. Birds also has a method called count() that counts the number of birds (adds a value to number). Which of the following lines of code will correctly print the number of birds? Keep in mind, the number of birds is 0 until they are counted!

Creating new instances of class objects can be a great way to keep track of values using attributes associated with the object. The values of these attributes can be easily changed at the object level. The following code illustrates a famous quote by George Bernard Shaw, using objects to represent people. Fill in the blanks to make the code satisfy the behavior described in the quote.

The City class has the following attributes: name, country (where the city is located), elevation (measured in meters), and population (approximate, according to recent statistics). Fill in the blanks of the max_elevation_city function to return the name of the city and its country (separated by a comma), when comparing the 3 defined instances for a specified minimal population. For example, calling the function for a minimum population of 1 million: max_elevation_city(1000000) should return “Sofia, Bulgaria”.

What makes an object different from a class?

We have two pieces of furniture: a brown wood table and a red leather couch. Fill in the blanks following the creation of each Furniture class instance, so that the describe_furniture function can format a sentence that describes these pieces as follows: “This piece of furniture is made of {color} {material}”

Classes and Methods (Optional)


Video: Instance Methods 

Key Takeaways:

  • Methods are functions that operate on the attributes of a specific instance of a class.
  • Methods can receive more parameters and return values if needed.
  • Instance variables are variables that have different values for different instances of the same class.

Examples:

  • A method called speak() that prints the name of the piglet.
  • Another method called newspeak() that prints a different message depending on the value of the name attribute.
  • A method called pig_years() that converts the age of the piglet to pig years.

Important:

  • Methods are just functions that belong to a specific class. So they can work as any other function.
  • Constructor is a special type of method that is used to initialize an object.
Delving into Instance Methods: Understanding Object Behavior in Python

In the realm of object-oriented programming (OOP), instance methods play a crucial role in defining the behavior of objects. These methods, unlike regular functions, are associated with specific instances of a class, enabling them to interact with the object’s data and perform actions specific to that instance.

Understanding the Role of Instance Methods

Instance methods are functions that belong to a class and are associated with instances of that class. They allow objects to encapsulate both data and behavior, a fundamental principle of OOP. By defining instance methods, developers can specify how objects should respond to certain stimuli or perform specific actions.

The Anatomy of an Instance Method

Instance methods follow a similar structure to regular functions, with the addition of the self parameter. This special parameter represents the instance of the class upon which the method is being called. It allows the method to access and modify the instance’s data.

Here’s a simplified example of an instance method:

Python

class Person:
    def __init__(self, name):
        self.name = name

    def greet(self):
        print(f"Hello, my name is {self.name}!")

In this example, the Person class has an instance method called greet(). When this method is called on an instance of the Person class, the self parameter refers to that specific instance. The method then accesses the name attribute of the instance and prints a personalized greeting.

Key Characteristics of Instance Methods

Instance methods possess several key characteristics that distinguish them from regular functions:

  1. Association with Instances: Instance methods are bound to specific instances of a class, not to the class itself.
  2. Access to Instance Data: Instance methods can access and modify the data of the instance upon which they are called.
  3. Encapsulation: Instance methods promote encapsulation by encapsulating the behavior of an object within the object itself.

Practical Applications of Instance Methods

Instance methods serve as versatile tools for defining object behavior in various programming scenarios:

  1. Creating Custom Methods: Developers can create custom methods to encapsulate specific actions or behaviors for their objects.
  2. Enhancing Object Interaction: Instance methods enable objects to interact with each other, facilitating data exchange and collaboration.
  3. Promoting Code Reusability: Instance methods can be reused across different objects, reducing code duplication and enhancing maintainability.

Mastering Instance Methods: Key Takeaways

  • Instance methods are functions associated with instances of a class.
  • The self parameter represents the instance upon which the method is called.
  • Instance methods allow objects to encapsulate both data and behavior.
  • Instance methods promote code reusability and enhance object interaction.

By understanding the concept of instance methods and their practical applications, developers can effectively design and implement object-oriented programs, creating robust and maintainable code.

So how are you doing so far? Is everything making sense? Are all those apple
examples making you hungry? Feel free to pause and grab a snack if
that’s what you need. We called out earlier that we use methods to get
objects to do stuff. We’ve seen several
methods in our example so far like lower for strings, append for lists or
values for dictionaries. The key to understanding
methods is this; methods are functions
that operate on the attributes of a specific
instance of a class. When we call the append
method on a list, we’re adding an
element to the end of that specific list and
not to any other lists. When we call the lower
method on the string, we’re making the contents of that specific string lowercase. How exactly does this happen? Let’s take a closer look by
defining our own methods. First, we need to define a class and create an instance of it like
we’ve done before. Nice, we’ve created
a piglet class. While our new piglet
might be cute, it can’t do a whole lot now. What if we wanted
to give it a voice? For objects to perform actions, they need methods and as
we called out before, a method is a function
that operates on a single instance
of an object. Let’s add a method to our class. You can see here that we
start defining a method with the def keyword just like
we would for a function, and see how the line
with the def keyword is indented to the right
inside the Piglet class? That’s how we define a function
as a method of the class. This function is receiving
a parameter called self. This parameter
represents the instance that the method is
being executed on. Let’s try this out
and see what happens. Hamlet equals piglet hamlet.speak and the piglet goes, oink oink. It sure does, but this
makes the piglet say the same thing for all
instances of the class. Boring? Let’s make the
method do something different depending on the
attribute of the instance. This time we’ve studied the
body of the class by defining an attribute called name with
a default value of Piglet. We can change that value
later but it’s a good idea to set it now to make sure
our variable is initialized. If you look closely at how we
wrote the newspeak method, you’ll see that it’s
using the value of self.name to know
what name to print. This means that it’s accessing
the attribute name from the current instance of
Piglet. Let’s try this out. So set hamlet equals piglet. Then we can say hamlet.name, and we’ll set the name
to a string Hamlet, and then we can
call.speak on hamlet. Meet hamlet our python pig. What? You didn’t know
pigs could talk? Well, they can in Python. In this example, the speak
method printed the name Hamlet which was the name
attribute that we set. What if we create
a new instance of the same class but
with a different name? It should generate
a different output. Let’s try this out. I think
Hamlet needs a friend. We’ve now created
two instances of the piglet class each of
them with their own name. When calling this
speak method each of them prints their
name and not the other. Variables that have
different values for different instances of the same class are
called instance variables, just like the name
variable in this example. Since methods are just functions that belong to a specific class, they can work as
any other function. So they can receive
more parameters and return values if needed. Let’s check out what a method returning
a value looks like. In this case, we’ve
created a method that converts the age of our
piglet to pig years. So the value that the
method returns to change when we change the years attribute
of our instance. Let’s create an instance and
check how this method works. Piggy is two-years-old
and human years, how old is he and pig years? So as the value of the
years attribute changes, the return value of the pig
years method changes to. Coming up, we’re going to learn about a few special types of methods including one in
particular called constructor.

Reading: What Is a Method?

Reading

Video: Constructors and Other Special Methods

Constructors and Special Methods in Python

Constructors are special methods that are called when you create an instance of a class. They are used to initialize the attributes of the object. By using a constructor, you can ensure that all of the important values are set when the object is created.

In addition to constructors, there are a number of other special methods that can be used to customize the behavior of objects. One of the most useful of these is the __str__ method, which is used to define how an object is represented when it is converted to a string.

Special methods can be a bit tricky to understand at first, but they are a powerful tool that can be used to create more flexible and reusable code.

Constructors and Special Methods in Python

Introduction

Python is an object-oriented programming language, which means that it is designed to create objects that encapsulate data and behavior. Objects are created from classes, which are blueprints for creating objects. Classes can define various methods, including constructors and special methods.

Constructors

A constructor is a special method that is called when an object is created. It is used to initialize the attributes of the object. The constructor method has the same name as the class and does not have a return statement.

Example:

Python

class Apple:
  def __init__(self, color, flavor):  # Constructor method
    self.color = color
    self.flavor = flavor

In this example, the __init__ method is the constructor of the Apple class. It takes two parameters, color and flavor, and assigns them to the color and flavor attributes of the object, respectively.

Creating an Instance of the Apple Class:

Python

jonagold = Apple("red", "sweet")

This code creates an instance of the Apple class and assigns it to the variable jonagold. The constructor method is called when the Apple class is called, and the color and flavor arguments are passed to the constructor method.

Special Methods

Special methods are methods that have a double underscore (__) at the beginning and end of their name. They are used to define how objects interact with Python and other objects. Some common special methods include:

  • __str__(): This method is used to define how an object is represented when it is converted to a string.
  • __repr__(): This method is used to define how an object is represented when it is repr()d.
  • __add__(): This method is used to define how objects are added together.
  • __mul__(): This method is used to define how objects are multiplied together.
  • __eq__(): This method is used to define how objects are compared for equality.

Example:

Python

class Apple:
  def __init__(self, color, flavor):  # Constructor method
    self.color = color
    self.flavor = flavor

  def __str__(self):  # Special method for string representation
    return f"An apple that is {self.color} and {self.flavor}"

In this example, the __str__ method is a special method that defines how an object of the Apple class is represented when it is converted to a string. It returns a string that describes the color and flavor of the apple.

Printing the Instance:

Python

print(jonagold)  # Output: An apple that is red and sweet

This code prints the value of the jonagold variable. The __str__ method is called automatically when the print() function is called, and the formatted string is returned.

Conclusion

Constructors and special methods are powerful tools that can be used to create more flexible and reusable code. By understanding how to use these methods, you can create objects that are easy to use and interact with.

Additional Resources

Up to now, we’ve been
creating classes with empty or default values and their attributes and then setting the attribute values after
we’ve created the object. This works, but it’s not ideal. Working this way means
we need to write a separate line for each
attribute we want to set, and that makes it really easy to forget to set an important value. Us humans are pros at forgetting to do things, even
important things. So when writing code, it’s a good idea
to do things early to prevent from
forgetting them later on. So let’s set those values
as we create the instance. This way, we know
that our instance has all the important values in it from the moment is created and we don’t
have to worry about it. To do this, we need to use a special method
called constructor. Lets go back to our apple
example to see this in action. The constructor of the
class is the method that’s called when you call
the name of the class. It’s always named init. You might remember that
all methods that start and end with two underscores
are special methods. Here, we’ve defined
a constructor, one very important
special method. This method on top of the
self variable that represents the instance receives two more parameters:
color and flavor. Then the method sets those values as the values
of the current instance. Let’s see how that works with the new instance of
the apple class. Jonagold equals apple, and
we’ll give it red and sweet. Great. Now, let’s check that all the attributes
are set correctly. Print jonagold.color. Perfect. So now by adding a constructor method
that sets the attributes, we can create the class and have its values set right
when it’s created. Pretty handy, right?
Constructors aren’t the only special
methods we can write. When we use the STR or print functions to convert
an object to a string, we are using a super-useful
special method. But before we go
ahead and define one, let’s see what happens
when we don’t define it. We just tried to print
our apple instance, and we got a very weird message. We have the words apple
and object in there, but what’s the rest of it? Well, when we don’t specify
a way to print an object, Python uses the default
method that prints the position where the object is stored in the
computer’s memory. This is definitely
not what we wanted. If you ever try and print
something and Python prints a random string
of numbers and letters, you’ll know that it’s likely using the default representation, which is the position of the object in the computer’s memory. So how do we tell Python to print something that
makes sense for us? We use the special STR method which returns the string
that we want to print. Let’s see what this looks like. By defining the
special STR method, we’re telling Python that
we want it to display when the print function is called with an instance of our
class. Check it out. Jonagold equals apple. We’ll give it red and sweet. Print jonagold. So the STR method lets us print a friendly message
instead of a bunch of numbers. In general, it’s a good idea
to think ahead and define the STR method when creating objects that
you want to print. There are a lot of
other special methods. We’re not going to look
at the rest of them here, but you can find
pointers to learn more about them in the
official documentation. You’ll find the link to
that in the next reading. These concepts are
new and not too easy. So don’t worry if you’re
still trying to figure out the difference between a
method and a function. We’ve all been there.
The best way to feel confident is to keep
practicing until it’s clear. You’re doing great.
So keep at it.

Reading: Special Methods

Reading

Video: Documenting Functions, Classes, and Methods

Docstrings are helpful for understanding and using code. Docstrings are brief texts that explain what something does. You can add docstrings to classes, methods, and functions. To add a docstring to a class, method, or function, type a string between triple quotes and indent it to the right like the body of the function. For example, the following code adds a docstring to the to_seconds function:

Python

def to_seconds(hours, minutes, seconds):
  """Returns the amount of seconds in the given hours, minutes, and seconds."""

This docstring explains that the to_seconds function takes three arguments: hours, minutes, and seconds. The function returns the amount of seconds in the given hours, minutes, and seconds.

Here is an example of how to use the help function to view the docstring for a class, method, or function:

Python

print(help(to_seconds))

This will print the following output:

Help on function to_seconds in module __main__:

to_seconds(hours, minutes, seconds)
    Returns the amount of seconds in the given hours, minutes, and seconds.

Docstrings are a valuable tool for documenting your code. They can help you to understand your own code and make it easier for others to understand your code. I recommend that you add docstrings to all of your code.

Documenting Functions, Classes, and Methods in Python

Introduction

Documentation is an essential aspect of software development. It provides valuable information about the code, making it easier to understand, use, and maintain. In Python, docstrings are the primary method for documenting functions, classes, and methods.

What are Docstrings?

Docstrings are special string literals that are used to document Python code. They are placed at the beginning of functions, classes, and methods to provide a brief description of what they do. Docstrings can also include additional information, such as parameters, return values, and usage examples.

Creating Docstrings

Docstrings are created using triple quotes (“”” or ”’). The docstring should be indented to align with the code it documents. For example, the following code creates a docstring for a function named add_numbers:

Python

def add_numbers(a, b):
    """
    Adds two numbers and returns the result.

    Args:
        a (int): The first number to add.
        b (int): The second number to add.

    Returns:
        int: The sum of a and b.
    """
    return a + b

In this example, the docstring provides a brief description of the function, its parameters, and its return value. It also uses the Args: and Returns: tags to clearly identify the parameter and return value information.

Using Docstrings with Functions

Docstrings can be used to document any function, regardless of its complexity. For example, the following code creates a docstring for a function that calculates the factorial of a number:

Python

def factorial(n):
    """
    Calculates the factorial of a number.

    Args:
        n (int): The number for which to calculate the factorial.

    Returns:
        int: The factorial of n.

    Raises:
        ValueError: If n is less than 0.
    """
    if n < 0:
        raise ValueError("n must be non-negative")
    if n == 0:
        return 1
    else:
        return n * factorial(n - 1)

In this example, the docstring not only describes the function’s purpose but also explains potential errors that can occur. It also uses the Raises: tag to indicate that the function can raise a ValueError exception if the input n is less than 0.

Using Docstrings with Classes

Docstrings can also be used to document classes. The docstring for a class should provide an overview of the class’s purpose and its key features. For example, the following code creates a docstring for a class named Point:

Python

class Point:
    """
    Represents a point in two-dimensional space.

    Attributes:
        x (int): The x-coordinate of the point.
        y (int): The y-coordinate of the point.
    """
    def __init__(self, x, y):
        self.x = x
        self.y = y

In this example, the docstring describes the Point class and its attributes, x and y. It also includes a brief explanation of the class constructor, __init__, which initializes the x and y attributes of a Point object.

Using Docstrings with Methods

Docstrings can be used to document methods within classes. The docstring for a method should provide a description of the method’s purpose, its parameters, and its return value. For example, the following code creates a docstring for a method named distance within the Point class:

Python

def distance(self, other):
    """
    Calculates the distance between the current point and another point.

    Args:
        other (Point): The other point to calculate the distance to.

    Returns:
        float: The distance between the current point and the other point.
    """
    dx = self.x - other.x
    dy = self.y - other.y
    return math.sqrt(dx * dx + dy * dy)

In this example, the docstring describes the distance method, its parameter (other, a Point object), and its return value (the distance between the current point and the other point).

Benefits of Docstrings

Docstrings offer several benefits, including:

  • Improved code comprehension: Docstrings make code easier to understand, both for the original author and for others who may need to work with the code later.
  • Enhanced maintainability: Docstrings provide valuable context for code changes, making it easier to maintain and

The world of classes and
methods can be a little puzzling when you’re still
learning your way around, and that’s why the help
function can come in handy. You might remember
that we can still use the Python function help to find documentation about
classes and methods. We can also do this on our own classes,
methods, and functions. Let’s check this out. We’ll start with the Apple class
we used before, and now we’ll ask for some help. See how when we asked for
help on our class we got a list of the methods that
are defined in the class? In this example, the
defined methods are the constructor and the
conversion to string. But this documentation is
super short and to be honest, it doesn’t explain a whole lot. So let’s go back to the
interpreter by typing Q. We want our methods, classes, and functions to give
us more information when we or someone else
use the help function. We can do that by
adding a docstring. A docstring is a brief text that explains what
something does. Let’s see how this works
with a simple function. First off, we want to
define the function. So def, we’ll call it
to_seconds and we’ll give it the parameters hours,
minutes, and seconds. After that, we add our docstring. We do this by typing
a string between triple quotes and we indent it to the right like the
body of the function. Returns the amount of seconds in the given hours,
minutes, and seconds. Next, we write the
code for our function. Return hours multiplied by 3600 plus minutes
times 60 plus seconds. So there we have it, we have a function with a
docstring in its body. Let’s see how we can use the
help function to see it. Help to_seconds. Success. The help function
shows us the string we wrote. As we called out earlier, we can add docstrings to
classes and methods too. Let’s use our piglet class to see what this would look like. Now we’ve got a bunch
of helpful information, we’ve added docstrings for our piglet class and
for its methods. Remember that the docstring
always has to be indented at the same level of the
block it’s documenting. Docstrings are super helpful for figuring out how
to use a function you’ve never used before. Not only that, if you’re reading a piece of code written
by someone else, docstrings let us understand the code much better
because the classes, methods, and functions
are clearly documented. So when writing your code, add docstrings to explain your functions,
classes, and methods. It’ll make a ton of difference to anyone who might
use your code.

Reading: Documenting with Docstrings

Reading

Reading: Classes and Methods Cheat Sheet (Optional)

Reading

Video: About Jupyter Notebooks (Optional)

Jupyter Notebooks are a special kind of document that can contain pieces of programming code. They are a more powerful tool than code blocks because they can contain other things like text, images, interactive widgets, and a whole lot more. This allows us to tell an interactive story with our code exercises. Jupyter Notebooks are open-source technology that you can use outside this platform.

Introduction to Jupyter Notebooks

Jupyter Notebooks are a powerful tool for writing and executing Python code. They are web-based notebooks that combine code, text, and mathematical expressions into a single document. This makes them ideal for data science, machine learning, and other tasks that involve both coding and exploration.

Key Features of Jupyter Notebooks

  • Interactive Code Execution: Jupyter Notebooks allow you to execute code one cell at a time, which makes it easy to experiment with different ideas and debug your code.
  • Rich Text and Code Formatting: Jupyter Notebooks support rich text formatting, including markdown, which makes it easy to add explanations, images, and other multimedia content to your notebooks.
  • Interactive Widgets: Jupyter Notebooks support interactive widgets, such as sliders and dropdown menus, which allow you to interactively explore data and visualize results.
  • Sharing and Collaboration: Jupyter Notebooks can be easily shared with others, and they can also be used for collaborative work.

Getting Started with Jupyter Notebooks

There are several ways to get started with Jupyter Notebooks. You can install them using pip or conda, or you can use a cloud-based platform such as Google Colab or JupyterHub.

Creating a Jupyter Notebook

To create a Jupyter Notebook, you can use the jupyter notebook command in your terminal. This will launch a Jupyter Notebook server and open a new notebook in your web browser.

Structure of a Jupyter Notebook

A Jupyter Notebook consists of cells, which are blocks of code or text. Cells can be executed by clicking the Run button or by pressing Shift-Enter.

Types of Cells

There are two main types of cells in Jupyter Notebooks: code cells and markdown cells. Code cells contain Python code, while markdown cells contain rich text formatting.

Using Jupyter Notebooks for Data Science

Jupyter Notebooks are a popular tool for data science because they provide a convenient and interactive environment for exploring, analyzing, and visualizing data.

Additional Resources

There are many resources available for learning more about Jupyter Notebooks, including the official documentation and numerous online tutorials.

I hope this tutorial provides a helpful overview of Jupyter Notebooks. Please let me know if you have any questions.

In all our quizzes so far, we’ve been working
with code blocks. Code blocks are a great tool for writing small snippets of code, but now we’re tackling more complex problems so we
need a more powerful tool. We’re going to start using a new tool called
Jupyter Notebooks, kicking off with the next quiz. A Jupyter Notebook
is a special kind of document that can contain
pieces of programming code. We can execute these
pieces of code inside the notebooks
one piece at a time, and the notebooks
can also contain other things like text, images, interactive widgets,
and a whole lot more. These extra elements
allow us to tell an interactive story
with our code exercises. Like Code Blocks,
Jupyter Notebooks lets us edit and run our code
in the web browser. The difference is that we can add explanations and
between the code, and also the pieces of code
are related to each other. Jupyter Notebooks are an
open-source technology that you can use
outside this platform, so if you’re interested, you could even run it
locally on your computer. Without further ado,
let’s check out how a Jupyter Notebook works and
what you can do with it. We’ll first click
on Open Notebook and wait until the
notebook loads. Now that it’s loaded, you can see some explanatory
text and a bit of code. We can execute the code by clicking the run
button in the toolbar, or we can also run it by pressing Shift-Enter
on our keyboard. Now that we’ve run our code, you can see there’s a number
here next to the first cell. This number tells us that
the code has been executed. If a cell generates any output, it’ll appear at the
end of the cell. Let’s try executing the next cell and see if we get an output. This cell includes
some print calls, so after executing it, the interpreter
printed the values. The comments in the
code tell us that these values should
be nine and one, but the print statement say that they’re zero that’s because we need to edit the code in the first cell to make it
do what it needs to do. Let’s edit the go-to function. Once we’ve made the change, we need to re-execute the cell so that the elevator
class is modified, and an elevator variable is created with the
new elevator class. Now we can re-execute
the second cell. Okay, now we get 10 and
negative one instead of zero. But that’s still not what our
comment say we should get. We still got work
to do on the code to make it do what
it’s supposed to do. We don’t want to get ahead
of ourselves, though. So let’s leave it at that. Once you’re working
through the exercises yourself in your
Jupyter Notebook, remember you have to re-execute the class definition
whenever you modify it. If you forget, the elevator
variable won’t change. If at any point you’re stuck or something doesn’t
work as expected, there’s more help in the
next reading. Good luck.

Reading: Help with Jupyter Notebooks

Reading

Code Reuse


Video: Inheritance

Inheritance is a powerful tool in object-oriented programming that allows you to reuse code written for one class in other classes. It is like a parent-child relationship, where the child class inherits the attributes and methods of the parent class. This can be very useful for avoiding code duplication and making your code more modular. For example, you could create a base class called Fruit that has attributes for color and flavor. Then, you could create child classes for Apple and Grape that inherit from the Fruit class. This would allow you to reuse the code for the Fruit class in both the Apple and Grape classes.

Inheritance

Inheritance is a fundamental concept in object-oriented programming (OOP) that allows classes to inherit attributes and methods from other classes. This can be helpful for code reuse and organization.

Understanding Inheritance

To understand inheritance, consider the following real-world example:

A car is a type of vehicle. A car inherits the attributes and methods of a vehicle, such as wheels, a motor, and the ability to move. However, a car also has its own unique attributes, such as doors, a windshield, and a steering wheel.

In OOP, you can represent this relationship using classes.

Python

class Vehicle:
    def __init__(self, wheels, motor):
        self.wheels = wheels
        self.motor = motor

    def move(self):
        print("The vehicle is moving.")

class Car(Vehicle):
    def __init__(self, wheels, motor, doors, windshield, steering_wheel):
        super().__init__(wheels, motor)
        self.doors = doors
        self.windshield = windshield
        self.steering_wheel = steering_wheel

    def open_door(self):
        print("The car door is opening.")

In this example, the Car class inherits from the Vehicle class. This means that the Car class has all of the attributes and methods of the Vehicle class, plus its own unique attributes and methods.

Benefits of Inheritance

Inheritance offers several benefits, including:

  • Code reuse: Inheritance allows you to reuse code written for one class in other classes. This can save you time and effort, and it can also make your code more maintainable.
  • Code organization: Inheritance can help you organize your code in a more logical and hierarchical way. This can make your code easier to understand and use.
  • Polymorphism: Inheritance is a key component of polymorphism, which is the ability of objects to take on many forms. This is a powerful feature of OOP that allows you to write more flexible and reusable code.

Types of Inheritance

There are three main types of inheritance in Python:

  • Single inheritance: This is the most common type of inheritance, where a class inherits from one parent class.
  • Multiple inheritance: This is where a class inherits from multiple parent classes.
  • Multilevel inheritance: This is where a class inherits from another class, which inherits from another class, and so on.

Guidelines for Inheritance

When using inheritance, it is important to follow some guidelines to ensure that your code is well-designed and maintainable.

  • Favor composition over inheritance: Sometimes, it is better to use composition, which is where you create a class that contains instances of other classes, instead of inheritance.
  • Keep your inheritance hierarchy shallow: Avoid creating deep inheritance hierarchies, as this can make your code difficult to understand and maintain.
  • Use interfaces to define abstract classes: Interfaces are a way to define abstract classes, which are classes that provide a blueprint for other classes to follow.

Conclusion

Inheritance is a powerful tool in object-oriented programming that can be used to write more reusable, maintainable, and flexible code. By understanding the concepts of inheritance and following the guidelines above, you can write better-designed OOP code.

[MUSIC] Wow, we’ve covered a bunch of new
stuff in these last few videos. You’re doing great. We’ve learned all about
object-oriented programming, and how to define our own classes and methods, including special methods like
constructors or string conversions. We’ve also learned how
to document them all. We’re now going to talk about another
aspect of object-oriented programming called inheritance. Just like people have parents,
grandparents, and so on, objects have an ancestry. The principle of inheritance let’s
a programmer build relationships between concepts and group them together. In particular, this allows us to reduce
code duplication by generalizing our code. For example, how could we develop our
apple representation to include other types of fruit, too? Well, one thing we know about
an apple is that it’s a fruit. So we could define a separate fruit class. We also know that all fruits
have a color and taste. So what if we moved our color and
flavor attributes into the fruit class? Here, we have a fruit class
with a constructor for the color and flavor attributes. Now, we can rewrite our apple class and easily add another fruit into the mix,
too. In Python, we use parentheses in the class
declaration to show an inheritance relationship. For our new fruit classes, we’ve used
that syntax to tell our computer that both the apple and the grape classes
inherit from the fruit class. Because of this, they automatically
have the same constructor, which sets the color and
flavor attributes. You can think of the fruit
class as the parent class, and the apple and grape classes as siblings. Let’s see this in action. First, we create an instance
of the apple class. Granny_smith equals Apple. And we’ll give it two parameters,
green as the color and tart as the flavor. And now, an instance of the grape class. Then, to check that this actually worked,
let’s print the attributes values. With the inheritance technique,
we can use the fruit class to store information that applies
to all kinds of fruit, and keep apple or grape specific
attributes in their own classes. For example, we could have an attribute to
track how much of an apple is left after it’s partially eaten. Of course, this applies to
both attributes and methods. If a class has an attribute or
a method defined in it, inheriting classes will have the same
attributes and methods defined in them. But we can also get them to behave
differently depending on what we change. To explore this, let’s go back to
our piglet example and change it so that there’s a base animal class. In this code, we’ve defined
a general class called animal, which has an attribute to store
the sound that the animal makes. The constructor of the class takes the
name that will be assigned to the instance when it’s created. There’s also a speak method that prints
the name of the animal together with the sound the animal makes. Then, we have a piglet class that
inherits from the animal class. We set the value of the sound attribute
to oink in the piglet class, and that’s the only thing we’ve
modified from the original. Everything else is inherited. Let’s see this in action. Let’s define a new class that
also inherits from animal. How about a cow class? Cool, and to finish, let’s create an
instance of this class to make it speak. So you can see that we can easily define
new classes that inherit from the base animal class and
use both the attributes and methods that the animal class provides. Pretty cool, right? Let’s think of a different example, something closer to what you might
be doing at your day-to-day job. In a system that handles the employees at
your company, you may have a class called employee, which could have the attributes
for things like full name of the person, the username used in company systems, the
groups the employee belongs to, and so on. The employee class could have
methods to do a bunch of things, like check if an employee
belongs to a certain group, or create an email address based on
the name and username attributes. The system could also
have a manager class. A manager is an employee, but has
additional information associated with it, like the employees that
report to a specific manager. Are you starting to get an idea
of the power of inheritance? Inheritance lets you reuse code written
for one class in other classes. Next up, we’re going to talk about
a different way of reusing code.

Reading: Object Inheritance

Reading

Video: Composition

In this tutorial, the concept of inheritance is discussed, focusing on the “is a” rule to establish ancestry among objects. The example of a software package class and a repository class is used to illustrate a relationship where one class is not a child of the other. Instead, the repository class contains instances of the package class, showcasing the concept of composition.

The tutorial emphasizes the importance of initializing mutable attributes, such as dictionaries, in the constructor to avoid shared attributes among instances. The analogy of apples and worms is used to clarify this concept, highlighting the distinction between mutable and immutable attributes.

The repository class is developed with an add_package method to add packages to the dictionary. The calculation of the total size of the repository is demonstrated using a method that iterates through the packages in the dictionary, accessing their size attribute. The power of composition is emphasized, allowing the use of attributes and methods from other objects within the class.

The tutorial concludes by encouraging a review of the content and reassures viewers that understanding may require multiple viewings. It sets the stage for the next video, where Python modules will be discussed.

Composition in Python: A Powerful Tool for Object-Oriented Programming

Inheritance is a powerful tool in Python that allows us to create new classes based on existing ones. But what if you have a relationship between classes where one class isn’t a child of the other? This is where composition comes in.

Imagine you have a software package class that represents an installable program. This class has attributes like name, version, and size. You also have a repository class that stores all available packages. You want to know how many packages are in the repository and their total size.

Here, inheritance wouldn’t be the right fit. The repository isn’t a package, and the package isn’t a repository. Instead, the repository has packages.

This is where composition shines. The repository class will have an attribute (like a list or dictionary) containing package objects. We can then use the methods of those package objects to get the information we need.

Here’s how it works:

  1. Create a Repository Class:

Python

class Repository:
  def __init__(self):
    self.packages = {}

  def add_package(self, package):
    self.packages[package.name] = package

  def get_total_size(self):
    total_size = 0
    for package in self.packages.values():
      total_size += package.size
    return total_size
  1. Use the Package Class:

Python

class Package:
  def __init__(self, name, version, size):
    self.name = name
    self.version = version
    self.size = size

# Create some packages
package1 = Package("app1", "1.0", 100)
package2 = Package("game2", "2.0", 500)

# Add them to the repository
repository = Repository()
repository.add_package(package1)
repository.add_package(package2)

# Get the total size
total_size = repository.get_total_size()

print(f"Total size of all packages: {total_size} bytes")

This code defines a Repository class with a packages dictionary to store package objects. We also define a Package class with attributes like name, version, and size.

The add_package method adds a package to the dictionary, and the get_total_size method iterates through the packages and sums their sizes.

This is just a basic example, but it shows how composition allows you to use the functionality of other classes within your own code.

Benefits of Composition:

  • Code Reuse: You can leverage existing functionality without subclassing.
  • Flexibility: You can combine different classes for unique needs.
  • Encapsulation: You can hide internal details of other classes.

Composition vs Inheritance:

FeatureCompositionInheritance
Relationship“Has-a”“Is-a”
Code ReuseThrough methodsThrough inheritance
FlexibilityMore flexibleLess flexible
EncapsulationBetter encapsulationLess encapsulation

Remember:

  • Use composition when you have a “has-a” relationship between classes.
  • Prefer composition over inheritance for code reuse and flexibility.
  • Use both techniques to create robust and maintainable object-oriented programs.

I hope this explanation, along with the code examples, helps you understand composition in Python. Feel free to experiment and explore different ways to use this powerful technique in your projects!

We talked about how inheritance creates an ancestry
for our objects. To check for this ancestry, we can use the is a rule. An apple is a fruit, a piglet is an animal. They inherit the
attributes and methods of their parent class
and so they allow us to reduce code duplication, but what if you have a
relationship between classes, where one class isn’t
a child of the other? Sounds confusing? Let’s check out an example to get a
better idea of this. Say we have a package
class that represents a software package which could be installed on every
machine on our network. This class has a lot of
information on the software, like the name, the version, the size, and more. We also have a repository
class that represents all the packages that we have available for
installation internally. In this class, we want to know how many packages there are and what’s the total size
of all the packages. In this case, the
repository isn’t a package and the package
isn’t a repository. Instead, the repository
contains packages. To model this within our code, the repository class will have an attribute that could be
a list or a dictionary, which will contain instances
of the package class. So for this scenario, we’ll make use of the code in the other classes by
calling their methods. This is what’s
called composition. Let’s see this in action. We’ll first create a
repository class that starts with an empty dictionary of
packages when it’s created. The dictionary will have the
names of the packages as keys and the package
objects as values. Nice. We have our class, which starts with an empty
dictionary of packages. You might be wondering why
we are adding the dictionary in the constructor instead
of directly to the class. The answer to this might be
a bit tricky to understand. So let’s go back to our
juicy apple example to help make sure this is clear. We defined earlier a class called apple and set some basic
attributes for it, like color and flavor. All instances of the
apple class will be initialized with the values that we preset for those attributes. If we change the color of one
apple from red to golden, we substitute the old
value with the new one. Super-important to
remember, this action happens only for that
particular instance. But what if this apple
has a worm in it? What if we wanted our apple class to also have a list of worms? If we created the list when
constructing the class, then all instances of
the apple class would have the same exact list. So if we added a
worm to the list, it would get added
to the one list that’s shared by all instances. To avoid this, we need to create the list at the time of
creating the instance, instead of when
creating the class. By doing this, each
instance will have its own list independent
of the others. This happens with all
attributes that are mutable, because when we modify
immutable attribute, we’re not replacing a
value with another, we’re changing the contents
of the original attribute. In our repository case, the packages attribute is a
dictionary, which is mutable. We’ll be modifying its contents by adding and removing
elements in it. If we created it at
the class level, all instances of the
repository class would use the same dictionary and items added or removed would affect all instances
at the same time. If that’s still a bit
confusing, don’t worry. I was also confused
the first time I came across it. Just
take your time. Re-watch this video if you need and remember
this rule of thumb, always initialize mutable
attributes in the constructor. So great, we’ve got
our dictionary, but how will we add
packages to it? We’ll create an
add_package method. Now, we can add packages
to the dictionary. We could also write
a similar method to remove the packages, but I bet you can work
that out without my help. Let’s do something more
interesting instead. We said that the packages had
a size attribute that holds the size in bytes that the
software package requires. So how could we calculate the size of the whole repository? We need to iterate over the packages contained
in the dictionary, adding up all their sizes. I’d go something like this. We’re going to add
up all the sizes. So the first thing we need to do, is create a result variable that we’ll use to
sum up the values. Awesome. We have our
result initialized. We now need to go through all the packages
in the dictionary. Remember, the keys
are the package names and the values are
the package objects. For our calculation, we only
care about the objects. So we’ll retrieve them with
the values dictionary method. Now, for each package, we want to add the size
to the result variable. Nice. We’re almost done. We just need to return
the result now. Take a look at the method
we’ve just written. It’s a method inside
the repository class, that’s making use of
the values method in the dictionary class and it’s accessing the size attribute
in the package class. That is the power of composition. When we have other
objects as attributes, we can use all their
attributes and methods to get our own
code to do what we want. Wow. That was pretty complex. Chances are, you won’t get it the first time around.
Most of us don’t. So if you’re worried you
might have missed something, take your time to
review the contents. We want you to feel
confident before moving on. When you’re ready, meet
me in the next video, where we’re going to talk
about a different kind of code we use using Python modules.

Reading: Object Composition

Reading

You can have a situation where two different classes are related, but there is no inheritance going on. This is referred to as composition — where one class makes use of code contained in another class. For example, imagine we have a Package class which represents a software package. It contains attributes about the software package, like name, version, and size. We also have a Repository class which represents all the packages available for installation. While there’s no inheritance relationship between the two classes, they are related. The Repository class will contain a dictionary or list of Packages that are contained in the repository. Let’s take a look at an example Repository class definition:

>>> class Repository:
...      def __init__(self):
...          self.packages = {}
...      def add_package(self, package):
...          self.packages[package.name] = package
...      def total_size(self):
...          result = 0
...          for package in self.packages.values():
...              result += package.size
...          return result

In the constructor method, we initialize the packages dictionary, which will contain the package objects available in this repository instance. We initialize the dictionary in the constructor to ensure that every instance of the Repository class has its own dictionary.

We then define the add_package method, which takes a Package object as a parameter, and then adds it to our dictionary, using the package name attribute as the key.

Finally, we define a total_size method which computes the total size of all packages contained in our repository. This method iterates through the values in our repository dictionary and adds together the size attributes from each package object contained in the dictionary, returning the total at the end. In this example, we’re making use of Package attributes within our Repository class. We’re also calling the values() method on our packages dictionary instance. Composition allows us to use objects as attributes, as well as access all their attributes and methods.

Video: Python Modules

  • Python provides modules to organize code for specific tasks.
  • Modules are separate files containing functions, classes, and data.
  • The import keyword is used to import modules.
  • The Python Standard Library includes many ready-to-use modules.
  • We explored examples using the random and datetime modules.
  • random provides functions for generating random numbers and making choices.
  • datetime provides classes and methods for handling dates and times.
  • We can access attributes and methods of instances to manipulate their data.
  • Modules offer powerful tools and organization for Python development.
  • Further details and reference materials are available online.
  • We will explore creating custom modules in future courses.
  • For now, focus on using existing Python modules to enhance your code.

What are Python modules?

Python modules are files containing Python code that can be imported and used in other Python programs. They are a fundamental part of the Python language and are used to organize and reuse code.

Why use modules?

There are several reasons to use modules in your Python programs:

  • Organize code: Modules help you organize your code by grouping related functions, classes, and variables into a single file. This makes your code easier to read, maintain, and reuse.
  • Prevent code duplication: Modules allow you to reuse code across different programs. This saves you time and effort, and it also helps to ensure that your code is consistent and correct.
  • Extend Python functionality: Modules can be used to extend the functionality of Python by providing new functions and classes.

How to import modules

There are two ways to import modules in Python:

  • Import the entire module:

Python

import module_name

This will import all of the functions, classes, and variables from the module into your program. You can then access them using the module name as a prefix.

  • Import specific items from a module:

Python

from module_name import item1, item2, ...

This will import only the specified items from the module into your program. You can then access them directly, without using the module name as a prefix.

Standard Library

Python comes with a large collection of standard modules that are available for you to use. These modules provide a wide range of functionality, including:

  • File processing: Reading and writing files
  • Network access: Sending and receiving data over the network
  • Regular expressions: Matching patterns in text
  • Date and time: Working with dates and times
  • Math: Performing mathematical calculations
  • Random: Generating random numbers

Examples of using modules:

Here are a few examples of how to use modules in your Python programs:

  • Import the math module:

Python

import math

# Calculate the square root of 2
square_root = math.sqrt(2)

print(square_root)
  • Import the datetime module:

Python

from datetime import datetime

# Get the current date and time
current_time = datetime.now()

print(current_time)
  • Import a custom module:

Python

from my_module import my_function

# Call a function from the custom module
result = my_function(10, 20)

print(result)

Creating your own modules

You can create your own modules to organize your code and make it easier to reuse. To create a module, simply create a new Python file and save it with a .py extension.

Summary

Modules are a powerful tool for organizing and reusing code in your Python programs. They can help you to write cleaner, more efficient, and more maintainable code.

Additional resources:

This tutorial has provided a basic introduction to Python modules. As you continue to learn Python, you will find that modules are an essential tool for writing professional and effective code.

So far, we’ve been
using the features that are baked into
the Python language. The basic statements
like if, for, while, or the definition of
functions or classes, are part of the language and ready for us to use
whenever we need them. The same goes for integers, floats, strings, lists,
and dictionaries. They’re all part of the
basic Python language because they’re used so often. Of course, this isn’t enough to get interesting things done. We’ll need a lot of
additional tools like being able to send
packets over the network, read files from our
machine, process images, or who knows what
you might want to do to make your work
more effective. To organize the code we need
to perform tasks like these, Python provides an
abstraction called a module. Modules can be used to
organize functions, classes, and other data
together in a structured way. Internally, modules
are set up through separate files containing the necessary classes
and functions. Python already comes with a bunch of ready-to-use modules. All these modules are
contained in a group called the Python
standard library. Let’s see how we can
use some of them. First, we’ll use
the import keyword to import the random module. This module is useful
for generating random numbers or
making random choices. Now that we’ve
imported the module, let’s use a function provided by this module called randint. This function receives two
parameters and generates a random number between the
two parameters that we pass. In this case, we’re generating a random number between 1 and 10. As you can see, this
function returns different numbers each time it’s called. Pretty fun, right? The syntax used for calling
a function provided by a module is similar to calling a method provided by a class. It uses a dot to
separate the name of the module and the function
provided by that module. Let’s try using a different
module, the datetime module. We use this for handling
dates and times. Now, let’s get the current date. If you’re wondering why we
have a doubled datetime, it’s because the datetime module provides a datetime class, and the datetime class gives
us a method called now. This now method generates an instance of the datetime
class for the current time. We can operate on this instance of datetime in a bunch of ways. Let’s check out a
couple of examples. When we call print with an instance of the
datetime class, we see the date printed
in a specific format. Behind the scenes, the
print function is calling the str method of the datetime class which formats it in the way
that we see here. We can also access the instance through its
attributes and methods. For example, we can look at the individual parts of the date like the year, like this. The datetime module provides more classes than
the datetime class. For example, we can use
the timedelta class to calculate a date in the future or in the past. Let’s try this out. In this case, we’re
creating an instance of the timedelta class with
a value of 28 days, then we’re adding it
to the instance of the datetime class that we already had and printing
out the result. There’s a lot more things available in the datetime
and random modules. If you’re interested
in learning more, you can read the whole reference, it’s available online and we’ll include a link in
the next reading. This is just a sneak peek into what you could
do with modules. You can also develop your own. We’ll talk more about
that in a later course. For now, just focus on using
existing Python modules.

Let’s say we want to use the Keras deep learning module. Upon running the script, an error is printed stating the Keras module could not be found. What might we have missed?

We need to import the Keras module

You got it! We must use the import keyword to import the module before it can be used.

Reading: Augmenting Python with Modules

Reading

Module Review


Video: OOP Wrap Up

Main Points:

  • Object-oriented programming (OOP) uses classes to represent real-world concepts.
  • Instances of classes are called objects.
  • Objects have attributes (information) and methods (actions).
  • Dot notation is used to access attributes and methods of objects.
  • Inheritance allows organizing objects hierarchically.
  • Composition allows objects to contain other objects.

Key Takeaways:

  • OOP helps model real-world concepts in code.
  • Objects promote code clarity and reusability.
  • Objects group functions and data related to specific entities.

Examples:

  • Sysadmins use objects for users and their accounts.
  • Objects group functions based on the data they manipulate.

Next Steps:

  • Complete a graded assessment to demonstrate understanding of OOP concepts.

[MUSIC] Object orientation is
not easy to understand. So congratulations on getting
through these Concepts. Let’s quickly recap the main
Concepts we’ve just covered. We’ve learned that in an object-oriented
language like python real-world concepts are represented by classes. We know that instances of classes
are usually called objects. That objects have attributes which are
used to store information about them and we can make objects do work
by calling their methods. We’ve also learned that we can access
attributes and methods using dot notation. We then dove into objects can
be organized by inheritance. And how they can be contained inside
each other using composition. Wow, that really is a lot of new stuff. Congratulations on sticking with it. Objects are a great way for
programmers to model real world Concepts. They let us have functions that work
on specific things like reading a file, setting the subject for an email, calculating the size of
a repository of packages and so on. Isn’t it cool to see how all
of this is coming together? As a sysadmin the objects ideal with
the most represent individual users and their accounts. I use them to group lots of different
properties that help me turn abstract code into tangible interactions. I also use objects in my code to group
functions based on the data they act upon. For example, I recently needed to write a
bunch of functions that were all operating on some specific file attributes. So I used a class to group all those
functions making my code clearer and more reusable. Super helpful, right? I thought so. Next up, we’ve got a graded assessment
to help you show off everything you’ve learned.