> For the complete documentation index, see [llms.txt](https://python4ml.cavementech.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://python4ml.cavementech.com/oops.md).

# OOPS

Python is actually an object; when you're working with Python, you are creating and manipulating objects. As you continue to learn to work with data in Python, you'll encounter objects everywhere:

* NumPy and pandas — the two libraries essential to working with data in Python: both define a number of their own object types
* Matplotlib — which you use to create data visualizations: uses object types to define the charts you create
* Scikit-learn — which you use to create machine learning models: uses object types to represent the models you train and use to make predictions.&#x20;

In OOP, objects have types, but instead of "type" we use the word **class**. Here are the correct names for each of these classes:

* String *class*
* List *class*
* Dictionary *class*

In everyday English, the word class refers to a group of similar things. In OOP, we use the word similarly — a class is a type of object.

When talking about programming, we often use the words "type" and "class" interchangeably, but "class" is more formally about objects. Throughout this lesson, we'll be using "class" as we learn about OOP.

### Defining a Class

We define a class similarly to how we define a function:

<figure><img src="/files/lS9Ia9h8G03UEuZzHxNC" alt=""><figcaption></figcaption></figure>

Notice that the class definition doesn't have parentheses `()`. This is optional for classes, and the convention is to not use them. Similarly, we indent the body of our class like a function's body.

The rules for naming classes are the same as naming functions and variables:

1. We must use only letters, numbers, or underscores.
   * We cannot use apostrophes, hyphens, whitespace characters, etc.
2. Class names can't begin with a number.

That said, there is a convention for variables and functions in Python called **Snake Case**, which uses all lowercase letters and underscores: `like_this`. With classes, the convention is to use **Camel Case**, where no underscores appear between words, and we capitalize the first letter of each word: `LikeThis`.

In OOP, we use **instance** to describe each different object. Let's look at an example:

<figure><img src="/files/UjbQ52jnid2skiSn3tEQ" alt=""><figcaption></figcaption></figure>

We can say the same of Python strings. We might create two Python strings, and they can hold different values, but they work the same way:

<figure><img src="/files/8hMbIw8B1L5In0KlcIw5" alt=""><figcaption></figcaption></figure>

Once we have defined our class, we can create an object of that class, which we call **instantiation**. If you create an object of a particular class, the technical phrase for what you did is to "**Instantiate** an object of that class." Let's learn how to instantiate an instance of our new class:

```
my_class_instance = ExampleClass()
```

That single line of our code actually did two things:

* Instantiated an object of the class `ExampleClass`
* Assigned that instance to the variable named `my_class_instance`

### Creating Methods

Relating back to our Tesla metaphor, an object of the Tesla "class" can do things like "unlock" and "accelerate." Similarly, Python strings have methods that can replace substrings, convert the case of the object, and more:

<figure><img src="/files/5RIHM2CWD1DrVRDlPkJ1" alt=""><figcaption></figcaption></figure>

*You can think of methods like special functions that belong to a particular class*. This is why we call the replace method `str.replace()`— because the method belongs to the `str` class.

While we can use a function with any object, each class has its own set of methods. Let's look at an example using some of Python's built-in classes:

```
my_string = "hello"   # an object of the str class
my_list = [1, 2, 3]   # an object of the list class
```

The list object has the `list.append()` method:

```
my_list.append(4)
print(my_list)
```

The syntax for creating a method is almost identical to creating a function, except we indent it within our class definition. This is how we would define a simple method:

```
class ExampleClass:
    def greet():
        return "hello"
```

### Passing Arguments

Let's create a method that accepts a string argument and then returns that string. The first argument will always be the object itself, so we'll specify `self` as the first argument and the string as our second argument:

```
class ExampleClass:

    def return_string(self, string):
        return string
```

Let's instantiate an object and call our method. Notice how when we call it, we leave out the `self` argument, just like we did on the previous screen:

```
mc = ExampleClass()
result = mc.return_string("Hey there!")
print(result)
```

```
Hey there!
```

### **constructor**

The init method — also called a **constructor** — is a special method that runs when we create an instance so we can perform any tasks to set up the instance.

The init method has a special name that starts and ends with two underscores: `__init__()`. Let's look at an example:

```
class ExampleClass:

    def __init__(self, string):
        print(string)

mc = ExampleClass("Hola!")
```

```
Hola!
```

Let's see how it works:

* We defined the `__init__()` method inside our class as accepting two arguments: `self` and `string`.
* Inside the `__init__()` method, we called the `print()` function on the `string` argument.
* When we instantiated `mc` — our `ExampleClass` object — we passed `"Hola!"` as an argument. The init function ran immediately, displaying the text "Hola!"

It's unusual to use `print()` inside an init method, but it helps us understand that the method has access to any arguments passed when we instantiate an object.

The init method's most common usage is to store data as an attribute:

```
class ExampleClass:

    def __init__(self, string):
        self.my_attribute = string

mc = ExampleClass("Hola!")
```

When we instantiate our new object, Python calls the init method, passing in the object:

<figure><img src="/files/GqaJe7wdFlzk854a0pJq" alt=""><figcaption></figcaption></figure>

Our code didn't result in any output, but now we have stored `"Hola"` in the attribute `my_attribute` inside our object. Like methods, we access attributes using dot notation, but attributes don't have parentheses like methods do. Let's use dot notation to access the attribute:

```
print(mc.my_attribute)
```

```
Hola!
```

The table below summarizes some of the differences between attributes and methods:

<figure><img src="/files/QzKsRRfgiK6iyscerinf" alt=""><figcaption></figcaption></figure>

Let's take a moment to summarize what we've learned so far:

* The power of objects is in their ability to store data.
* Data is stored as attributes inside objects.
* We access attributes using dot notation.
* To give attributes values when we instantiate objects, we pass them as arguments to a special method called `__init__()`, which runs when we instantiate an object.
