Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Hint: You can adjust the default video playback speed in your account settings.
Hint: You can set your subtitle preferences in your account settings.
Sorry! Looks like there’s an issue with video playback 🙁 This might be due to a temporary outage or because of a configuration issue with your browser. Please see our video player troubleshooting guide to resolve the issue.

Python Basics: Object-Oriented Programming (Summary)

Now you’re ready to use object-oriented programming (OOP) to write more readable and maintainable code in Python! With OOP, you can create blueprints for objects that contain both data and behaviors.

In this video course, you’ve learned how to:

  • Create a class
  • Use classes to create new objects
  • Instantiate classes with attributes and methods

To learn more about OOP, check out:

OOP is a big topic, and Real Python has several resources to help you expand your skill set. There’s even a learning path that’ll help you solidly grasp the fundamentals of OOP so that you can make your programs easier to write and maintain.

To reinforce what you’ve learned here, complete the quiz in the next lesson. Then, head over to Python Basics Exercises: Object-Oriented Programming.

Then, keep growing your skill set as a programmer by continuing with the other Python Basics courses and getting yourself a copy of Python Basics: A Practical Introduction to Python 3.

Download

Sample Code (.zip)

1.5 KB
Download

Course Slides (.pdf)

5.2 MB

00:00 So you’re at the end of Object-Oriented Programming. That was a lot of information. And again, you have to practice this stuff for it to really sink in. A lot of it might seem like, what do I use that for?

00:10 Or in what cases is that useful? Can you give me a real-life application for this? And there are loads. You can use it everywhere. But it really won’t start to click until you yourself start using it or reading other people’s code and understanding why they’re using it in this case and how it’s being used. You’ve learned about the object-oriented programming paradigm.

00:32 Now, paradigm is just a fancy word for saying style or way of doing things. You’ve defined a class. You’ve added attributes to that class. You’ve added methods. You’ve used objects created from classes.

00:47 If you’re looking to dive in a bit deeper, Real Python has some additional resources for you to get stuck into. For instance, Object-Oriented Programming (OOP) in Python 3, which is a very in-depth article about everything you want to know about object-oriented programming in Python 3.

01:04 That itself also has another video course that goes into more detail than this one. You have Getters and Setters: Manage Attributes in Python. You’ll have seen how in the class instances in this course, you’ve been referencing attributes and setting attributes with dot notation and assignment directly, but the getting and setting of these attributes is a big topic.

01:27 If you’re curious about more of that and how to customize the behavior of that—maybe every time you get an attribute, you want something to happen. More likely, it is when you set an attribute you want something to happen.

01:38 Maybe it needs to update other attributes. Check out that article. And finally, we’ve got Operator and Function Overloading in Custom Python Classes. Now, this is the dunder methods we are talking about, where you can customize how your instances behave with certain operators, like + (addition), - (subtraction), == (equals to), > (greater than), < (less than). Now, Real Python’s content on object-oriented programming, classes, and instances is not limited to these three, but is a huge subject.

02:12 And that was an introduction to object-oriented programming. Thank you for watching and following along.

risko619 on July 19, 2023

I really didnt understand this, nor was I able to run the exercise. Feeling a bit overwhelmed with all this stuff now

Martin Breuss RP Team on July 20, 2023

@risko619 aah, yes OOP is a complex programming topic that can totally take a while to sink in. Best thing to do is to keep reading or watching content that covers it, and just mess around trying to solve the challenges. Even if you don’t get it all the way, putting in some practice time and (figuratively) banging your head against that OOP wall helps to make it click eventually.

Is there anything specific that you got stuck with? Did you check out the other resources that Ian mentioned in this lesson?

Sneha Nath on Sept. 16, 2023

Excellent course and very informative! Thank you so much.

alphafox28js on Nov. 19, 2023

I wish they would have explaned how to print all the functions of the class at with one iteration…

class Dog:
    species = "Canis Familiaris"
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        return (f"{self.name}, {self.age}")
        Pepper = Dog('Pepper',4)

    # Instance Method:
    def description(self):
        return (f"{self.name} is {self.age} years old, and she is the greatest dog alive!")
        Pepper.description()

    def speak(self, sound):
        return f"{self.name} says {sound}"
        Pepper.speak()

Pepper = Dog('Pepper',4)
Pepper.description()
Pepper.speak('Woof! Woof!')

Martin Breuss RP Team on Nov. 22, 2023

I’m not sure I correctly understood your question, but generally, you can inspect a Python object using dir().

That gives you everything though, so you might want to filter the results, e.g. like shown below if you’re only looking for instance methods you defined in the class:

