Class Creation
00:00 In the previous lesson, I explained why you might want to write object-oriented code. In this lesson, Iâll introduce you to how to do just that in Python.
00:10
You saw the PosixPath class and how to instantiate it into an object in the lesson prior to this. That instantiation process is done by calling the class with parentheses.
00:22
The constructor call can take arguments, which most often are the initial values for attributes that you want to keep in the object. Python uses a special method called .__init__(), which it calls as part of the class instantiation process.
00:37
You may hear programmers refer to this as the constructor. That isnât technically correct, but itâs similar enough to constructors in other languages that it isnât really worth debating. When you write your own class, you likely are going to need to write a .__init__() method to set it up.
00:54
All of an objectâs methods, including .__init__(), automatically get called with a reference to the object as their first argument. When you declare a method, you have to include this argument in the signature. By convention, in Python, this is known as self. Technically, you can call it anything you want.
01:13 The compiler doesnât enforce the name, but your fellow programmers may beat you, enforcing the convention through a different kind of argument. Letâs go revisit our circle from the previous lesson, this time as a class.
01:30
Logically enough, to declare a class, you use the class keyword. Here I have declared a class called Circle. The colon declares a block, and like with other blocks in Python, everything indented under here is part of the declaration.
01:50
By convention, the initialization method, .__init__(), is typically included first. As you can see here, the first argument to the method, like all methods, is self. When itâs called, it will contain a reference to the object.
02:06
Remember when I said .__init__() strictly isnât a constructor? This is why. When this method is called, the object has actually already been constructed. Itâs being passed into the .__init__() to be initialized by you.
02:20
This is also why .__init__() doesnât have to return anything. The object already exists, and you can do anything to it when itâs passed in as self. A true constructor in the strictest sense actually has to return the newly constructed object. The second argument here to .__init__() is specific to my Circle class, and itâs the radius of the circle.
02:45 Note that the argument being passed into the initializer doesnât mean itâs part of the object. To do that, you have to actually assign it to the object.
02:53
As self is a reference to the object being initialized, you use dot notation to store it. Iâve gone with the typical pattern of naming the object attribute the same thing as that passed into the method, but thereâs no requirement to do this.
03:08 This little chunk of boilerplate code actually kind of annoys me. Pretty much every single class you write is going to have this, and if youâve got several arguments, you can have several lines of code devoted to this.
03:20 I feel like there should be an easier way to do this, but donât quite know what it would look like. This isnât just a Python thing. Most object-oriented languages have a similar setup.
03:31
So, thatâs .__init__(). How about another method?
03:36
Pardon the lack of spacing here. Normally, you put an empty line between methods, but in the REPL, you canât do that. This line is the declaration of a method called .area().
03:47
Like all methods in a class, it takes the self argument, and in this case, no others. The
03:57
code for .area() implements the famous Ïr² formula. Notice how it is accessing the radius stored on the object using self.radius.
04:07
Again, this is possible because self is being passed into the method and is the object. Thatâs enough for your first class. Letâs use it to create a circle object.
04:21
By using parentheses on the class, Iâm calling it. Calling the class constructs an object. Because .__init__() took one argument in addition to self, the constructor here expects a value.
04:33
If I hadnât put the 3 in this code, Iâd get an error. As I did put 3, it gets passed into the constructed object, which then calls .__init__(), passing the argument along. The .__init__() then stores the 3 in the self.radius attribute. Once .__init__() is done, the constructor returns the new object, which Iâve stored in the variable named small.
04:59 I can access the attributes on the object by using dot notation,
05:05
and similar for calling a method. Note that self isnât used in the invocation of this method. Calling a method on an object automatically passes the object to the method.
05:16
As the .area() method doesnât take any other arguments, it is invoked without any at all. Letâs create another circle.
05:26 This time, the radius is a bit bigger,
05:30 and you can see its attribute.
05:34 And of course, I can calculate the area on this one as well. Attributes are also editable
05:43
Here, Iâve set the radius to a new value. When I recall .area(),
05:49
a new result is calculated. Similar to the whole self thing, the style you use when you write a class isnât enforced by the compiler, but it is best to stick with known practices.
06:02 Itâs easier for someone else to understand your code if you use the conventions that everyone else does. First off, like variables, attributes on an object use snake case. Thatâs all lowercase, with words separated by underscores. Class names themselves use pascal case. Thatâs no underscores, but with capitals on each word.
06:25 If youâre coming from other object-oriented languages, you might be wondering about things like private, protected, and public permission structures. If youâre not coming from other languages, these concepts control who can see the attribute or call a methodâthe object, inheritors, or anyone. Python doesnât really have this concept.
06:44 It uses a suggested convention based on naming instead. If you are coming from another language, this might seem like a bit of a shock. It took me a little to adjust the idea as well.
06:55 I used to write code where I tried my best to protect programmers from themselves, trying to stop them from doing things they werenât supposed to. Pythonâs attitude is a little more permissive. It essentially says, Hey, this is dangerous, but if you know what youâre doing, weâre all adults here. So, how do you signal danger? Well, Python has public and non-public members.
07:16 Non-public members are indicated by putting an underscore in front of their names. This isnât enforced in any way. People using the object can touch these attributes and call these methods, but youâre warning them that theyâre not really part of the public-facing interface, and they might change. How intensely you use these ideas is kind of a style thing.
07:38 I donât tend to use non-public values very much unless thereâs an important reason why. For example, if Iâve got two values that must be set together, I might store them with underscores and provide a method for changing them.
07:49 At the same time, I have come across code thatâs the other way though. One of the libraries Iâve contributed to once in a while is called Asciimatics. Itâs a terminal-based animation and TUI builder.
08:01 The core maintainer really likes his non-public attributes. Pretty much everything is non-public, with special mechanisms for exposing the API. I havenât actually had a chat with him as to why, but I suspect he used to write object-oriented code in another language and has carried the habit over.
08:19
Youâve already seen .__init__(), and Iâve spoken about other special methods denoted by their double underscores. Key functionality provided by Python in classes is mostly built using these kinds of methods. Although they are a system thing, thereâs nothing stopping you from using the same mechanism.
08:37 Do note that how they show up is a bit different though. Any member with two leading underscores gets renamed. It kind of hides the member hidesâas in, makes it less obvious.
08:50 You can still do whatever you like with it, but you can think of it as an extra-special warning. If the single underscore is a note in the manual saying you probably shouldnât do that, this is the sticker sealing your device shut, saying voids warranty. The sticker doesnât stop you, but you really should know what youâre doing.
09:09
These special methods get renamed by Python. This renaming is called name mangling. For example. .__member gets renamed as .__ClassName__member.
09:21 Just like with the single underscore, I can get at it if I know the name, but you canât really claim ignorance if youâre going hunting for things. This works for both attributes and methods.
09:34 Next up, Iâll dive into the various ways you can associate data with a class, covering more information about attributes.
Christopher Trudeau RP Team on Oct. 31, 2023
Hi Amanda,
Thanks for the comment. Dataclasses are covered in part 2 of the course. Hope you enjoy it.
Become a Member to join the conversation.

amandajones700 on Oct. 31, 2023
You could use the @dataclass decorator as a cleaner way of setting variables e.g.
rather than