# Defining and using custom classes When I was getting started in Python I learned to make classes for tkinter GUI's before I understood how they work. Everything I did with classes worked, but I didn't understand how. Hopefully you'll first learn to understand classes, and then learn to use them. ## What are classes? Python comes with many classes that we know already. ```python >>> str >>> int >>> list >>> dict >>> ``` Calling these classes as if they were functions makes a new **instance** of them. For example, `str()` makes a `str` instance, also known as a string. ```python >>> str() '' >>> int() 0 >>> list() [] >>> dict() {} >>> ``` We can also get an instance's class with `type()`: ```python >>> type('') >>> type(0) >>> type([]) >>> type({}) >>> ``` Let's say that we make a program that processes data about websites. With a custom class, we're not limited to `str`, `int` and other classes Python comes with. Instead we can define a Website class, and make Websites and process information about websites directly. Defining our own types like this is called **object-orientated programming**. ## First class In Python, `pass` does nothing. ```python >>> pass >>> ``` Let's use it to define an empty class. ```python >>> class Website: ... pass ... >>> Website >>> ``` The `pass` is needed here, just like [when defining functions that do nothing](defining-functions.md#first-functions). Note that I named the class `Website`, not `website`. This way we know that it's a class. Built-in classes use lowercase names (like `str` instead of `Str`) because they are faster to type, but use CapsWord names for your classes. Now we can make a Website instance by calling the class. ```python >>> github = Website() >>> github <__main__.Website object at 0x7f36e4c456d8> >>> type(github) >>> ``` We can say that `github` is "a Website instance", "a Website object" or "a Website". All of these mean the same thing. Now we can attach more information about github to our Website. ```python >>> github.url = 'https://github.com/' >>> github.founding_year = 2008 >>> github.free_to_use = True >>> ``` We can also access the information easily. ```python >>> github.url 'https://github.com/' >>> github.founding_year 2008 >>> github.free_to_use True >>> ``` As you can see, our Website is mutable, like lists are, not immutable like strings are. We can change the website in-place without creating a new Website. `url`, `founding_year` and `free_to_use` are not variables, they are **attributes**. More specifically, they are **instance attributes**. The biggest difference is that we need to use a dot for setting and getting values of attributes, but we don't need that with variables. Modules also use instance attributes for accessing their content. For example, when we do `random.randint`, `random` is a module instance and `randint` is one of its attributes. If we make another Website, does it have the same `url`, `founding_year` and `free_to_use`? ```python >>> effbot = Website() >>> effbot.url Traceback (most recent call last): File "", line 1, in AttributeError: 'Website' object has no attribute 'url' >>> ``` It doesn't. We'd need to define the attributes for effbot also. The attributes are stored in a dictionary called `__dict__`. It's not recommended to use it for code that needs to be reliable, but it's a handy way to see which attributes the instance contains. ```python >>> github.__dict__ {'free_to_use': True, 'founding_year': 2008, 'url': 'https://github.com/'} >>> effbot.__dict__ {} >>> ``` ## Class attributes What happens if we set an attribute of the `Website` class to some value instead of doing that to an instance? ```python >>> Website.is_online = True >>> Website.is_online True >>> ``` Seems to be working, but what happened to the instances? ```python >>> github.is_online True >>> effbot.is_online True >>> ``` What was that? Setting `Website.is_online` to a value also set `github.is_online` and `effbot.is_online` to that value! Actually, `is_online` is still not in github's or effbot's `__dict__`. github and effbot get that attribute directly from the `Website` class. ```python >>> github.__dict__ {'free_to_use': True, 'founding_year': 2008, 'url': 'https://github.com/'} >>> effbot.__dict__ {} >>> ``` `Website.is_online` is `Website`'s class attribute, and in Python you can access class attributes through instances also, so in this case `github.is_online` points to `Website.is_online`. That can be confusing, which is why it's not recommended to use class attributes like this. Use instance attributes instead, e.g. `github.is_online = True`. ## Functions and methods Let's [define a function](defining-functions.md) that prints information about a website. ```python >>> def website_info(website): ... print("URL:", website.url) ... print("Founding year:", website.founding_year) ... print("Free to use:", website.free_to_use) ... >>> website_info(github) URL: https://github.com/ Founding year: 2008 Free to use: True >>> ``` Seems to be working. We should be able to get information about all websites, so maybe we should attach the `website_info` function to the Website class? ```python >>> Website.info = website_info >>> Website.info(github) URL: https://github.com/ Founding year: 2008 Free to use: True >>> ``` It's working, but `Website.info(github)` is a lot of typing, so wouldn't `github.info()` be much better? ```python >>> github.info() URL: https://github.com/ Founding year: 2008 Free to use: True >>> ``` What the heck happened? We didn't define a `github.info`, it just magically worked! `Website.info` is our `website_info` function, so `github.info` should also be the same function. But `Website.info` takes a `website` argument, which we didn't give it when we called `github.info()`! But is `github.info` the same thing as `Website.info`? ```python >>> Website.info >>> github.info > >>> ``` It's not. Instead, `github.info` is a **method**. If we set a function as a class attribute, the instances will have a method with the same name. Methods are "links" to the class attribute functions. So `Website.info(github)` does the same thing as `github.info()`, and when `github.info()` is called it automatically gets `github` as an argument. In other words, **`Class.method(instance)` does the same thing as `instance.method()`**. This also works with built-in classes, for example `'hello'.lower()` is same as `str.lower('hello')`. ## Defining methods when defining the class Maybe we could define a method when we make the class instead of adding it later? ```python >>> class Website: ... def info(self): # self will be github ... print("URL:", self.url) ... print("Founding year:", self.founding_year) ... print("Free to use:", self.free_to_use) ... >>> github = Website() >>> github.url = 'https://github.com/' >>> github.founding_year = 2008 >>> github.free_to_use = True >>> github.info() URL: https://github.com/ Founding year: 2008 Free to use: True >>> ``` It's working. The `self` argument in `Website.info` was `github`. You could call it something else too such as `me`, `this` or `instance`, but use `self` instead. Other Python users have gotten used to it, and the official style guide recommends it also. We still need to set `url`, `founding_year` and `free_to_use` manually. Maybe we could add a method to do that? ```python >>> class Website: ... def initialize(self, url, founding_year, free_to_use): ... self.url = url ... self.founding_year = founding_year ... self.free_to_use = free_to_use ... def info(self): ... print("URL:", self.url) ... print("Founding year:", self.founding_year) ... print("Free to use:", self.free_to_use) ... >>> github = Website() >>> github.initialize('https://github.com/', 2008, True) >>> github.info() URL: https://github.com/ Founding year: 2008 Free to use: True >>> ``` That works. The attributes we defined in the initialize method are also available in the info method. We could also access them directly from `github`, for example with `github.url`. But we still need to call `github.initialize`. In Python, there's a "magic" method that runs when we create a new Website by calling the Website class. It's called `__init__` and it does nothing by default. If our `__init__` method takes other arguments than self we can call the class with arguments and they will be given to `__init__`. Like this: ```python >>> class Website: ... def __init__(self, url, founding_year, free_to_use): ... self.url = url ... self.founding_year = founding_year ... self.free_to_use = free_to_use ... def info(self): ... print("URL:", self.url) ... print("Founding year:", self.founding_year) ... print("Free to use:", self.free_to_use) ... >>> github = Website('https://github.com/', 2008, True) >>> github.info() URL: https://github.com/ Founding year: 2008 Free to use: True >>> ``` Classes have many other magic methods too, but I'm not going to cover them in this tutorial. ## When should I use classes? Don't do this: ```python class MyProgram: def __init__(self): print("Hello!") word = input("Enter something: ") print("You entered " + word + ".") program = MyProgram() ``` You should avoid using things like `print` and `input` in the `__init__` method. The `__init__` method should be simple and it should just set things up. Usually you shouldn't use a class if you're only going to make one instance of it, and you don't need a class either if you're only going to have one method. In this example `MyProgram` has only one method and only one instance. Make functions instead, or just write your code without any functions if it's short enough for that. This program does the same thing and it's much more readable: ```python print("Hello!") word = input("Enter something: ") print("You entered " + word + ".") ``` ## Summary - Object-orientated programming is programming with custom data types. In Python that means using classes and instances. - Use CapsWords for class names and lowercase_words_with_underscores for other names. This makes it easy to see which objects are classes and which objects are instances. - Calling a class as if it was a function makes a new instance of it. - `foo.bar = baz` sets `foo`'s attribute `bar` to `baz`. - Use class attributes for functions and instance attributes for other things. - Functions as class attributes can be accessed as instance methods. They get their instance as the first argument. Call that `self` when you define the method. - `__init__` is a special method, and it's ran when a new instance of a class is created. It does nothing by default. - Don't use classes if your code is easier to read without them. *** If you have trouble with this tutorial, please [tell me about it](../contact-me.md) and I'll make this tutorial better, or [ask for help online](../getting-help.md). If you like this tutorial, please [give it a star](../README.md#how-can-i-thank-you-for-writing-and-sharing-this-tutorial). You may use this tutorial freely at your own risk. See [LICENSE](../LICENSE). [Previous](exceptions.md) | [Next](docstrings.md) | [List of contents](../README.md#basics)