print(
    [
        method
        for method in dir(Pepper)
        if callable(getattr(Pepper, method))
        and not method.startswith("__")
    ]
)

When you run this code on the Pepper object you created above, then you’ll get the two instance method names as a result:

['description', 'speak']

Alternatively, you could also look into the built-in inspect module.

Did this answer your question, or did you mean something else?

alphafox28js on Nov. 23, 2023

Good Morning @Martin Breuss… So basically, when trying to instantiate the Attributes of the class via Instances. I keep running into a problem when separting Main_Class vs SubClass vs code variable assignments.

example. would be something like this… New Window_Script 1: Main_Class

class Animal:
    species = "Mammals"

    def __init__(self, name, age): #type: [Canine. breed: breed of canine, internal_temp: warm/cold blooded.]
        self.name = name
        self.age = age

# Instance Methods:
    def __str__(self):
        return f"{self.name} is {self.age} years old."

    def speak(self, sound):
        return f"Sound of {self.name}; {sound}."

class Pitbull(Animal):
    def speak(self, sound = "GgggrrrWOOF!"):
        return f"{self.name} says {sound}."

class Shephard(Animal):
    pass

class Doberman(Animal):
    pass

New Window_Script 2: Subscript_Class

from P_Class_Animal import Animal

class Pitbull(Animal):
    def speak(self, sound= "ggggrrrWoooF!"):
        return f"{self.name} says {sound}"

class Shephard(Animal):
    pass

class Doberman(Animal):
    pass

New Window_Script 3: Variables of Animial Classes

from P_Class_Animal import Animal   
from C_Class_Animal import Animal, Pitbull

#instantiate Parent Class
Dog = Animal('Wolf', 2)
print(Dog)

#instantiate child/sub classes which points to the breed
Athena = Pitbull('Athena', 3,)
Athena.speak()
print(Athena)

While I was able to get the above working (to a point), still will not let me call .species(), the problem comes when I try to essentially create a dictionary for the Animal Class and define Multiple Types of Dogs in the Child Class with a variety of attributes

alphafox28js on Nov. 23, 2023

Now,lets say I added the following, and if i were to instantiate lets say Pitbull as follows, it has yet to function properly, not sure how to correct. New_Window P_Class

class Animal:

    def __init__(self, name, age, height, coat): #type: [Canine. breed: breed of canine, internal_temp: warm/cold blooded.]
        self.name = name
        self.age = age
        self.height = height
        self.coat = coat

# Instance Methods:
    def __str__(self):
        return f"{self.name} is {self.age} years old."

    def speak(self, sound):
        return f"Sound of {self.name}; {sound}."

New_Window C_Class

from P_Class_Animal import Animal

class Pitbull(Animal):
    def speak(self, sound= "ggggrrrWoooF!"):
        return f"{self.name} says {sound}"
    def height(self):
        return super().height()

class Shephard(Animal):
    pass

class Doberman(Animal):
    pass

New_Window Animal Variables:

from P_Class_Animal import Animal   
from C_Class_Animal import Animal, Pitbull

#instantiate Parent Class
Dog = Animal('Wolf', 2)
print(Dog)

#instantiate child/sub classes which points to the breed
Pitbull = Pitbull('Pitbull', 3,)
Pitbull.speak()
Pitbull.height()
print(Pitbull)

This returns the error of: TypeError. Wanting me to instantiate two more positional arguments. In my mind, this should not be the case because they were defined in the P_Class…What am I missing here?

TypeError: Animal.__init__() missing 2 required positional arguments: 'height' and 'coat'

Even adding the two additional arguments, it still returns TypeError

from P_Class_Animal import Animal   
from C_Class_Animal import Animal, Pitbull

#instantiate Parent Class
Dog = Animal('Wolf', 2)
print(Dog)

#instantiate child/sub classes which points to the breed
Pitbull = Pitbull('Pitbull', 3, ".5m", 'Short')
Pitbull.speak()
Pitbull.height()
print(Pitbull)

alphafox28js on Nov. 23, 2023

BTW, Thank You for the Help and Support on this. I truly appreciate it! Also worth noting, I had to add path support to JSON to get the above to work in VS Code via Python.

settings.json

{
    "python.analysis.extraPaths": [
        "C:/Program Files (x86)/Google/google_appengine",
        "C:/Program Files (x86)/Google/google_appengine/lib/flask-0.12"]

}
{
    "python.analysis.extraPaths": ["PYTHONPATH": "${workspaceFolder/../extern"]
}

launch.json

{
    "configurations": [
        {
            "name": "Python: File",
            "type": "python",
            "request": "launch",
            "program": "${file}",
            "justMyCode": true,
            "env": {
                "PYTHONPATH": "${workspaceFolder/../extern"
            }
        },
        {
            "name": "Python: File",
            "type": "python",
            "request": "launch",
            "program": "${file}",
            "justMyCode": true
        }
    ]
}

Hopefully someone down the line will find this useful :)

Martin Breuss RP Team on Dec. 12, 2023

@alphafox28js you probably figured this out at this point, but let me try to recap a few points, maybe it’s helpful for you or someone else reading over this.

Naming Conventions

According to PEP 8, the naming convention for modules and packages is to:

use a short, lowercase word or words. Separate words with underscores to improve readability.

Currently, you’ve used uppercase words for your module (=file) names, which is uncommon. It doesn’t break anything, but it’ll make your code look more professional when you stick to the naming convention.

Overwriting Names

In your first comment, you’re showing the same names multiple times in different files (if I understand your text correctly).

For example, you’re defining Pitbull multiple times in different files:

  1. In a file that you probably called P_Class_Animal.py
  2. In a file that you probably called C_Class_Animal.py

In the third file, you also double up your import of the Animal class, which won’t have an effect:

from P_Class_Animal import Animal   
from C_Class_Animal import Animal, Pitbull

The second import of Animal will overwrite the name, which renders the first line unnecessary.

Note: This won’t make any troubles in the code you’re showing. You’re importing Animal from P_Class_Animal in C_Class_Animal, so the Animal object you’re referencing twice in this third file is actually the same object.

However, it makes your code harder to read and reason about. It’s always better to cut it down to the essentials, which will help you to avoid bugs and make it more straightforward to keep track of what’s going on in your code.

You always just need to define any name you want to use once. If you define it in a different module, then you can import it into another module where you want to use it.

Instantiation

The TypeError about missing positional arguments is a result that you’re not passing the required amount of arguments when you’re instantiating an object from your class.

You’ve defined that an Animal requires four positional arguments:

class Animal:
    def __init__(self, name, age, height, coat):
        self.name = name
        self.age = age
        self.height = height
        self.coat = coat

Later, you create a child class that inherits from Animal:

class Pitbull(Animal):
    pass  # More on the code here a bit later

Inheriting from a parent class (without overwriting the initializer) means that your child class will need as many arguments as the parent class—in this case that’s four.

Therefore, when you attempt to initialize either an Animal or a Pitbull object with only two arguments, you’ll get the mentioned error:

>>> Dog = Animal('Wolf', 2)
TypeError: Animal.__init__() missing 2 required positional arguments: 'height' and 'coat'

>>> Pitbull = Pitbull('Pitbull', 3)
TypeError: Animal.__init__() missing 2 required positional arguments: 'height' and 'coat'

You can see from the error messages that Pitbull is also calling .__init__() of Animal.

You mention:

In my mind, this should not be the case because they were defined in the P_Class…What am I missing here?

You only defined the method signature of the Animal class’ .__init__(), where you define that it needs to have four positional arguments.

When you go to call that method, which you do by instantiating an object of either class, then you also need to provide values for these required arguments. Does that clarify the situation?

So… given the info above, do you understand why the code in your last code example still returns the same TypeError:

from P_Class_Animal import Animal   
from C_Class_Animal import Animal, Pitbull

#instantiate Parent Class
Dog = Animal('Wolf', 2)
print(Dog)

#instantiate child/sub classes which points to the breed
Pitbull = Pitbull('Pitbull', 3, ".5m", 'Short')
Pitbull.speak()
Pitbull.height()
print(Pitbull)

Let me know :)

Method vs Argument

Finally, there’s another bug in your code that you may or may not have encountered, and it’s hiding in your definition of the Pitbull child class:

class Pitbull(Animal):
    def speak(self, sound= "ggggrrrWoooF!"):
        return f"{self.name} says {sound}"
    def height(self):
        return super().height()

Currently, you’re using Python’s super() to call a .height() method from the parent Animal class.

However, in the code you provided, you never defined such a method. You wrote an initializer function that collects a value for .height, but that value won’t be callable. It’s an instance attribute and not an method.

So, even if you correctly initialize a Pitbull object, when you’ll try to call .height() on it, you’ll get another error:

>>> Pitbull = Pitbull('Pitbull', 3, ".5m", 'Short')

>>> Pitbull.speak()
'Pitbull says ggggrrrWoooF!'

>>> Pitbull.height()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object is not callable

If you want, you can give it a try to explain to me what’s happening here. :)

Ok that’s it, nice work going through this course in such depth and thinking about the concepts covered. Best way to learn is to think, try, and talk about it! :D

Become a Member to join the conversation.