WEBVTT
1
01:00:00.000 --> 01:00:03.458
[CLASSICAL MUSIC]
2
01:00:03.458 --> 01:00:23.875
3
01:00:23.875 --> 01:00:26.583
DAVID J. MALAN: All right,
this is CS50's Introduction
4
01:00:26.583 --> 01:00:27.916
to Programming with Python.
5
01:00:27.916 --> 01:00:31.541
My name is David Malan, and this is our
week on object-oriented programming,
6
01:00:31.541 --> 01:00:32.791
or OOP.
7
01:00:32.791 --> 01:00:34.750
It turns out that, in
the world of programming,
8
01:00:34.750 --> 01:00:37.583
there's different paradigms
of programming languages.
9
01:00:37.583 --> 01:00:40.208
There's different ways of
solving problems with code,
10
01:00:40.208 --> 01:00:42.000
and it's a little hard
to see this at first
11
01:00:42.000 --> 01:00:43.916
if you've only learned one language.
12
01:00:43.916 --> 01:00:47.416
But over time, if and when you learn
other languages besides Python,
13
01:00:47.416 --> 01:00:50.083
you'll start to notice certain
patterns and certain capabilities
14
01:00:50.083 --> 01:00:52.000
of some languages but not another.
15
01:00:52.000 --> 01:00:54.791
Thus far, within the
world of Python, you and I
16
01:00:54.791 --> 01:00:58.541
have largely been writing code that's
procedural in nature, whereby we're
17
01:00:58.541 --> 01:01:00.708
writing procedures;
we're writing functions;
18
01:01:00.708 --> 01:01:03.166
and we're doing things top to bottom.
19
01:01:03.166 --> 01:01:05.458
Everything is step by
step by step, as you would
20
01:01:05.458 --> 01:01:07.208
expect in general from an algorithm.
21
01:01:07.208 --> 01:01:09.250
But along the way,
we've actually dabbled
22
01:01:09.250 --> 01:01:14.083
in another paradigm known as functional
programming with Python whereby we've
23
01:01:14.083 --> 01:01:15.875
been able to pass functions around.
24
01:01:15.875 --> 01:01:18.666
We even had an anonymous
function some weeks ago.
25
01:01:18.666 --> 01:01:22.500
And that's evidence of features of
a functional programming language,
26
01:01:22.500 --> 01:01:24.708
even though we've just
scratched the surface thereof.
27
01:01:24.708 --> 01:01:28.083
Today we focus on another paradigm,
and this one in more detail--
28
01:01:28.083 --> 01:01:30.166
namely object-oriented programming.
29
01:01:30.166 --> 01:01:32.958
And now, while some of you might
have prior programming experience
30
01:01:32.958 --> 01:01:36.500
and have learned languages like Java,
which are, by design, fundamentally
31
01:01:36.500 --> 01:01:40.291
object-oriented, Python indeed
allows you a bit of flexibility
32
01:01:40.291 --> 01:01:43.250
when it comes to how you
solve problems with code.
33
01:01:43.250 --> 01:01:46.458
But it turns out
object-oriented programming
34
01:01:46.458 --> 01:01:49.000
is a pretty compelling
solution to problems
35
01:01:49.000 --> 01:01:52.666
that you invariably encounter as
your programs get longer, larger,
36
01:01:52.666 --> 01:01:54.375
and more complicated.
37
01:01:54.375 --> 01:01:56.333
So indeed, OOP, for
our purposes, is going
38
01:01:56.333 --> 01:02:00.791
to be a solution to a problem that
builds on so many of the lessons past.
39
01:02:00.791 --> 01:02:02.458
So let's go ahead and do this.
40
01:02:02.458 --> 01:02:05.833
Let's start by writing a
program very procedurally
41
01:02:05.833 --> 01:02:07.625
by opening up VS Code here.
42
01:02:07.625 --> 01:02:10.875
I'm going to go ahead and create
a program called student.py.
43
01:02:10.875 --> 01:02:14.083
And in this program, I want to
do something relatively simple
44
01:02:14.083 --> 01:02:18.375
initially, as we might have done
some weeks ago now, where I just
45
01:02:18.375 --> 01:02:22.166
ask a user for their name and, maybe
in the context of the Harry Potter
46
01:02:22.166 --> 01:02:26.291
universe, their house, and just
print out where that student is from.
47
01:02:26.291 --> 01:02:30.166
And let's gradually enhance this program
by adding more and more features to it
48
01:02:30.166 --> 01:02:33.875
and see if we don't stumble upon
problems that, up until now,
49
01:02:33.875 --> 01:02:36.958
we might not have had very elegant,
well-designed solutions to.
50
01:02:36.958 --> 01:02:40.250
But if we introduce, explicitly,
object-oriented programming
51
01:02:40.250 --> 01:02:43.250
as a programming technique, I
bet we can clean up our code
52
01:02:43.250 --> 01:02:47.208
and set the stage for writing even more
sophisticated programs, longer programs
53
01:02:47.208 --> 01:02:48.083
down the line.
54
01:02:48.083 --> 01:02:51.791
So in student.py, let me go
ahead and do a name variable,
55
01:02:51.791 --> 01:02:54.250
setting it equal to the
return value of input,
56
01:02:54.250 --> 01:02:57.041
and just prompt the user
for their name like this.
57
01:02:57.041 --> 01:02:59.791
And then let me go ahead and do
the same for a house variable
58
01:02:59.791 --> 01:03:03.041
and prompt the user for their
house, using input like this.
59
01:03:03.041 --> 01:03:05.208
And let's do something super simple now.
60
01:03:05.208 --> 01:03:07.875
Let's just go ahead and
print out an f string
61
01:03:07.875 --> 01:03:11.291
that says something
like name from house,
62
01:03:11.291 --> 01:03:13.958
just so that I can confirm that
the contents of these variables
63
01:03:13.958 --> 01:03:15.000
are indeed as I expect.
64
01:03:15.000 --> 01:03:17.083
I'm not going to do any
error checking or trimming
65
01:03:17.083 --> 01:03:18.333
or anything like that for now.
66
01:03:18.333 --> 01:03:21.416
I'm really just going to spit back
out whatever the user just typed in.
67
01:03:21.416 --> 01:03:24.375
All right, let me go ahead
and run Python of student.py.
68
01:03:24.375 --> 01:03:29.083
Let's use our go-to, like Harry, as
in Harry Potter, from Gryffindor.
69
01:03:29.083 --> 01:03:32.958
And when I hit Enter, now let's see
if I see that Harry from Gryffindor
70
01:03:32.958 --> 01:03:34.041
is indeed the case.
71
01:03:34.041 --> 01:03:37.375
All right, so I think we have a working
program at this point, but let's
72
01:03:37.375 --> 01:03:40.541
now introduce some of those lessons
learned way back from week zero
73
01:03:40.541 --> 01:03:42.583
where we started writing
our own functions,
74
01:03:42.583 --> 01:03:46.250
not necessarily because it solves
the problem more correctly--
75
01:03:46.250 --> 01:03:47.916
I daresay this is correct as is.
76
01:03:47.916 --> 01:03:52.041
But it begins to give us building
blocks that we can extend so
77
01:03:52.041 --> 01:03:54.125
as to solve more complicated programs.
78
01:03:54.125 --> 01:03:58.375
So let me go back up to student.py,
and let's go ahead now and do this.
79
01:03:58.375 --> 01:04:01.875
Let's put the entire logic I just
wrote inside of our typical method
80
01:04:01.875 --> 01:04:05.458
called main, and let me indent those
three lines so that at least they're
81
01:04:05.458 --> 01:04:07.333
now combined into one main method.
82
01:04:07.333 --> 01:04:12.208
But instead of using input
on line 2 an input on line 3,
83
01:04:12.208 --> 01:04:14.083
don't we go ahead and
assume, for the moment,
84
01:04:14.083 --> 01:04:17.000
that we've got some function
called get_name in the world,
85
01:04:17.000 --> 01:04:19.958
and let's go ahead and assume we've
got another function like get_house
86
01:04:19.958 --> 01:04:21.708
in the world that don't take parameters.
87
01:04:21.708 --> 01:04:24.166
But their purpose in
life is, by their name,
88
01:04:24.166 --> 01:04:28.458
going to be to get the user's name and
to get their users house, respectively.
89
01:04:28.458 --> 01:04:32.083
And then I'm going to print out
the exact same f string as before.
90
01:04:32.083 --> 01:04:34.333
I, of course, need to
implement these functions now.
91
01:04:34.333 --> 01:04:38.291
So let me go lower in my file and
define a function called get_name.
92
01:04:38.291 --> 01:04:41.375
Nothing in these parentheses because
it's not going to take a parameter.
93
01:04:41.375 --> 01:04:44.083
And I'm going to go ahead and do
something like name equals input
94
01:04:44.083 --> 01:04:48.750
("Name"), just like before, and then
I'm going to go ahead and return name.
95
01:04:48.750 --> 01:04:52.000
So it's a super simple function,
but it's an abstraction.
96
01:04:52.000 --> 01:04:55.458
I now have a function called
get_name whose implementation details
97
01:04:55.458 --> 01:04:56.916
I don't have to care about anymore.
98
01:04:56.916 --> 01:04:58.458
I just know that the function exists.
99
01:04:58.458 --> 01:05:00.250
And I can tighten this up, in fact, .
100
01:05:00.250 --> 01:05:02.875
Don't really need a
name variable on line 8
101
01:05:02.875 --> 01:05:05.875
if I'm immediately going to return
that same name variable on line 9.
102
01:05:05.875 --> 01:05:07.833
So let me just tighten
this up a little bit
103
01:05:07.833 --> 01:05:09.791
even though it doesn't
change the functionality
104
01:05:09.791 --> 01:05:15.750
and just immediately return the return
value of the inputs function call here.
105
01:05:15.750 --> 01:05:18.916
Let's do something very
similar now for get_house,
106
01:05:18.916 --> 01:05:20.750
which will similarly take no arguments.
107
01:05:20.750 --> 01:05:22.666
I'm going to go ahead
and return the return
108
01:05:22.666 --> 01:05:25.541
value of input, this time
prompting the user for their house.
109
01:05:25.541 --> 01:05:27.250
And I need one final detail.
110
01:05:27.250 --> 01:05:32.208
At the very bottom, let's continue our
habit of doing if the name of this file
111
01:05:32.208 --> 01:05:35.750
equals, equals, quote, unquote,
main, then let's go ahead
112
01:05:35.750 --> 01:05:38.875
and actually call main and
recall that we have that in place
113
01:05:38.875 --> 01:05:42.458
so that, if this eventually becomes
part of a module, a library of sorts,
114
01:05:42.458 --> 01:05:44.791
I don't accidentally call main blindly.
115
01:05:44.791 --> 01:05:50.250
I only do it if I mean to run main
from the command line on this file.
116
01:05:50.250 --> 01:05:53.458
All right, so if I didn't make
any mistakes here, let me go ahead
117
01:05:53.458 --> 01:05:57.416
and, in my terminal window, again,
run Python of student.py, Enter.
118
01:05:57.416 --> 01:05:59.250
Let's type in Harry, Enter.
119
01:05:59.250 --> 01:06:01.541
Let's type in Gryffindor, Enter.
120
01:06:01.541 --> 01:06:02.875
And we're set.
121
01:06:02.875 --> 01:06:05.583
Harry from Gryffindor
seems to still be working.
122
01:06:05.583 --> 01:06:09.416
So we haven't really solved
the problem any more correctly,
123
01:06:09.416 --> 01:06:11.875
but I've laid the
foundation to maybe now do
124
01:06:11.875 --> 01:06:13.583
some more interesting
things because I've
125
01:06:13.583 --> 01:06:16.875
had these building blocks in place.
126
01:06:16.875 --> 01:06:21.083
But let me propose that we could be
doing this a little bit differently.
127
01:06:21.083 --> 01:06:23.000
get_name, get_house is fine.
128
01:06:23.000 --> 01:06:26.416
But at the end of the day, I'm really
trying to get a student from the user.
129
01:06:26.416 --> 01:06:30.083
I want their name and their
house, not just one or the other.
130
01:06:30.083 --> 01:06:32.833
So maybe it would be
a little cleaner still
131
01:06:32.833 --> 01:06:36.083
to define a function
called get_student and let
132
01:06:36.083 --> 01:06:38.291
get_student do all of this work for us.
133
01:06:38.291 --> 01:06:43.000
Now, theoretically, get_student could
call get_name and could call get_house.
134
01:06:43.000 --> 01:06:44.750
But because these
functions are so short,
135
01:06:44.750 --> 01:06:48.875
I think I'm OK with just defining
one function, called get_student,
136
01:06:48.875 --> 01:06:50.666
that similarly won't take any arguments.
137
01:06:50.666 --> 01:06:52.250
But it's going to do two things.
138
01:06:52.250 --> 01:06:55.875
It's going to get the students name,
by prompting them with input as before.
139
01:06:55.875 --> 01:06:57.625
And it's going to get
the student's house,
140
01:06:57.625 --> 01:07:01.125
by also prompting them as before.
141
01:07:01.125 --> 01:07:02.416
Now, hmm.
142
01:07:02.416 --> 01:07:04.833
I want to return the
student, but I think
143
01:07:04.833 --> 01:07:06.875
I might have painted
myself into a corner
144
01:07:06.875 --> 01:07:10.125
here because I now have two
variables-- name and house.
145
01:07:10.125 --> 01:07:13.250
And yet, up until now, we've pretty
much returned one or the other.
146
01:07:13.250 --> 01:07:15.000
We've returned one value.
147
01:07:15.000 --> 01:07:17.500
So any suggestions
for how we can perhaps
148
01:07:17.500 --> 01:07:22.375
solve this problem that I just created
for myself, whereby I want to return,
149
01:07:22.375 --> 01:07:28.083
really, a student, but I currently have
a name variable and a house variable.
150
01:07:28.083 --> 01:07:30.541
I'd minimally like to
return both of those.
151
01:07:30.541 --> 01:07:33.208
AUDIENCE: I believe that,
we can return a dictionary,
152
01:07:33.208 --> 01:07:35.000
includes the name and the house.
153
01:07:35.000 --> 01:07:36.833
DAVID J. MALAN: Yeah,
so we absolutely could
154
01:07:36.833 --> 01:07:41.000
return a dictionary-- a dict object in
Python, whereby maybe one key is name;
155
01:07:41.000 --> 01:07:43.666
one key is house; and the
values thereof are exactly
156
01:07:43.666 --> 01:07:44.916
the values of these variables.
157
01:07:44.916 --> 01:07:46.791
So we could totally do that.
158
01:07:46.791 --> 01:07:49.375
I worry that that might be
getting a little complicated.
159
01:07:49.375 --> 01:07:51.916
I wonder if there's a
simpler way instead.
160
01:07:51.916 --> 01:07:55.000
Any other instincts-- even if
you're not sure it would work?
161
01:07:55.000 --> 01:07:56.541
AUDIENCE: Return both name and house?
162
01:07:56.541 --> 01:07:58.333
DAVID J. MALAN: Return
both name and house.
163
01:07:58.333 --> 01:07:59.416
I like the sound of that.
164
01:07:59.416 --> 01:08:00.250
It sounds simple.
165
01:08:00.250 --> 01:08:03.041
I don't have to figure out what a
dictionary is going to look like.
166
01:08:03.041 --> 01:08:05.750
And in fact, this, too,
would be a valid approach,
167
01:08:05.750 --> 01:08:07.250
even if you've not seen this before.
168
01:08:07.250 --> 01:08:11.625
It turns out, in Python, that
you can return multiple values,
169
01:08:11.625 --> 01:08:12.958
but that's a bit of a white lie.
170
01:08:12.958 --> 01:08:16.333
Or we could take Muhammad's approach
of actually returning a dictionary
171
01:08:16.333 --> 01:08:17.833
and putting multiple keys therein.
172
01:08:17.833 --> 01:08:19.833
So here, again, we have
yet another example
173
01:08:19.833 --> 01:08:22.375
of how you can solve the same
problem in at least two ways,
174
01:08:22.375 --> 01:08:24.708
and I daresay we're
about to see even more.
175
01:08:24.708 --> 01:08:27.916
So one way you could solve
this problem, whereby
176
01:08:27.916 --> 01:08:32.916
you want to return multiple values,
would be to do something like this.
177
01:08:32.916 --> 01:08:37.666
I could go ahead and literally return
not just name, but I could put a comma
178
01:08:37.666 --> 01:08:39.250
and also return house.
179
01:08:39.250 --> 01:08:41.958
This is not necessarily something
you can do in other languages
180
01:08:41.958 --> 01:08:44.166
if you have programmed in
other languages before.
181
01:08:44.166 --> 01:08:45.416
It depends on the language.
182
01:08:45.416 --> 01:08:49.291
But it looks like, thanks to
this comma, maybe I can, in fact,
183
01:08:49.291 --> 01:08:51.916
return two values as
[INAUDIBLE] proposed.
184
01:08:51.916 --> 01:08:55.375
Well, if I'm returning to
values in this way on line 10,
185
01:08:55.375 --> 01:08:57.916
how do I get both
values at the same time?
186
01:08:57.916 --> 01:08:59.208
Well, there's a couple of ways.
187
01:08:59.208 --> 01:09:01.833
Let me go up to my main function.
188
01:09:01.833 --> 01:09:04.416
I know, minimally, I'm going
to have to change the get_name
189
01:09:04.416 --> 01:09:05.958
and get_house to get_student.
190
01:09:05.958 --> 01:09:09.041
But what am I going to
store the return value in?
191
01:09:09.041 --> 01:09:11.500
I think I could actually do this.
192
01:09:11.500 --> 01:09:14.916
And we have seen this technique
before, where you can unpack,
193
01:09:14.916 --> 01:09:17.541
so to speak, sequences of
values that are coming back.
194
01:09:17.541 --> 01:09:21.083
And indeed, consider this
to be exactly that. name,
195
01:09:21.083 --> 01:09:25.000
house is some kind of sequence that
I'm returning of values-- name, house.
196
01:09:25.000 --> 01:09:27.291
So if I want to unpack
those and store the return
197
01:09:27.291 --> 01:09:30.000
values in two separate
variables, I can, in fact,
198
01:09:30.000 --> 01:09:34.208
use the commas on the left-hand side of
my assignment operator, the equal sign,
199
01:09:34.208 --> 01:09:35.416
to do just that.
200
01:09:35.416 --> 01:09:40.000
Now, to be clear, I don't need to call
these variables name and house here.
201
01:09:40.000 --> 01:09:43.500
I could simplify this and
use just n here and h here,
202
01:09:43.500 --> 01:09:46.583
and then I could return just n and h.
203
01:09:46.583 --> 01:09:51.208
But I would argue that's not very clear
to the reader as to what's going on.
204
01:09:51.208 --> 01:09:52.958
So I think, in this
case, even though it's
205
01:09:52.958 --> 01:09:56.000
a coincidence that I've used the
same variable names in get_student
206
01:09:56.000 --> 01:09:59.875
and get_name, and in main, it's a
little more readable to someone like me.
207
01:09:59.875 --> 01:10:01.541
So I'm going to leave it as is.
208
01:10:01.541 --> 01:10:04.041
Well, let's go ahead and
see, now, if this works.
209
01:10:04.041 --> 01:10:07.166
Let me clear my screen down here
and run Python of student.py, Enter.
210
01:10:07.166 --> 01:10:08.375
Let's again type in Harry.
211
01:10:08.375 --> 01:10:11.041
Let's again type in Gryffindor, Enter.
212
01:10:11.041 --> 01:10:14.791
And voila, we still see that
Harry is from Gryffindor.
213
01:10:14.791 --> 01:10:16.958
But what are we actually doing here?
214
01:10:16.958 --> 01:10:19.666
What are we actually doing
by returning this value?
215
01:10:19.666 --> 01:10:24.333
Well, it turns out that what
we've just done is use a tuple.
216
01:10:24.333 --> 01:10:29.791
A tuple is another type of data in
Python that's a collection of values--
217
01:10:29.791 --> 01:10:32.291
x, y or x, y, z.
218
01:10:32.291 --> 01:10:36.750
It's similar in spirit to a list,
in that sense, but it's immutable.
219
01:10:36.750 --> 01:10:37.666
It's not mutable.
220
01:10:37.666 --> 01:10:38.708
Now, what does that mean?
221
01:10:38.708 --> 01:10:41.708
A list, as we've seen it before,
is a data structure in Python
222
01:10:41.708 --> 01:10:43.458
that you can change the values of.
223
01:10:43.458 --> 01:10:47.250
You can go into bracket 0 for the first
location and change the value there.
224
01:10:47.250 --> 01:10:50.208
You can go to bracket 1, bracket
2, bracket 3 and actually change
225
01:10:50.208 --> 01:10:51.458
the values in lists.
226
01:10:51.458 --> 01:10:54.791
But if you have no intention of
changing the values of variables
227
01:10:54.791 --> 01:10:57.750
and you want to return,
effectively, multiple values,
228
01:10:57.750 --> 01:10:59.541
you don't have to even
return it as a list.
229
01:10:59.541 --> 01:11:03.416
You can return it as a tuple
instead, just by using a comma.
230
01:11:03.416 --> 01:11:07.000
And it turns out we can make
explicit that-- here's the white lie.
231
01:11:07.000 --> 01:11:10.500
I'm not actually returning
to values per se.
232
01:11:10.500 --> 01:11:13.416
Whenever you use a comma
in this way on line 9,
233
01:11:13.416 --> 01:11:17.500
you're actually returning
one value, which is a tuple.
234
01:11:17.500 --> 01:11:20.291
Inside of that tuple now are two values.
235
01:11:20.291 --> 01:11:23.791
So it's similar in spirit to
returning one list with two thing Here
236
01:11:23.791 --> 01:11:26.291
I'm returning one tuple with two things.
237
01:11:26.291 --> 01:11:28.958
And the mere fact that I've
used a comma and nothing else
238
01:11:28.958 --> 01:11:31.375
tells Python that I indeed
want to return a tuple.
239
01:11:31.375 --> 01:11:34.083
But there's more explicit
syntax that we can use instead.
240
01:11:34.083 --> 01:11:39.291
I can actually-- more verbosely-- put
explicit parentheses around the values
241
01:11:39.291 --> 01:11:42.458
of this tuple just to make
more clear to me, to the reader
242
01:11:42.458 --> 01:11:44.083
that this isn't two values per se.
243
01:11:44.083 --> 01:11:46.500
This is one value with
two things inside of it.
244
01:11:46.500 --> 01:11:48.458
And what I can actually
do then, too, is--
245
01:11:48.458 --> 01:11:51.000
I don't have to unpack
this up here, so to speak.
246
01:11:51.000 --> 01:11:54.833
I can actually go up here and maybe
give a more apt name, like student,
247
01:11:54.833 --> 01:11:58.375
and I can name the value,
or rather name the variable
248
01:11:58.375 --> 01:12:01.333
in which I'm storing the
return value of get_student as,
249
01:12:01.333 --> 01:12:02.791
quote, unquote, "student."
250
01:12:02.791 --> 01:12:05.166
So maybe this is a
little better design now
251
01:12:05.166 --> 01:12:08.625
because I'm sort of abstracting
away what a student is.
252
01:12:08.625 --> 01:12:11.666
It's implemented at the moment
as a tuple with two values.
253
01:12:11.666 --> 01:12:15.875
But at least, now I have a variable
called what I mean, a student.
254
01:12:15.875 --> 01:12:17.375
But there's going to be a catch.
255
01:12:17.375 --> 01:12:21.583
On line 3, I still want to print out
that student's name and their house.
256
01:12:21.583 --> 01:12:24.541
But I don't have a name variable
anymore, and I don't have a house.
257
01:12:24.541 --> 01:12:28.625
And I also don't have a dictionary, as
was proposed earlier, so I can't even
258
01:12:28.625 --> 01:12:31.833
go at those keys by name.
259
01:12:31.833 --> 01:12:35.166
But what a tuple is-- it's very
similar in spirit to a list,
260
01:12:35.166 --> 01:12:37.000
but it is indeed just immutable.
261
01:12:37.000 --> 01:12:39.791
And what I mean by that is
I can still index into it
262
01:12:39.791 --> 01:12:45.041
numerically by saying
student [0] for the item
263
01:12:45.041 --> 01:12:47.000
in the first location in that tuple.
264
01:12:47.000 --> 01:12:50.416
And then over here, instead of
house, I can say student [1].
265
01:12:50.416 --> 01:12:54.291
student [1] is going to give me
the second location in that tuple.
266
01:12:54.291 --> 01:12:56.166
Let me go ahead and
clear my terminal window.
267
01:12:56.166 --> 01:12:58.083
Again, run Python of student.py.
268
01:12:58.083 --> 01:12:59.250
Let's type in Harry.
269
01:12:59.250 --> 01:13:04.500
Let's type in Gryffindor, Enter,
and we still have some working code.
270
01:13:04.500 --> 01:13:07.208
Let me pause here now
and see if there are
271
01:13:07.208 --> 01:13:11.500
any questions on this
technique of returning a tuple
272
01:13:11.500 --> 01:13:14.750
and indexing into it in this way.
273
01:13:14.750 --> 01:13:17.125
AUDIENCE: I guess, what's
an actual use case where
274
01:13:17.125 --> 01:13:21.333
you would use a tuple versus a list
or something else that's similar?
275
01:13:21.333 --> 01:13:23.166
DAVID J. MALAN: It's a
really good question.
276
01:13:23.166 --> 01:13:25.291
When would you use a
tuple versus a list?
277
01:13:25.291 --> 01:13:28.208
When you want to program
defensively, or, in general,
278
01:13:28.208 --> 01:13:31.375
when you know that the values in
this variable shouldn't change,
279
01:13:31.375 --> 01:13:34.333
so why would you use a data type
that allows them to be changed?
280
01:13:34.333 --> 01:13:37.791
It just invites mistakes,
bugs down the line, either
281
01:13:37.791 --> 01:13:40.666
by you or colleagues who are
interacting with your code.
282
01:13:40.666 --> 01:13:44.083
So tuple is just another
way where you can increase
283
01:13:44.083 --> 01:13:47.708
the probability of correctness by just
not letting anyone, yourself included,
284
01:13:47.708 --> 01:13:49.750
change the contents therein.
285
01:13:49.750 --> 01:13:52.250
So it's just another
tool in your toolkit.
286
01:13:52.250 --> 01:13:55.333
But let's make clear, then,
what I mean by "immutable."
287
01:13:55.333 --> 01:13:58.833
Again, I claim that "immutable" means
that you cannot change the value.
288
01:13:58.833 --> 01:14:01.000
Well, let's go ahead and try to do this.
289
01:14:01.000 --> 01:14:03.750
Let me go ahead and run this
program once more as is--
290
01:14:03.750 --> 01:14:05.291
Python of student.py.
291
01:14:05.291 --> 01:14:09.416
Let me go ahead and
type in, for instance--
292
01:14:09.416 --> 01:14:11.375
how about Padma's name?
293
01:14:11.375 --> 01:14:13.208
And I'm going to go
ahead and say that Padma
294
01:14:13.208 --> 01:14:14.916
is in Gryffindor as in the movies.
295
01:14:14.916 --> 01:14:16.958
And we see-- Padma from Gryffindor.
296
01:14:16.958 --> 01:14:20.791
But technically, I went down this
rabbit hole in looking at Harry Potter
297
01:14:20.791 --> 01:14:21.541
more closely.
298
01:14:21.541 --> 01:14:25.000
Technically, in the books, Padma,
I believe, was from Ravenclaw.
299
01:14:25.000 --> 01:14:27.083
So this is actually a
mistake or an inconsistency
300
01:14:27.083 --> 01:14:28.750
between the movies and the books.
301
01:14:28.750 --> 01:14:31.625
Let's see if we can't fix this
inconsistency in our code.
302
01:14:31.625 --> 01:14:33.208
So how about we do this?
303
01:14:33.208 --> 01:14:36.333
If the student's name that's
inputted equals Padma,
304
01:14:36.333 --> 01:14:40.208
why don't we override
whatever the house is
305
01:14:40.208 --> 01:14:42.875
and change it to be properly Gryffindor.
306
01:14:42.875 --> 01:14:46.083
Let me go ahead and do if student--
307
01:14:46.083 --> 01:14:49.708
now, if I want to get at Padma's name,
I'm going to have to do student [0].
308
01:14:49.708 --> 01:14:52.625
I have to know what location
the name is in in this tuple.
309
01:14:52.625 --> 01:14:57.750
But if that value equals equals Padma,
let's go ahead with this if statement
310
01:14:57.750 --> 01:14:58.666
and make a change.
311
01:14:58.666 --> 01:15:02.208
Let's change the student's [1] value.
312
01:15:02.208 --> 01:15:04.625
So the second value, if
we're zero indexing--
313
01:15:04.625 --> 01:15:07.375
let's change it to be another
house in the world of Harry Potter
314
01:15:07.375 --> 01:15:08.916
called Ravenclaw.
315
01:15:08.916 --> 01:15:11.750
So I'm just fixing
maybe the user's input.
316
01:15:11.750 --> 01:15:14.166
They watched the movie so
they type in Padma Gryffindor,
317
01:15:14.166 --> 01:15:17.333
but, mm-mm, in the books,
it was Padma from Ravenclaw.
318
01:15:17.333 --> 01:15:20.250
All right, let me go ahead and
go down to my terminal window,
319
01:15:20.250 --> 01:15:24.041
clear my terminal, and do
Python of student.py, Enter.
320
01:15:24.041 --> 01:15:27.000
I'm going to do Harry as well as
Gryffindor, just to demonstrate
321
01:15:27.000 --> 01:15:29.750
that that is still working as intended.
322
01:15:29.750 --> 01:15:33.958
Let me clear my screen again, though,
and run Python of student.py on Padma,
323
01:15:33.958 --> 01:15:37.500
and I'll put her, too, in Gryffindor,
as in the movies, and hit Enter.
324
01:15:37.500 --> 01:15:40.916
And now I just see a big
mess of errors on the screen.
325
01:15:40.916 --> 01:15:42.708
Some kind of exception has been thrown.
326
01:15:42.708 --> 01:15:45.166
And indeed, a type error has happened.
327
01:15:45.166 --> 01:15:48.750
I'm using a data type wherein there's
an error, and what is that error?
328
01:15:48.750 --> 01:15:53.041
Well, 'tuple' object does
not support item assignment.
329
01:15:53.041 --> 01:15:57.375
It's a little arcanely expressed-- that
is, it's not really very user friendly.
330
01:15:57.375 --> 01:15:59.250
But if you think about
what those words mean,
331
01:15:59.250 --> 01:16:01.458
'tuple" object does not
support item assignment.
332
01:16:01.458 --> 01:16:03.458
So assignment is copying
from right to left.
333
01:16:03.458 --> 01:16:05.208
So somehow, that's invalid.
334
01:16:05.208 --> 01:16:10.250
And here is a manifestation
of the immutability of tuples.
335
01:16:10.250 --> 01:16:14.208
You cannot change location
0 or 1 or anything inside.
336
01:16:14.208 --> 01:16:15.250
That is a feature.
337
01:16:15.250 --> 01:16:17.250
That is the design of a tuple.
338
01:16:17.250 --> 01:16:20.000
So if I want to override
that, I think I'm
339
01:16:20.000 --> 01:16:23.125
going to have to use a different
type of data that we've used before--
340
01:16:23.125 --> 01:16:24.750
namely a list, and that's fine.
341
01:16:24.750 --> 01:16:28.416
If you want to enable yourself
and colleagues using your code
342
01:16:28.416 --> 01:16:31.083
to change the contents
of that container,
343
01:16:31.083 --> 01:16:34.375
well, we can go ahead and
return not a tuple using
344
01:16:34.375 --> 01:16:37.541
explicit parentheses or no
parentheses, just the comma,
345
01:16:37.541 --> 01:16:39.250
but I can use square brackets.
346
01:16:39.250 --> 01:16:41.916
And if I'm using square brackets
on the left and the right,
347
01:16:41.916 --> 01:16:44.458
this is indeed explicitly a list.
348
01:16:44.458 --> 01:16:47.041
Same idea, but it's mutable.
349
01:16:47.041 --> 01:16:50.000
That is to say you can change
the contents of a list.
350
01:16:50.000 --> 01:16:53.125
So making no other changes,
just returning a list
351
01:16:53.125 --> 01:16:57.833
with square brackets instead of a tuple
with parentheses or just the comma.
352
01:16:57.833 --> 01:17:01.041
Let me go ahead now and run
Python of student.py, Enter.
353
01:17:01.041 --> 01:17:04.000
Let me type in Harry
and Gryffindor again.
354
01:17:04.000 --> 01:17:05.458
That's still working.
355
01:17:05.458 --> 01:17:06.416
Good to see.
356
01:17:06.416 --> 01:17:11.416
Let me run this once more and type in
Padma and Gryffindor, as in the movies,
357
01:17:11.416 --> 01:17:15.333
but no, now we've corrected
it to be Padma from Ravenclaw,
358
01:17:15.333 --> 01:17:17.958
as in the books instead.
359
01:17:17.958 --> 01:17:22.541
Any questions now on tuples
versus lists or this idea
360
01:17:22.541 --> 01:17:26.541
of immutability versus mutability.
361
01:17:26.541 --> 01:17:31.833
AUDIENCE: Can we use a nested
tuple in Python, like a nest list?
362
01:17:31.833 --> 01:17:32.958
DAVID J. MALAN: Absolutely.
363
01:17:32.958 --> 01:17:35.250
You can have not only
nested lists in Python,
364
01:17:35.250 --> 01:17:38.291
where one of the elements in a
list could be another list-- so you
365
01:17:38.291 --> 01:17:41.500
have some square brackets out here; you
might have some other square brackets
366
01:17:41.500 --> 01:17:42.041
inside.
367
01:17:42.041 --> 01:17:44.833
You can absolutely do the
same with a tuple as well.
368
01:17:44.833 --> 01:17:47.833
There is no constraint on the types
of values you can put in there.
369
01:17:47.833 --> 01:17:50.125
We've not had occasion
to do that in this case.
370
01:17:50.125 --> 01:17:54.041
I'm just returning a simple
tuple with two elements.
371
01:17:54.041 --> 01:17:56.291
But yes, you could
absolutely do that, too.
372
01:17:56.291 --> 01:17:59.416
Other questions on tuples versus lists?
373
01:17:59.416 --> 01:18:03.208
AUDIENCE: OK, for example,
when I see the square brackets,
374
01:18:03.208 --> 01:18:05.250
is it mainly used for the list?
375
01:18:05.250 --> 01:18:07.041
DAVID J. MALAN: Oh, a
really good question.
376
01:18:07.041 --> 01:18:07.875
Sort of.
377
01:18:07.875 --> 01:18:12.250
So when you create a value like
a list, you use square brackets,
378
01:18:12.250 --> 01:18:14.041
and that would indeed
be a visual indicator
379
01:18:14.041 --> 01:18:16.458
that this is definitely a list.
380
01:18:16.458 --> 01:18:19.041
If you instead see parentheses,
that's a visual indicator,
381
01:18:19.041 --> 01:18:21.708
when creating a value, that
it's definitely a tuple.
382
01:18:21.708 --> 01:18:26.416
However, somewhat confusingly,
both lists and tuples
383
01:18:26.416 --> 01:18:30.375
use square brackets when you
access the contents of them.
384
01:18:30.375 --> 01:18:34.000
When you index into them
at location 0 or location 1
385
01:18:34.000 --> 01:18:36.083
you always use square brackets.
386
01:18:36.083 --> 01:18:37.500
So that's the distinction there.
387
01:18:37.500 --> 01:18:38.875
Good question.
388
01:18:38.875 --> 01:18:42.916
Allow me to propose now, if I may, that
we solve this problem yet another way,
389
01:18:42.916 --> 01:18:46.250
and let's see if we're either making
things better or for worse than us.
390
01:18:46.250 --> 01:18:49.916
Recall that dictionaries, or dict
objects, also exist in Python.
391
01:18:49.916 --> 01:18:53.250
And a dictionary is this
collection of keys and values.
392
01:18:53.250 --> 01:18:55.458
And the upside, in
particular, of a dictionary
393
01:18:55.458 --> 01:18:57.083
is that they have better semantics.
394
01:18:57.083 --> 01:18:59.625
You don't just have to
assume that a name is always
395
01:18:59.625 --> 01:19:03.291
going to be at location 0; house is
always going to be at location 1.
396
01:19:03.291 --> 01:19:06.916
That's the kind of thing, especially if
you had three, four, or more values--
397
01:19:06.916 --> 01:19:10.000
eventually you or someone is
going to get confused and forget
398
01:19:10.000 --> 01:19:12.958
what the order is and you're
going to write buggy code.
399
01:19:12.958 --> 01:19:15.333
So a dictionary is a
little more powerful
400
01:19:15.333 --> 01:19:19.000
in that you can semantically
associate keys, little descriptions,
401
01:19:19.000 --> 01:19:20.041
with the values--
402
01:19:20.041 --> 01:19:22.541
those keys and those
values, respectively.
403
01:19:22.541 --> 01:19:25.666
So let me go ahead and do this, and we
can do this in a few different ways.
404
01:19:25.666 --> 01:19:28.958
But let me propose that we
focus on get_student here .
405
01:19:28.958 --> 01:19:30.583
And let's go ahead and do this.
406
01:19:30.583 --> 01:19:35.875
Let me go ahead and delete the
implementation of get_student as is.
407
01:19:35.875 --> 01:19:40.083
Let me create a student variable and
initialize it to an empty dictionary.
408
01:19:40.083 --> 01:19:43.083
And I can do that with
just two curly braces here.
409
01:19:43.083 --> 01:19:46.958
And then let me go ahead and set
two keys inside of that dictionary.
410
01:19:46.958 --> 01:19:49.708
Inside of the student, there
will be, quote unquote, a "name"
411
01:19:49.708 --> 01:19:53.625
key, and the value of that is going to
be whatever the return value of input
412
01:19:53.625 --> 01:19:55.625
is when I prompt the
user for their name.
413
01:19:55.625 --> 01:19:59.708
And then the "house" key, inside
of that same student dictionary,
414
01:19:59.708 --> 01:20:02.750
is going to be the return value
of whatever the user types
415
01:20:02.750 --> 01:20:07.000
in for their house. , And lastly I'm
going to go ahead and return student.
416
01:20:07.000 --> 01:20:11.000
So now I am literally
returning one thing, still,
417
01:20:11.000 --> 01:20:15.291
but this time, it's a dict rather
than a tuple, rather than a list.
418
01:20:15.291 --> 01:20:18.041
But there are still two things
in it, technically four things
419
01:20:18.041 --> 01:20:19.708
if you count the keys and the values.
420
01:20:19.708 --> 01:20:22.083
But there's two key value pairs.
421
01:20:22.083 --> 01:20:24.625
Now, my code up here is going
to have to change a little bit.
422
01:20:24.625 --> 01:20:27.375
And let's simplify this and
remove, for instance, now
423
01:20:27.375 --> 01:20:31.458
the Padma if statement just to
focus on what's changing at hand.
424
01:20:31.458 --> 01:20:34.000
And let me go ahead now
and leave line 2 alone.
425
01:20:34.000 --> 01:20:37.708
I'm still going to have a student
variable that gets assigned the return
426
01:20:37.708 --> 01:20:39.166
value of get_student.
427
01:20:39.166 --> 01:20:45.375
But what I want to do here now is
actually access the keys inside of that
428
01:20:45.375 --> 01:20:49.083
dictionary-- not by numeric index,
which was for tuples and lists--
429
01:20:49.083 --> 01:20:52.541
0 and 1, but by way of the keys.
430
01:20:52.541 --> 01:20:55.291
Now, normally, I might be in
the habit, as I personally
431
01:20:55.291 --> 01:20:58.916
am, of using double quotes-- quote,
unquote, "name" inside of there
432
01:20:58.916 --> 01:21:02.166
and quote, unquote,
"house" inside of there.
433
01:21:02.166 --> 01:21:05.833
But before I even run this
code and show you a mistake--
434
01:21:05.833 --> 01:21:08.750
see an error on the screen,
does anyone want to call out
435
01:21:08.750 --> 01:21:10.666
what I have done wrong here?
436
01:21:10.666 --> 01:21:12.375
This is just an f string.
437
01:21:12.375 --> 01:21:16.708
I just want to print out the value of
the name key, the value of the house
438
01:21:16.708 --> 01:21:18.666
key in this dictionary.
439
01:21:18.666 --> 01:21:21.083
AUDIENCE: [INAUDIBLE]
440
01:21:21.083 --> 01:21:21.333
441
01:21:21.333 --> 01:21:23.625
DAVID J. MALAN: Your audio
was a little garbled for us.
442
01:21:23.625 --> 01:21:26.250
But I think I heard double
quotes and single quotes.
443
01:21:26.250 --> 01:21:29.708
So I'm going to assume that, indeed,
you've identified precisely the issue.
444
01:21:29.708 --> 01:21:31.541
I'm just going to
confuse Python right now.
445
01:21:31.541 --> 01:21:35.833
Even though this is an f string inside
of double quotes, prefixed with an f,
446
01:21:35.833 --> 01:21:39.000
I can't actually use my double
quotes inside my double quotes
447
01:21:39.000 --> 01:21:41.125
because that's going to
potentially confuse Python.
448
01:21:41.125 --> 01:21:44.375
If I run this program now,
Python of student.py and hit
449
01:21:44.375 --> 01:21:46.166
Enter, I get a syntax error.
450
01:21:46.166 --> 01:21:48.000
So the program didn't even run fully.
451
01:21:48.000 --> 01:21:51.958
It just couldn't be understood because
it got confused by those double quotes.
452
01:21:51.958 --> 01:21:54.041
So the simplest fix
here would indeed just
453
01:21:54.041 --> 01:21:58.250
be to use not double quotes but
single quotes around the keys,
454
01:21:58.250 --> 01:22:02.041
or conversely, flip the double quotes
on the outside to single quotes,
455
01:22:02.041 --> 01:22:03.583
then use double quotes in the inside.
456
01:22:03.583 --> 01:22:05.125
You just want to be consistent.
457
01:22:05.125 --> 01:22:09.041
So a subtle detail, but again, this
is now specific to dictionary syntax.
458
01:22:09.041 --> 01:22:14.666
This isn't fundamental to how we're
solving this current problem at hand.
459
01:22:14.666 --> 01:22:16.083
Well, let's go ahead and try this.
460
01:22:16.083 --> 01:22:18.416
Let me go ahead now and
run Python of student.py.
461
01:22:18.416 --> 01:22:19.833
Let's go ahead and type in Harry.
462
01:22:19.833 --> 01:22:20.916
Let's type in Gryffindor.
463
01:22:20.916 --> 01:22:24.083
And hopefully, Harry is
back from Gryffindor.
464
01:22:24.083 --> 01:22:25.375
No syntax errors.
465
01:22:25.375 --> 01:22:26.250
No other errors.
466
01:22:26.250 --> 01:22:28.000
I think I'm back in business here.
467
01:22:28.000 --> 01:22:31.125
And what I do like to be
clear about using a dictionary
468
01:22:31.125 --> 01:22:34.000
is that it's allowing me
just better semantics.
469
01:22:34.000 --> 01:22:38.958
And again, I don't have to remember,
memorize, document that 0 is name;
470
01:22:38.958 --> 01:22:40.166
1 is house.
471
01:22:40.166 --> 01:22:43.291
Instead, "name" is name
and "house" is house.
472
01:22:43.291 --> 01:22:45.500
It's just a little clearer,
a little more expressive.
473
01:22:45.500 --> 01:22:49.250
So that's generally a good thing,
especially if we stored more data about
474
01:22:49.250 --> 01:22:53.000
students than just their name and their
house-- if you had three fields, four,
475
01:22:53.000 --> 01:22:54.625
five, 10 different fields--
476
01:22:54.625 --> 01:22:58.875
no one's going to want to remember or be
able to remember forever which is zero
477
01:22:58.875 --> 01:23:00.625
which is 1, which is 2, and so forth.
478
01:23:00.625 --> 01:23:04.958
Better to introduce names, like
"name" and "house" in this case.
479
01:23:04.958 --> 01:23:07.125
But let me tighten this up further.
480
01:23:07.125 --> 01:23:10.541
I'm typically in the habit of not
introducing variables unnecessarily,
481
01:23:10.541 --> 01:23:12.250
unless they make the code more readable.
482
01:23:12.250 --> 01:23:15.958
And an alternative way to format
the same code would be this.
483
01:23:15.958 --> 01:23:19.333
Strictly speaking, I don't need
to create an empty dictionary,
484
01:23:19.333 --> 01:23:23.375
then add one key to it,
then add a second key to it,
485
01:23:23.375 --> 01:23:25.375
and then return that dictionary.
486
01:23:25.375 --> 01:23:29.458
I can actually consolidate this all
into one statement, if you will.
487
01:23:29.458 --> 01:23:30.958
Let me go ahead and do this.
488
01:23:30.958 --> 01:23:35.458
Let me go ahead and say name
equals inputs return value,
489
01:23:35.458 --> 01:23:38.583
house equals inputs
return value, and then,
490
01:23:38.583 --> 01:23:41.666
instead of returning any variable name
student, which I'm going to propose
491
01:23:41.666 --> 01:23:44.208
doesn't need to exist
anymore, let me just create
492
01:23:44.208 --> 01:23:46.416
and return the dictionary all at once.
493
01:23:46.416 --> 01:23:50.458
Let me do, quote, unquote, "name" in
lowercase here, and then the variable.
494
01:23:50.458 --> 01:23:52.041
It's storing the user's name.
495
01:23:52.041 --> 01:23:55.208
Then, quote, unquote, "house," as
my second key, the value of which
496
01:23:55.208 --> 01:23:57.083
is going to be house, the variable.
497
01:23:57.083 --> 01:23:58.166
Now, is this better?
498
01:23:58.166 --> 01:23:59.000
Maybe, maybe not.
499
01:23:59.000 --> 01:24:00.958
Maybe the first way was
a little more readable,
500
01:24:00.958 --> 01:24:03.416
and that's totally fine
to create variables
501
01:24:03.416 --> 01:24:05.833
if they improve the
readability of your code.
502
01:24:05.833 --> 01:24:09.833
But just know that you can also create
and return a dictionary on the fly
503
01:24:09.833 --> 01:24:12.208
like this, so to speak,
all in one line, and I
504
01:24:12.208 --> 01:24:14.333
think it's arguably pretty
reasonable in this case.
505
01:24:14.333 --> 01:24:14.875
Why?
506
01:24:14.875 --> 01:24:16.083
It's just pretty short.
507
01:24:16.083 --> 01:24:18.875
I probably wouldn't do this if it
got longer and longer and longer.
508
01:24:18.875 --> 01:24:22.583
I might minimally then start moving
my key value pairs to separate lines.
509
01:24:22.583 --> 01:24:27.458
But this would just be a slightly more
compact way of doing this as well.
510
01:24:27.458 --> 01:24:29.375
But let me propose we
do one more change.
511
01:24:29.375 --> 01:24:32.958
Let's go ahead and introduce
that same special casing of Padma
512
01:24:32.958 --> 01:24:36.458
to fix her house from Gryffindor,
for instance, to Ravenclaw.
513
01:24:36.458 --> 01:24:38.250
How do we do this with dictionaries?
514
01:24:38.250 --> 01:24:41.625
Well, dictionaries,
like lists, are mutable.
515
01:24:41.625 --> 01:24:45.416
You can change what is in
them, just like you can lists.
516
01:24:45.416 --> 01:24:46.250
How do you do that?
517
01:24:46.250 --> 01:24:48.041
It's just a little
different syntactically.
518
01:24:48.041 --> 01:24:50.291
So let's go back into
main and do this fix.
519
01:24:50.291 --> 01:24:57.708
If the student variable has a name
key that equals equals Padma, then,
520
01:24:57.708 --> 01:25:04.375
indented, go ahead and change the value
of the house key inside of that student
521
01:25:04.375 --> 01:25:08.666
dictionary to be, quote,
unquote, "Ravenclaw" instead.
522
01:25:08.666 --> 01:25:11.416
So very similar in spirit
to what we did with a list.
523
01:25:11.416 --> 01:25:16.250
But instead of using location 0 and 1,
we're much more clearly, explicitly,
524
01:25:16.250 --> 01:25:19.375
semantically using, quote, unquote,
"name" and, quote, unquote,
525
01:25:19.375 --> 01:25:24.250
"house," because you index into lists
and tuples using numbers, but you index
526
01:25:24.250 --> 01:25:27.916
into dictionaries using
strings, as I've done here.
527
01:25:27.916 --> 01:25:31.250
All right, let me go ahead
and run Python of student.py.
528
01:25:31.250 --> 01:25:33.208
We'll again do Harry from Gryffindor.
529
01:25:33.208 --> 01:25:35.125
And I think all is well.
530
01:25:35.125 --> 01:25:38.708
Let me run it one more time this
time with Padma, who, in the movies,
531
01:25:38.708 --> 01:25:43.375
is from Gryffindor, but should
really be from Ravenclaw.
532
01:25:43.375 --> 01:25:49.250
Any questions then on this progression
from tuples to lists to dictionaries?
533
01:25:49.250 --> 01:25:53.458
We haven't necessarily introduced
anything new, other than those tuples,
534
01:25:53.458 --> 01:25:56.250
which have been available
to us all this time.
535
01:25:56.250 --> 01:26:00.000
But the goal at the moment is just
to demonstrate this distinction
536
01:26:00.000 --> 01:26:02.250
among these different
data types and how they
537
01:26:02.250 --> 01:26:04.333
each work a little bit differently.
538
01:26:04.333 --> 01:26:10.416
AUDIENCE: What if a combination
of lists is there in our tuple?
539
01:26:10.416 --> 01:26:16.125
We can change the list because are
immutable but lists are mutable?
540
01:26:16.125 --> 01:26:17.125
DAVID J. MALAN: Correct.
541
01:26:17.125 --> 01:26:19.291
You can change the
contents of lists, and you
542
01:26:19.291 --> 01:26:23.125
can put most anything you want in them--
other lists or strings, as I've done,
543
01:26:23.125 --> 01:26:24.583
integers, or anything else.
544
01:26:24.583 --> 01:26:27.916
Tuples you can do the exact same
thing, but you cannot change them once
545
01:26:27.916 --> 01:26:29.083
you've created them.
546
01:26:29.083 --> 01:26:33.041
A dictionary is more like a
list in that it is mutable.
547
01:26:33.041 --> 01:26:34.208
You can change it.
548
01:26:34.208 --> 01:26:37.583
But the way you index
into a dictionary is
549
01:26:37.583 --> 01:26:40.541
by way of these keys, these
strings, as we keep saying,
550
01:26:40.541 --> 01:26:42.250
rather than by numbers--
551
01:26:42.250 --> 01:26:45.083
those numeric indices.
552
01:26:45.083 --> 01:26:47.875
All right, well, let me
propose that there is yet
553
01:26:47.875 --> 01:26:50.458
another way of solving this problem.
554
01:26:50.458 --> 01:26:53.875
And I would argue that there's
now an opportunity at hand.
555
01:26:53.875 --> 01:26:56.750
Even though this program isn't
particularly complicated--
556
01:26:56.750 --> 01:27:01.208
all I'm doing is collecting a
name from the user and a house
557
01:27:01.208 --> 01:27:03.166
from the user-- you
could imagine wanting,
558
01:27:03.166 --> 01:27:05.291
longer term, to collect
even more information,
559
01:27:05.291 --> 01:27:07.791
like the student's
patronus or magical spell
560
01:27:07.791 --> 01:27:10.666
or a whole bunch of other information
that might belong in a student.
561
01:27:10.666 --> 01:27:15.291
And right now, we're just using
these very general purpose data
562
01:27:15.291 --> 01:27:16.458
types in Python--
563
01:27:16.458 --> 01:27:20.083
a tuple to combine some values
together; a list to do the same,
564
01:27:20.083 --> 01:27:23.625
but let us change it later; a
dictionary, which is more powerful
565
01:27:23.625 --> 01:27:25.250
because it's a little more structured.
566
01:27:25.250 --> 01:27:28.708
It does have keys, and it
has values, not just values.
567
01:27:28.708 --> 01:27:30.250
But you know what?
568
01:27:30.250 --> 01:27:33.500
We wouldn't have to be having this
conversation if the authors of Python
569
01:27:33.500 --> 01:27:37.125
had just given us a data
type called student.
570
01:27:37.125 --> 01:27:39.000
Wouldn't it have been
nice if there were just
571
01:27:39.000 --> 01:27:42.166
a type of variable I could
create in my code called student?
572
01:27:42.166 --> 01:27:43.916
Then we wouldn't have
to figure out, well,
573
01:27:43.916 --> 01:27:46.416
do we use a tuple or a
list or a dictionary?
574
01:27:46.416 --> 01:27:48.333
But that's pretty reasonable.
575
01:27:48.333 --> 01:27:52.166
You can imagine just how slippery
of a slope that is, so to speak,
576
01:27:52.166 --> 01:27:55.166
if the creators of a
language had to anticipate
577
01:27:55.166 --> 01:27:58.250
all the possible types of
data that programmers like you
578
01:27:58.250 --> 01:28:00.375
and me want to store in your programs.
579
01:28:00.375 --> 01:28:02.875
So they just gave us these
general purpose tools.
580
01:28:02.875 --> 01:28:05.750
But they gave us another
general purpose tool
581
01:28:05.750 --> 01:28:09.375
that's going to allow us to
create our own data types as well
582
01:28:09.375 --> 01:28:14.291
and actually give them names,
and that terminology is a class.
583
01:28:14.291 --> 01:28:20.125
A class is like a blueprint
for pieces of data objects.
584
01:28:20.125 --> 01:28:24.166
A class is a mold that you
can define and give a name.
585
01:28:24.166 --> 01:28:26.875
And when you use that mold
or you use that blueprint,
586
01:28:26.875 --> 01:28:31.000
you get types of data that are
designed exactly as you want.
587
01:28:31.000 --> 01:28:36.208
So in short, classes allow you to
invent your own data types in Python
588
01:28:36.208 --> 01:28:37.375
and give them a name.
589
01:28:37.375 --> 01:28:41.291
And this is a primary feature
of object oriented programming,
590
01:28:41.291 --> 01:28:44.041
to be able to create your
own objects in this way
591
01:28:44.041 --> 01:28:48.291
and, in the case of Python in classes,
even give them some custom names.
592
01:28:48.291 --> 01:28:50.333
So what does this mean in real terms?
593
01:28:50.333 --> 01:28:52.375
Well, let me go ahead
and come back to VS Code
594
01:28:52.375 --> 01:28:56.500
here, and let me propose that we
introduce a little bit of new syntax.
595
01:28:56.500 --> 01:28:58.916
I'm going to go ahead and
clear my terminal window first.
596
01:28:58.916 --> 01:29:02.791
I'm going to go to the top of my file,
and I'm just going to start a thought
597
01:29:02.791 --> 01:29:03.916
but not finish it yet.
598
01:29:03.916 --> 01:29:09.083
I'm going to use this new keyword for
classes, called, literally, class,
599
01:29:09.083 --> 01:29:11.708
so the new keyword we're
going to have here.
600
01:29:11.708 --> 01:29:13.916
And if I go back to
our slides here, this
601
01:29:13.916 --> 01:29:16.083
would be the official URL
where you can read up more
602
01:29:16.083 --> 01:29:18.000
on this particular feature of Python.
603
01:29:18.000 --> 01:29:22.083
In the official tutorial, class
is a new keyword we can use.
604
01:29:22.083 --> 01:29:25.500
Now, this is coincidentally related to
students because students take classes,
605
01:29:25.500 --> 01:29:28.500
but it has nothing to do with the
fact that we're dealing with students.
606
01:29:28.500 --> 01:29:31.791
Class is a general purpose
term in a lot of languages--
607
01:29:31.791 --> 01:29:35.500
Python among them-- that allow you
to define these custom containers
608
01:29:35.500 --> 01:29:38.625
with custom names for pieces of data.
609
01:29:38.625 --> 01:29:39.875
So let's go back to VS Code.
610
01:29:39.875 --> 01:29:41.333
Let's use this new keyword.
611
01:29:41.333 --> 01:29:44.500
And let me propose that we
create a class called Student.
612
01:29:44.500 --> 01:29:47.000
And by convention, I'm going
to use a capital S here,
613
01:29:47.000 --> 01:29:49.583
and I'm going to go
ahead, and with a colon,
614
01:29:49.583 --> 01:29:52.500
get to, later, the
implementation of this class.
615
01:29:52.500 --> 01:29:55.625
So I'm just going to use dot dot dot,
which is a valid placeholder for now,
616
01:29:55.625 --> 01:29:57.833
that just indicates to me
that I'm going to come back
617
01:29:57.833 --> 01:29:59.000
to implementing this later.
618
01:29:59.000 --> 01:30:02.125
But as of now, it does, in fact, exist.
619
01:30:02.125 --> 01:30:08.458
I now have a student class defined for
me that I can now use in my code here.
620
01:30:08.458 --> 01:30:09.875
How am I going to use it?
621
01:30:09.875 --> 01:30:13.250
Well, first of all, let
me go down to get_student,
622
01:30:13.250 --> 01:30:18.625
and let me change this code to no longer
use a dictionary but to use this class.
623
01:30:18.625 --> 01:30:19.666
I'm going to do this.
624
01:30:19.666 --> 01:30:22.791
I'm going to give myself a variable
called student, as I've done before,
625
01:30:22.791 --> 01:30:26.166
but I'm going to set it
equal to capital Student ().
626
01:30:26.166 --> 01:30:28.916
627
01:30:28.916 --> 01:30:32.208
So I'm going to do what appears
to be calling a function
628
01:30:32.208 --> 01:30:35.583
and that function, Student
with a capital S, notice,
629
01:30:35.583 --> 01:30:39.500
matches the name that I gave
this class at the top of my file.
630
01:30:39.500 --> 01:30:41.166
All right, what do I next want to do?
631
01:30:41.166 --> 01:30:43.333
I'm going to go ahead and
give this student a name.
632
01:30:43.333 --> 01:30:46.041
Now, if I were still
using a dictionary, I
633
01:30:46.041 --> 01:30:50.541
would say student, quote, unquote,
"name," using square brackets.
634
01:30:50.541 --> 01:30:52.125
But this is not a dictionary.
635
01:30:52.125 --> 01:30:54.500
It turns out classes
have what, for now, we'll
636
01:30:54.500 --> 01:30:58.916
call attributes, properties of sorts
that allow you to specify values inside
637
01:30:58.916 --> 01:30:59.416
of them.
638
01:30:59.416 --> 01:31:01.708
And the syntax for that
happens to be a dot.
639
01:31:01.708 --> 01:31:03.166
We've seen dots before.
640
01:31:03.166 --> 01:31:06.333
We've used it in the context of
modules and libraries, more generally.
641
01:31:06.333 --> 01:31:08.875
This is another similar
in spirit use of a dot
642
01:31:08.875 --> 01:31:12.125
that allows you to get at
something inside of something else.
643
01:31:12.125 --> 01:31:15.208
So student.name is
going to be the syntax
644
01:31:15.208 --> 01:31:18.083
I use for giving this student a name.
645
01:31:18.083 --> 01:31:21.666
And that name is going to be whatever
the return value of "Name" is.
646
01:31:21.666 --> 01:31:24.958
And then I'm going to go ahead
and say student.house to give
647
01:31:24.958 --> 01:31:29.291
another attribute called "House" and
give that the return value of input
648
01:31:29.291 --> 01:31:30.750
here, prompting the user for house.
649
01:31:30.750 --> 01:31:33.625
And then, as before, I'm
just going to return student.
650
01:31:33.625 --> 01:31:37.166
But now what's really
powerful about class,
651
01:31:37.166 --> 01:31:39.791
and object-oriented
programming more generally,
652
01:31:39.791 --> 01:31:43.375
is that I've created this custom
data type called, literally,
653
01:31:43.375 --> 01:31:47.333
Student, capital S. I've stored
one such student in a variable
654
01:31:47.333 --> 01:31:50.833
like I can always do in a variable
called student, lowercase s.
655
01:31:50.833 --> 01:31:52.333
But I could call it anything I want.
656
01:31:52.333 --> 01:31:56.166
It just makes sense to call it student
as well, but lowercase for clarity.
657
01:31:56.166 --> 01:31:58.291
And then I'm returning that variable.
658
01:31:58.291 --> 01:32:01.666
And because of my syntax
in lines 14 and 15,
659
01:32:01.666 --> 01:32:05.083
that has the result of
putting inside of that class
660
01:32:05.083 --> 01:32:09.291
a name attribute and a house attribute.
661
01:32:09.291 --> 01:32:11.625
I just need to make one
more change up here.
662
01:32:11.625 --> 01:32:13.416
I'm going to go ahead
and remove our Padma
663
01:32:13.416 --> 01:32:15.500
code, just so we can
focus only on what's new,
664
01:32:15.500 --> 01:32:17.416
rather than fixing her house.
665
01:32:17.416 --> 01:32:20.208
And I'm going to go in here
and change the syntax that
666
01:32:20.208 --> 01:32:21.916
previously was for dictionaries.
667
01:32:21.916 --> 01:32:26.958
Again, dictionaries use square
brackets and then strings in quotes--
668
01:32:26.958 --> 01:32:29.958
either single quotes or double
quotes, depending on the context.
669
01:32:29.958 --> 01:32:33.208
Here, though, I'm going to
change this to be student.name,
670
01:32:33.208 --> 01:32:37.458
and over here, I'm going to
change it to be student.house.
671
01:32:37.458 --> 01:32:40.791
And that's just going to be my new
syntax for getting the contents of what
672
01:32:40.791 --> 01:32:43.250
appears to be a class called student.
673
01:32:43.250 --> 01:32:47.458
Let me go ahead and rerun
Python of student.py, Enter.
674
01:32:47.458 --> 01:32:49.333
Let's type in Harry's name as before.
675
01:32:49.333 --> 01:32:52.500
Let's put him in Gryffindor,
crossing our fingers as we often do,
676
01:32:52.500 --> 01:32:56.500
and Harry is indeed from Gryffindor.
677
01:32:56.500 --> 01:32:58.166
What, though, have I done?
678
01:32:58.166 --> 01:33:01.000
Let's introduce one other
bit of terminology here
679
01:33:01.000 --> 01:33:05.583
it turns out that I can create a
class, using that class keyword.
680
01:33:05.583 --> 01:33:09.958
But any time you use a class, you're
creating what are called objects.
681
01:33:09.958 --> 01:33:14.666
And here is the word objects, as an
object-oriented programming, or OOP.
682
01:33:14.666 --> 01:33:16.250
Let me go back to my code here.
683
01:33:16.250 --> 01:33:18.916
And even though I haven't really
implemented much of it at all--
684
01:33:18.916 --> 01:33:20.958
I literally just left it
with a dot, dot, dot--
685
01:33:20.958 --> 01:33:25.416
that's enough code, lines 1 and 2,
to just invent a new data type called
686
01:33:25.416 --> 01:33:29.708
Student, capital S, that may or may not
have some future functionality as well.
687
01:33:29.708 --> 01:33:31.375
That's enough to create a class.
688
01:33:31.375 --> 01:33:34.166
What, though, am I doing on line 11?
689
01:33:34.166 --> 01:33:40.083
On line 11, what I'm technically doing
is creating an object of that class.
690
01:33:40.083 --> 01:33:42.000
So this, too, is another term of art.
691
01:33:42.000 --> 01:33:44.791
You create objects from classes.
692
01:33:44.791 --> 01:33:47.000
So if we go back to that
metaphor, that a class is
693
01:33:47.000 --> 01:33:50.916
like a blueprint for a house
or a class is like a mold,
694
01:33:50.916 --> 01:33:54.750
an object is when you use that
blueprint to build a specific house
695
01:33:54.750 --> 01:33:58.458
or something that comes out of-- in
plaster, the mold, when you actually
696
01:33:58.458 --> 01:34:01.250
use that mold to create such an object.
697
01:34:01.250 --> 01:34:04.916
So a class is, again, the
definition of a new data type.
698
01:34:04.916 --> 01:34:10.125
The object is the incarnation of,
or technically instantiation of.
699
01:34:10.125 --> 01:34:13.583
And another term for objects
would actually be an instance.
700
01:34:13.583 --> 01:34:16.208
You have instances of classes as well.
701
01:34:16.208 --> 01:34:17.458
So that's a lot of vocabulary.
702
01:34:17.458 --> 01:34:19.708
But at the end of the day,
it just boils down to this.
703
01:34:19.708 --> 01:34:22.750
You can define your own class,
which is really your own data type.
704
01:34:22.750 --> 01:34:26.791
You can then store attributes inside
of it, using this dot notation here.
705
01:34:26.791 --> 01:34:31.041
And then you can access those same
attributes using code like this here.
706
01:34:31.041 --> 01:34:33.958
And now, I have a proper
"student" data type,
707
01:34:33.958 --> 01:34:36.625
and I don't have to
hack something together
708
01:34:36.625 --> 01:34:39.000
using a tuple or a list
or even a dictionary.
709
01:34:39.000 --> 01:34:43.500
I now have a proper data type called
"student" that the authors of Python
710
01:34:43.500 --> 01:34:46.041
didn't give me; I gave myself.
711
01:34:46.041 --> 01:34:50.791
Any questions now on classes, this
new keyword, class, or this idea
712
01:34:50.791 --> 01:34:53.625
of these objects or instances thereof?
713
01:34:53.625 --> 01:34:58.333
AUDIENCE: Is the class
object mutable or immutable?
714
01:34:58.333 --> 01:34:59.666
DAVID J. MALAN: A good question.
715
01:34:59.666 --> 01:35:02.208
And we've clearly laid the stage
for having that conversation
716
01:35:02.208 --> 01:35:03.541
about every data type now.
717
01:35:03.541 --> 01:35:07.958
We will see that they are mutable,
but you can make them immutable.
718
01:35:07.958 --> 01:35:10.083
So you can get the best of both worlds.
719
01:35:10.083 --> 01:35:13.416
Now, by writing some actual code-- and
we'll write more code than the dot,
720
01:35:13.416 --> 01:35:18.208
dot, dot in just a bit, other questions
on classes or these objects thereof?
721
01:35:18.208 --> 01:35:21.625
AUDIENCE: Then what would be
the properties of those classes?
722
01:35:21.625 --> 01:35:24.750
DAVID J. MALAN: So at the moment, the
properties of-- or the attributes of,
723
01:35:24.750 --> 01:35:28.250
as I've been calling them thus far--
would just be "Name" and "House."
724
01:35:28.250 --> 01:35:32.333
It turns out that there may very well
be other attributes built into classes
725
01:35:32.333 --> 01:35:33.541
that we may see before long.
726
01:35:33.541 --> 01:35:36.166
But for now, the only two
attributes that I care about
727
01:35:36.166 --> 01:35:38.333
are the ones that I myself created--
728
01:35:38.333 --> 01:35:42.208
namely "Name" and "House" or,
again, what I would call attributes.
729
01:35:42.208 --> 01:35:43.916
And in a little bit,
we're going to start
730
01:35:43.916 --> 01:35:47.541
calling those same attributes, more
technically, instance variables.
731
01:35:47.541 --> 01:35:50.708
"Name" and "House," as I
presented them here in VS Code
732
01:35:50.708 --> 01:35:55.750
are really just variables called
"name" and called "house" inside
733
01:35:55.750 --> 01:36:00.125
of an object whose type is student.
734
01:36:00.125 --> 01:36:02.583
All right, so what more can
we do with these classes?
735
01:36:02.583 --> 01:36:06.208
Well, again, on line 11 is
where we're instantiating
736
01:36:06.208 --> 01:36:11.000
an object of the student class and
assigning it to a student variable.
737
01:36:11.000 --> 01:36:12.416
We're then adding attributes--
738
01:36:12.416 --> 01:36:16.000
"Name" and "House," respectively--
on lines 12 and 13 currently.
739
01:36:16.000 --> 01:36:17.958
Both of those have values
that are technically
740
01:36:17.958 --> 01:36:21.208
strings or strs, because that's what
the return value of the input is.
741
01:36:21.208 --> 01:36:24.000
But those attributes values
could actually be any data type.
742
01:36:24.000 --> 01:36:27.000
We're just keeping things simple
and focusing on defining students
743
01:36:27.000 --> 01:36:28.333
in terms of two strings--
744
01:36:28.333 --> 01:36:29.375
"Name" and "House."
745
01:36:29.375 --> 01:36:32.125
And then, on line 14, we're
returning that variable.
746
01:36:32.125 --> 01:36:35.333
We're returning that object to
main so that we can actually
747
01:36:35.333 --> 01:36:37.708
print out who is from what house.
748
01:36:37.708 --> 01:36:40.958
Well, let's go ahead and add a bit more
functionality here because, right now,
749
01:36:40.958 --> 01:36:44.166
on lines 12 and 13,
this is a little manual.
750
01:36:44.166 --> 01:36:47.333
And it's a little reckless of
me to just be putting anything
751
01:36:47.333 --> 01:36:50.333
I want inside of this student object.
752
01:36:50.333 --> 01:36:53.041
It turns out with classes,
unlike with dictionaries,
753
01:36:53.041 --> 01:36:57.625
we can actually standardize, all the
more, what those attributes can be
754
01:36:57.625 --> 01:37:00.291
and what kinds of values
you can set them to.
755
01:37:00.291 --> 01:37:02.375
So let me go ahead and do this.
756
01:37:02.375 --> 01:37:04.375
Let me propose that it
would actually be really
757
01:37:04.375 --> 01:37:08.875
nice if, instead of doing
this here, let me go ahead
758
01:37:08.875 --> 01:37:10.833
and simplify my code as follows.
759
01:37:10.833 --> 01:37:13.708
Let me go ahead and give myself
a local variable called name
760
01:37:13.708 --> 01:37:17.208
and set it equal to the return value of
input, like we've done many times now
761
01:37:17.208 --> 01:37:17.791
already.
762
01:37:17.791 --> 01:37:20.333
Let me give myself one other
variable for now, called house,
763
01:37:20.333 --> 01:37:23.333
and set it equal to the
return value of input
764
01:37:23.333 --> 01:37:25.291
as well, prompting the
user for their house.
765
01:37:25.291 --> 01:37:30.416
And now, instead of creating a
student object from my student class
766
01:37:30.416 --> 01:37:34.500
and then manually putting the
name attribute inside of it
767
01:37:34.500 --> 01:37:37.166
and the house attribute
inside of it, let
768
01:37:37.166 --> 01:37:39.458
me actually do something more powerful.
769
01:37:39.458 --> 01:37:40.583
Let me do this.
770
01:37:40.583 --> 01:37:45.333
Let me call that Student function,
which is identical to the class name--
771
01:37:45.333 --> 01:37:48.125
just by defining a
class, you get a function
772
01:37:48.125 --> 01:37:52.583
whose name is identical to the class
name, with the capital letter included.
773
01:37:52.583 --> 01:37:54.625
But instead of just
doing open parenthesis,
774
01:37:54.625 --> 01:37:58.166
closed parenthesis, let
me pass in the name that I
775
01:37:58.166 --> 01:38:02.791
want to fill this object with and the
house that I want to put in that object
776
01:38:02.791 --> 01:38:03.375
as well.
777
01:38:03.375 --> 01:38:09.375
And now let me set the return value as
before to be student equals like this.
778
01:38:09.375 --> 01:38:11.083
So what have I done that's different?
779
01:38:11.083 --> 01:38:13.583
Fundamentally, I'm still getting
user input in the same way.
780
01:38:13.583 --> 01:38:16.750
I'm using input on line
11 and input on line 12.
781
01:38:16.750 --> 01:38:21.125
And I just so happen to be storing
those return values in local variables.
782
01:38:21.125 --> 01:38:24.875
And now we're setting the stage for
the more powerful features of classes
783
01:38:24.875 --> 01:38:27.208
and object-oriented
programming more generally.
784
01:38:27.208 --> 01:38:31.500
Notice that I'm deliberately
passing to this capital S Student
785
01:38:31.500 --> 01:38:33.875
function, name, house--
786
01:38:33.875 --> 01:38:36.291
I'm passing in arguments
to the function.
787
01:38:36.291 --> 01:38:39.750
Now, the student class is not going
to know what to do with those yet,
788
01:38:39.750 --> 01:38:44.791
but now I'm standardizing how I'm
passing data into this student class.
789
01:38:44.791 --> 01:38:47.833
And ultimately, it's going to
give me an opportunity to error
790
01:38:47.833 --> 01:38:51.166
check those inputs, to make sure that
the name is valid, that it has a value
791
01:38:51.166 --> 01:38:52.875
and it's not just the
user hitting Enter.
792
01:38:52.875 --> 01:38:56.041
It's going to allow me to ensure
that it's a valid house, that it's
793
01:38:56.041 --> 01:38:59.208
Gryffindor or Hufflepuff
or Ravenclaw or Slytherin
794
01:38:59.208 --> 01:39:02.916
or not just hitting Enter or some
random value that the user types in.
795
01:39:02.916 --> 01:39:05.916
Because I'm passing "Name"
and "House" to the student
796
01:39:05.916 --> 01:39:08.791
class, this particular
function, I'm going
797
01:39:08.791 --> 01:39:11.416
to have more control over
the correctness of my data.
798
01:39:11.416 --> 01:39:14.541
So let's now go up to the student
class, which, up until now,
799
01:39:14.541 --> 01:39:16.125
I left as just dot, dot, dot.
800
01:39:16.125 --> 01:39:19.625
It turns out that, in
the context of classes,
801
01:39:19.625 --> 01:39:23.916
there are a number of not
just attributes or instance
802
01:39:23.916 --> 01:39:27.041
variables that you can put
inside, but also methods.
803
01:39:27.041 --> 01:39:32.083
Classes come with certain methods,
or functions inside of them,
804
01:39:32.083 --> 01:39:35.708
that you can define, and they
just behave in a special way,
805
01:39:35.708 --> 01:39:37.875
by nature of how Python works.
806
01:39:37.875 --> 01:39:42.625
These functions allow you to
determine behavior in a standard way.
807
01:39:42.625 --> 01:39:45.083
They are special methods in that sense.
808
01:39:45.083 --> 01:39:46.333
Now, what do I mean by this.
809
01:39:46.333 --> 01:39:48.083
Well, let me go back to VS Code here.
810
01:39:48.083 --> 01:39:53.000
And let me propose that I start
to define a standard function
811
01:39:53.000 --> 01:39:56.916
called underscore underscore,
or Dunder, as it's
812
01:39:56.916 --> 01:39:59.916
abbreviated, init,
underscore underscore,
813
01:39:59.916 --> 01:40:02.916
and then I'm going to go
ahead and do open parentheses,
814
01:40:02.916 --> 01:40:06.875
and then I'm going to put in
here, literally, the word self.
815
01:40:06.875 --> 01:40:08.416
More on that in just a moment.
816
01:40:08.416 --> 01:40:11.791
But now, inside of this function,
I'm going to have an opportunity
817
01:40:11.791 --> 01:40:16.791
to customize this class's objects.
818
01:40:16.791 --> 01:40:20.333
That is to say this underscore,
underscore init method,
819
01:40:20.333 --> 01:40:25.208
or Dunder init method is specifically
known as an instance method,
820
01:40:25.208 --> 01:40:26.708
and it's called exactly this.
821
01:40:26.708 --> 01:40:28.875
This is designed by
the authors of Python.
822
01:40:28.875 --> 01:40:34.291
And if you want to initialize the
contents of an object from a class,
823
01:40:34.291 --> 01:40:37.875
you define this method, and we'll
see what it's about to do here.
824
01:40:37.875 --> 01:40:41.250
Let me go back to VS Code, and
let me do something like this.
825
01:40:41.250 --> 01:40:47.750
self.name = name, and
self.house = house.
826
01:40:47.750 --> 01:40:51.250
But I don't want to just init
this object very generically.
827
01:40:51.250 --> 01:40:56.166
I want this method, called init,
to take in not just self but name,
828
01:40:56.166 --> 01:40:58.375
house as well.
829
01:40:58.375 --> 01:40:59.916
Now, what in the world is going on?
830
01:40:59.916 --> 01:41:01.708
Because there's a lot
of weird syntax here.
831
01:41:01.708 --> 01:41:03.375
There's this Dunder init method--
832
01:41:03.375 --> 01:41:05.791
double underscore,
init, double underscore.
833
01:41:05.791 --> 01:41:08.750
There's, all of a sudden,
this parameter called self.
834
01:41:08.750 --> 01:41:12.458
And then there's this new
syntax-- self.name and self.house.
835
01:41:12.458 --> 01:41:16.541
Now you're seeing really a manifestation
of object-oriented programming.
836
01:41:16.541 --> 01:41:18.250
It's not all that
different fundamentally
837
01:41:18.250 --> 01:41:20.541
from what we've been doing
for weeks with dictionaries,
838
01:41:20.541 --> 01:41:23.125
by adding keys to dictionaries.
839
01:41:23.125 --> 01:41:28.333
But in this case, we're adding
variables to objects, a.k.a.
840
01:41:28.333 --> 01:41:30.500
instance variables to objects.
841
01:41:30.500 --> 01:41:31.458
Now, what's going on?
842
01:41:31.458 --> 01:41:32.541
Let's do this in reverse.
843
01:41:32.541 --> 01:41:34.750
Let's go back to the line
of code we wrote earlier.
844
01:41:34.750 --> 01:41:38.708
On line 15, I am treating
the name of this class--
845
01:41:38.708 --> 01:41:41.916
Student with a capital
S-- as a function.
846
01:41:41.916 --> 01:41:44.583
And I am passing in two values--
847
01:41:44.583 --> 01:41:45.916
"Name" and "House."
848
01:41:45.916 --> 01:41:48.708
What I've highlighted here
on the screen, on line 15,
849
01:41:48.708 --> 01:41:51.250
is generally known as
a constructor call.
850
01:41:51.250 --> 01:41:56.875
This is a line of code that is going
to construct a student object for me.
851
01:41:56.875 --> 01:42:02.791
Using synonyms, it is going to
instantiate a student object for me.
852
01:42:02.791 --> 01:42:05.583
And again, how is it going
to create that object?
853
01:42:05.583 --> 01:42:09.458
It's going to use the student class
as a template, as a mold of sorts
854
01:42:09.458 --> 01:42:12.291
so that every student
is structured the same.
855
01:42:12.291 --> 01:42:13.875
Every student is going to have a name.
856
01:42:13.875 --> 01:42:15.458
Every student's going to have a house.
857
01:42:15.458 --> 01:42:20.916
But because I can pass in arguments
to this Student function, capital S,
858
01:42:20.916 --> 01:42:26.666
I'm going to be able to customize
the contents of that object.
859
01:42:26.666 --> 01:42:29.666
So if you think about the real world--
if you've ever been on a street
860
01:42:29.666 --> 01:42:34.666
or a neighborhood where all of the
houses look the same but they might be
861
01:42:34.666 --> 01:42:37.541
painted differently; they might be
decorated a little bit differently
862
01:42:37.541 --> 01:42:40.958
on the outside, all of those houses
might have been built using the exact
863
01:42:40.958 --> 01:42:42.541
same blueprint--
864
01:42:42.541 --> 01:42:43.875
a mold, if you will.
865
01:42:43.875 --> 01:42:48.083
But then you can specialize exactly
the finer points of those houses.
866
01:42:48.083 --> 01:42:51.000
By painting the outside a different
color or planting different trees,
867
01:42:51.000 --> 01:42:52.500
you can style them differently.
868
01:42:52.500 --> 01:42:57.750
Similar in spirit here, we
have a Student blueprint
869
01:42:57.750 --> 01:43:01.583
that's always going to have now a name
and a house, but it's up to you and me
870
01:43:01.583 --> 01:43:04.166
to pass in any name and
any house that we want.
871
01:43:04.166 --> 01:43:06.041
Now, where is this function?
872
01:43:06.041 --> 01:43:09.125
The fact that I'm calling Student,
capital, S and then a parenthesis
873
01:43:09.125 --> 01:43:11.250
and a closed parenthesis
with arguments inside
874
01:43:11.250 --> 01:43:12.958
suggest that there's
a function somewhere
875
01:43:12.958 --> 01:43:17.500
in the world that has been defined,
with def, that's going to be called.
876
01:43:17.500 --> 01:43:20.416
Well, as you might have
guessed by now, the function
877
01:43:20.416 --> 01:43:25.500
that will always be called, by
definition of how Python classes work,
878
01:43:25.500 --> 01:43:30.000
is a function called double
underscore, init, double underscore.
879
01:43:30.000 --> 01:43:30.500
Why?
880
01:43:30.500 --> 01:43:33.333
It's a crazy name, but it's
what the authors of Python
881
01:43:33.333 --> 01:43:38.750
chose to just implement the
initialization of an object in Python.
882
01:43:38.750 --> 01:43:40.916
Now, the only weird thing--
883
01:43:40.916 --> 01:43:43.125
especially weird thing,
I will admit, is this.
884
01:43:43.125 --> 01:43:49.125
It would be way clearer, to me, too, if
the only two parameters for init we're
885
01:43:49.125 --> 01:43:50.666
just name, house.
886
01:43:50.666 --> 01:43:53.541
That's how we've defined every
function thus far in the class.
887
01:43:53.541 --> 01:43:57.291
You just specify the parameters that
you want the function to accept.
888
01:43:57.291 --> 01:44:00.333
And that lines up with
what I'm doing on line 15.
889
01:44:00.333 --> 01:44:04.541
I am only passing in two
things to the student function.
890
01:44:04.541 --> 01:44:07.833
But it turns out that
the authors of Python
891
01:44:07.833 --> 01:44:09.541
need to give us a
little bit of help here
892
01:44:09.541 --> 01:44:14.458
because suppose that you pass in
"Name" and "House" to this init method.
893
01:44:14.458 --> 01:44:17.833
And a method is just a
function inside of a class.
894
01:44:17.833 --> 01:44:20.291
What are you going to do
with the name and the house?
895
01:44:20.291 --> 01:44:22.333
Literally, where are
you going to put them?
896
01:44:22.333 --> 01:44:26.500
If you want to remember the name
and the house for this student,
897
01:44:26.500 --> 01:44:29.166
you've got to be able to
store those values somewhere.
898
01:44:29.166 --> 01:44:32.416
And how do you store them
in the current object
899
01:44:32.416 --> 01:44:34.833
that has just been "instantiated?"
900
01:44:34.833 --> 01:44:37.625
Well, the authors of Python
decided that the convention
901
01:44:37.625 --> 01:44:41.958
is going to be that this init
method also, semi secretly, takes
902
01:44:41.958 --> 01:44:44.625
a third argument, that
has to come first.
903
01:44:44.625 --> 01:44:46.666
By convention, it's
called self, but you could
904
01:44:46.666 --> 01:44:48.250
call it technically anything you want.
905
01:44:48.250 --> 01:44:50.208
But the convention is
to always call it self.
906
01:44:50.208 --> 01:44:53.500
And self, as its name
implies, gives you access
907
01:44:53.500 --> 01:44:56.791
to the current object
that was just created.
908
01:44:56.791 --> 01:44:58.000
What does that mean?
909
01:44:58.000 --> 01:45:02.166
Again, now, on line 14, now that
it's moved down a little bit,
910
01:45:02.166 --> 01:45:04.083
this line here is a constructor.
911
01:45:04.083 --> 01:45:06.166
It constructs a student object.
912
01:45:06.166 --> 01:45:08.291
But there's nothing in
that object initially.
913
01:45:08.291 --> 01:45:09.750
There's no name; there's no house.
914
01:45:09.750 --> 01:45:12.791
But the object exists in
the computer's memory.
915
01:45:12.791 --> 01:45:16.541
It's up to, now, you to store the name
and the house inside of that object.
916
01:45:16.541 --> 01:45:17.500
How do you do that?
917
01:45:17.500 --> 01:45:21.500
Well, Python will just automatically
call this init method for you,
918
01:45:21.500 --> 01:45:27.500
and it's going to automatically pass
in a reference to an argument that
919
01:45:27.500 --> 01:45:32.375
represents the current object that it
just constructed in memory for you,
920
01:45:32.375 --> 01:45:34.708
and it's up to you to
populate it with values.
921
01:45:34.708 --> 01:45:37.666
And what this means is that,
inside of your init method,
922
01:45:37.666 --> 01:45:42.875
you can literally do self.name
to create a new attribute, a.k.a.
923
01:45:42.875 --> 01:45:46.166
an instance variable, inside
of that otherwise empty
924
01:45:46.166 --> 01:45:48.416
object and put this name inside of it.
925
01:45:48.416 --> 01:45:51.875
It allows you to do self.house
and store that value of house.
926
01:45:51.875 --> 01:45:54.000
Now, you could call these
things anything you want.
927
01:45:54.000 --> 01:45:54.750
They could be n.
928
01:45:54.750 --> 01:45:56.416
They could be h, as before.
929
01:45:56.416 --> 01:46:00.250
But that's really not
very self-explanatory.
930
01:46:00.250 --> 01:46:04.250
Much better to do this kind of
convention. self.name equals name.
931
01:46:04.250 --> 01:46:06.333
self.house equals house.
932
01:46:06.333 --> 01:46:11.708
And this is like installing into the
otherwise empty object the value name
933
01:46:11.708 --> 01:46:16.083
and house and storing them in,
really, identically named instance
934
01:46:16.083 --> 01:46:17.708
variables in the object.
935
01:46:17.708 --> 01:46:20.833
And again, an object is
just an instance of a class.
936
01:46:20.833 --> 01:46:23.208
Now, I know that was
a lot of vocabulary.
937
01:46:23.208 --> 01:46:24.875
That's a lot of weird syntax.
938
01:46:24.875 --> 01:46:28.625
So any questions on this init
method, whose purpose in life,
939
01:46:28.625 --> 01:46:33.541
again, is to initialize an otherwise
empty object when you first create it?
940
01:46:33.541 --> 01:46:36.791
AUDIENCE: So what is the difference
between the init method and default
941
01:46:36.791 --> 01:46:37.500
constructor?
942
01:46:37.500 --> 01:46:38.833
DAVID J. MALAN: A good question.
943
01:46:38.833 --> 01:46:41.583
So in other languages--
if you programmed before.
944
01:46:41.583 --> 01:46:45.166
For instance, Java-- there are
functions that are explicitly called
945
01:46:45.166 --> 01:46:47.916
constructors that construct an object.
946
01:46:47.916 --> 01:46:49.416
They initialize it with values.
947
01:46:49.416 --> 01:46:53.291
Python technically calls this init
method the initialization method.
948
01:46:53.291 --> 01:46:54.958
It initializes the value.
949
01:46:54.958 --> 01:47:00.875
It's on line 15 now of my code, if I
scroll back down, that I'm technically
950
01:47:00.875 --> 01:47:02.416
constructing the object.
951
01:47:02.416 --> 01:47:05.333
It turns out there's another
special method in Python,
952
01:47:05.333 --> 01:47:07.291
that we won't talk
about in detail today,
953
01:47:07.291 --> 01:47:11.125
called underscore underscore,
new, underscore underscore
954
01:47:11.125 --> 01:47:13.375
that actually handles
the process of creating
955
01:47:13.375 --> 01:47:15.458
an empty object in memory for us.
956
01:47:15.458 --> 01:47:17.833
But, generally speaking,
you, the programmer,
957
01:47:17.833 --> 01:47:19.916
don't need to manipulate
the new function.
958
01:47:19.916 --> 01:47:20.916
It just works for you.
959
01:47:20.916 --> 01:47:23.750
Instead, you define your
own init method here
960
01:47:23.750 --> 01:47:27.041
and init function inside of
your class, and that method
961
01:47:27.041 --> 01:47:29.916
initializes the contents of the object.
962
01:47:29.916 --> 01:47:32.750
So there's technically a distinction
between constructing the object
963
01:47:32.750 --> 01:47:35.250
with new and initializing it with init.
964
01:47:35.250 --> 01:47:39.250
But in the world of Python, you pretty
much only worry about the init method.
965
01:47:39.250 --> 01:47:42.250
Python generally does
the other part for you.
966
01:47:42.250 --> 01:47:43.000
A good question.
967
01:47:43.000 --> 01:47:44.333
Others?
968
01:47:44.333 --> 01:47:48.166
AUDIENCE: What about if you want
to store more than one name or more
969
01:47:48.166 --> 01:47:49.083
than one house?
970
01:47:49.083 --> 01:47:49.750
DAVID J. MALAN: A good question.
971
01:47:49.750 --> 01:47:52.666
If you want to store more than
one name or more than one house,
972
01:47:52.666 --> 01:47:54.083
you can do this in different ways.
973
01:47:54.083 --> 01:47:57.916
You could create other attributes--
technically called instance variables--
974
01:47:57.916 --> 01:48:01.708
like self.name1, self.name2.
975
01:48:01.708 --> 01:48:03.708
But we've seen, in the
past, that that is not
976
01:48:03.708 --> 01:48:06.291
a very good design, just to
have multiple variables to store
977
01:48:06.291 --> 01:48:07.250
multiple things.
978
01:48:07.250 --> 01:48:12.625
Maybe, instead, you have an instance
variable called self.names, plural,
979
01:48:12.625 --> 01:48:15.958
and you set it equal to a list
of names or a list of houses.
980
01:48:15.958 --> 01:48:19.000
Now, in this case, I don't think that
really solves a problem because I'm
981
01:48:19.000 --> 01:48:22.958
trying to implement a student, singular,
so it doesn't really make sense
982
01:48:22.958 --> 01:48:24.250
to have multiple first names.
983
01:48:24.250 --> 01:48:27.166
Maybe a nickname, maybe a last
name, so we could add those, too.
984
01:48:27.166 --> 01:48:29.375
But I don't think we need
multiple names per se
985
01:48:29.375 --> 01:48:31.333
and, in this case, multiple houses.
986
01:48:31.333 --> 01:48:34.500
But absolutely, you could do that using
some of our familiar building blocks
987
01:48:34.500 --> 01:48:35.916
like lists.
988
01:48:35.916 --> 01:48:37.541
Other questions?
989
01:48:37.541 --> 01:48:40.291
AUDIENCE: How are classes or
objects represented in memory?
990
01:48:40.291 --> 01:48:43.041
DAVID J. MALAN: How are classes
and objects represented in memory?
991
01:48:43.041 --> 01:48:44.958
So the class is technically just code.
992
01:48:44.958 --> 01:48:48.333
It is the code on the top of my
file-- lines 1 through fou4--
993
01:48:48.333 --> 01:48:52.250
that defines that blueprint,
that template, if you will.
994
01:48:52.250 --> 01:48:54.833
Objects are stored in
the computer's memory
995
01:48:54.833 --> 01:48:56.333
by taking up some number of bytes.
996
01:48:56.333 --> 01:48:59.125
So you're probably familiar with
bytes or kilobytes or megabytes.
997
01:48:59.125 --> 01:49:01.750
There's some chunk of
bytes, probably all
998
01:49:01.750 --> 01:49:03.708
in the same location in
the computer's memory
999
01:49:03.708 --> 01:49:07.916
or RAM, where those objects are stored.
1000
01:49:07.916 --> 01:49:11.500
But that's what Python, the
program, handles for you.
1001
01:49:11.500 --> 01:49:14.958
Python the interpreter figures out where
in the computer's memory to put it.
1002
01:49:14.958 --> 01:49:18.041
You and I, the programmers, get to
think and solve problems at this level.
1003
01:49:18.041 --> 01:49:21.916
Python, the interpreter, handles
those lower level details for you.
1004
01:49:21.916 --> 01:49:25.166
How about one final question
on classes and objects?
1005
01:49:25.166 --> 01:49:28.125
AUDIENCE: So my question
is if we can the same do
1006
01:49:28.125 --> 01:49:31.625
the same thing with the
dictionaries, so why to use classes?
1007
01:49:31.625 --> 01:49:32.875
DAVID J. MALAN: Good question.
1008
01:49:32.875 --> 01:49:34.750
If you can do the same things
as you can with dictionaries,
1009
01:49:34.750 --> 01:49:35.958
why should you use classes?
1010
01:49:35.958 --> 01:49:38.416
Because we are just scratching
the surface now of what
1011
01:49:38.416 --> 01:49:39.833
you can do with classes.
1012
01:49:39.833 --> 01:49:42.666
Allow me to go back, now,
to my keyboard and show you
1013
01:49:42.666 --> 01:49:44.375
more of what you can do with classes.
1014
01:49:44.375 --> 01:49:47.750
But in short, you can do
much more with classes.
1015
01:49:47.750 --> 01:49:51.791
You can ensure the correctness of
your data much more with classes.
1016
01:49:51.791 --> 01:49:53.166
You can error-check things.
1017
01:49:53.166 --> 01:49:57.375
And generally, you can design more
complicated software more effectively.
1018
01:49:57.375 --> 01:49:59.458
And we'll continue to
see, today, features
1019
01:49:59.458 --> 01:50:02.416
of Python and object-oriented
programming more generally
1020
01:50:02.416 --> 01:50:04.875
that allows us to do just that.
1021
01:50:04.875 --> 01:50:09.333
So let me propose, in fact, that first,
let's just tighten up this current
1022
01:50:09.333 --> 01:50:13.916
implementation, which again has us with
an init method that just declares two
1023
01:50:13.916 --> 01:50:16.875
instance variables-- self.name and
self.house, house, which, again,
1024
01:50:16.875 --> 01:50:21.416
just creates those variables inside of
the otherwise empty object and assigns
1025
01:50:21.416 --> 01:50:22.125
them values--
1026
01:50:22.125 --> 01:50:23.708
name and house, respectively.
1027
01:50:23.708 --> 01:50:26.000
Let me go ahead and just
do one little thing here.
1028
01:50:26.000 --> 01:50:27.791
I don't really need
this student variable.
1029
01:50:27.791 --> 01:50:31.750
Let me just tighten this up so that
each time we improve or change the code,
1030
01:50:31.750 --> 01:50:35.000
we're focusing, really, on
just the minimal changes alone.
1031
01:50:35.000 --> 01:50:37.250
So I've not fundamentally
done anything different.
1032
01:50:37.250 --> 01:50:39.291
I just got rid of the
variable name, and I'm just
1033
01:50:39.291 --> 01:50:43.125
returning the return value
of this student function
1034
01:50:43.125 --> 01:50:45.125
that's constructing
my new object for me.
1035
01:50:45.125 --> 01:50:48.833
So I'm just tightening things up as
we've done many times in the past.
1036
01:50:48.833 --> 01:50:53.291
Well, what if something goes
wrong in creating this student?
1037
01:50:53.291 --> 01:50:56.791
For instance, what if the user does
not give us a name, and they just hit
1038
01:50:56.791 --> 01:50:58.791
Enter when prompted for name.
1039
01:50:58.791 --> 01:51:01.250
I don't want to put in
my computer's memory
1040
01:51:01.250 --> 01:51:04.375
a bogus student object that has no name.
1041
01:51:04.375 --> 01:51:07.833
I'd ideally like to check for
errors before I even create it
1042
01:51:07.833 --> 01:51:09.583
so I don't create a nameless student.
1043
01:51:09.583 --> 01:51:13.791
It would just be weird and probably a
bug to have an object that has no name.
1044
01:51:13.791 --> 01:51:16.208
Similarly, I don't want
the user to be able to type
1045
01:51:16.208 --> 01:51:18.708
in something random as their house.
1046
01:51:18.708 --> 01:51:21.375
At least in the world of
Harry Potter, there's really
1047
01:51:21.375 --> 01:51:23.791
only four houses, at Hogwarts at least.
1048
01:51:23.791 --> 01:51:25.666
There's, again,
Gryffindor and Hufflepuff
1049
01:51:25.666 --> 01:51:29.625
and Ravenclaw and Slytherin--
a list of four valid houses.
1050
01:51:29.625 --> 01:51:34.291
It would be nice if I somehow validated
that the user's input is indeed
1051
01:51:34.291 --> 01:51:35.541
in that list.
1052
01:51:35.541 --> 01:51:39.916
Now, I could do all of that
validation in my get_student function.
1053
01:51:39.916 --> 01:51:41.750
I could check, is the name empty?
1054
01:51:41.750 --> 01:51:44.416
If so, don't create the student object.
1055
01:51:44.416 --> 01:51:46.666
Is the house one of those four houses?
1056
01:51:46.666 --> 01:51:49.083
If not, don't create the student object.
1057
01:51:49.083 --> 01:51:52.750
But that would be rather
decoupled from the student itself.
1058
01:51:52.750 --> 01:51:57.625
get_student currently exists as just
my own function in my student.py file.
1059
01:51:57.625 --> 01:52:01.125
But classes-- and really,
object-oriented programming-- more
1060
01:52:01.125 --> 01:52:05.291
generally encourages you
to encapsulate, inside
1061
01:52:05.291 --> 01:52:09.750
of a class, all functionality
related to that class.
1062
01:52:09.750 --> 01:52:12.375
So if you want to validate
that a name exists--
1063
01:52:12.375 --> 01:52:14.750
if you want to validate
that a house is correct,
1064
01:52:14.750 --> 01:52:20.041
that belongs just fundamentally in
the class called student itself,
1065
01:52:20.041 --> 01:52:22.583
not in some random function
that you wrote elsewhere.
1066
01:52:22.583 --> 01:52:24.875
Again, this is just
methodology because, again,
1067
01:52:24.875 --> 01:52:28.541
if we think about writing code that
gets longer and longer, more and more
1068
01:52:28.541 --> 01:52:31.250
complicated, it should make
just intuitive sense that,
1069
01:52:31.250 --> 01:52:34.250
if you keep all the house--
1070
01:52:34.250 --> 01:52:37.208
all of the name and all of the
house-related code in the student,
1071
01:52:37.208 --> 01:52:38.708
it's just better organization.
1072
01:52:38.708 --> 01:52:41.583
Keep all of the related code
together, and that's probably
1073
01:52:41.583 --> 01:52:43.125
going to set you up for more success.
1074
01:52:43.125 --> 01:52:45.166
And indeed, that's part
of this methodology
1075
01:52:45.166 --> 01:52:47.125
of object-oriented programming.
1076
01:52:47.125 --> 01:52:51.916
Let me go ahead now and
change my students classes
1077
01:52:51.916 --> 01:52:53.875
init method to do this.
1078
01:52:53.875 --> 01:52:56.375
If the name is blank--
1079
01:52:56.375 --> 01:52:59.333
so if not name-- and we've seen
this kind of syntax before.
1080
01:52:59.333 --> 01:53:02.666
If you say in Python,
Pythonically, if not name,
1081
01:53:02.666 --> 01:53:04.500
that's doing something like this.
1082
01:53:04.500 --> 01:53:07.291
If name equals, equals, quote, unquote--
1083
01:53:07.291 --> 01:53:09.041
but I can do this a
little more elegantly.
1084
01:53:09.041 --> 01:53:11.958
Just say, if not name,
would be the more Pythonic.
1085
01:53:11.958 --> 01:53:15.583
Well, I want to return an error.
1086
01:53:15.583 --> 01:53:17.208
I might want to do something like this.
1087
01:53:17.208 --> 01:53:18.916
Print missing name.
1088
01:53:18.916 --> 01:53:20.541
But this is not good enough.
1089
01:53:20.541 --> 01:53:24.000
It does not suffice to
just print out missing name
1090
01:53:24.000 --> 01:53:26.083
and then let the rest
of the code go through.
1091
01:53:26.083 --> 01:53:27.791
All right, well, what
could I do instead?
1092
01:53:27.791 --> 01:53:30.791
In the past, we've seen another
technique I could do sys.exit,
1093
01:53:30.791 --> 01:53:33.583
and I could say something like
missing name, and I could go up here
1094
01:53:33.583 --> 01:53:34.750
and I could import sys.
1095
01:53:34.750 --> 01:53:37.958
But this is a really obnoxious
solution to the problem.
1096
01:53:37.958 --> 01:53:41.250
Just because you or maybe
a colleague messed up
1097
01:53:41.250 --> 01:53:43.750
and called a function
with an invalid name,
1098
01:53:43.750 --> 01:53:45.666
you're going to quit my whole program?
1099
01:53:45.666 --> 01:53:49.750
That's really, really
extreme of a response,
1100
01:53:49.750 --> 01:53:52.125
and you probably don't want
to do that if your program is
1101
01:53:52.125 --> 01:53:53.041
in the middle of running.
1102
01:53:53.041 --> 01:53:54.625
You might want to clean some stuff up.
1103
01:53:54.625 --> 01:53:57.958
You might want to save files you don't
want to just exit a program sometimes
1104
01:53:57.958 --> 01:54:01.208
in some arbitrary line, just
because input was invalid.
1105
01:54:01.208 --> 01:54:03.333
So I don't think we
want to do that either.
1106
01:54:03.333 --> 01:54:07.375
But we do, now, have a
mechanism for signaling errors.
1107
01:54:07.375 --> 01:54:09.500
Unfortunately, I can't
do something like this.
1108
01:54:09.500 --> 01:54:13.625
I could try returning none and say,
uh-uh, this student does not exist.
1109
01:54:13.625 --> 01:54:15.666
I'm going to hand you back none instead.
1110
01:54:15.666 --> 01:54:16.916
But it's too late.
1111
01:54:16.916 --> 01:54:21.416
If we scroll back down to where I'm
creating the student, it's on line 17
1112
01:54:21.416 --> 01:54:23.083
now where I've highlighted this code.
1113
01:54:23.083 --> 01:54:25.541
The student has already been created.
1114
01:54:25.541 --> 01:54:28.750
There is an object somewhere
in the computer's memory
1115
01:54:28.750 --> 01:54:30.375
that's structured as a student.
1116
01:54:30.375 --> 01:54:32.708
It just doesn't have
any values inside of it.
1117
01:54:32.708 --> 01:54:35.541
But it's too late,
therefore, to return none.
1118
01:54:35.541 --> 01:54:36.666
That ship has sailed.
1119
01:54:36.666 --> 01:54:37.958
The object exists.
1120
01:54:37.958 --> 01:54:40.458
You can't just suddenly say,
nope, nope, there is no object.
1121
01:54:40.458 --> 01:54:41.625
There is an object.
1122
01:54:41.625 --> 01:54:43.583
It's up to you to signal an error.
1123
01:54:43.583 --> 01:54:45.333
And how do you signal an error?
1124
01:54:45.333 --> 01:54:48.083
Well, we've actually seen this
before, but we haven't had occasion
1125
01:54:48.083 --> 01:54:49.583
to create our own errors.
1126
01:54:49.583 --> 01:54:54.750
It turns out, in Python, there's
another keyword related to exceptions
1127
01:54:54.750 --> 01:54:58.583
that Python itself uses to raise
all of those exceptions we've
1128
01:54:58.583 --> 01:54:59.750
talked about in the past.
1129
01:54:59.750 --> 01:55:04.500
When you've caught things like value
errors or other such exceptions that
1130
01:55:04.500 --> 01:55:09.083
come with Python, well, it turns
out you, the programmer can raise--
1131
01:55:09.083 --> 01:55:12.875
that is create your own exceptions
when something just really goes wrong--
1132
01:55:12.875 --> 01:55:15.750
not wrong enough that you want to
quit and exit the whole program,
1133
01:55:15.750 --> 01:55:18.958
but enough that you need to
somehow alert the programmer
1134
01:55:18.958 --> 01:55:20.583
that there has been an error.
1135
01:55:20.583 --> 01:55:23.583
Something exceptional, in
a very bad way-- something
1136
01:55:23.583 --> 01:55:29.250
exceptional has happened, and let them
try to catch that exception as needed.
1137
01:55:29.250 --> 01:55:32.250
So let me go back to VS
Code here and propose
1138
01:55:32.250 --> 01:55:36.500
that, if the user passes in an
invalid name-- it's just empty,
1139
01:55:36.500 --> 01:55:37.833
so there's not a name.
1140
01:55:37.833 --> 01:55:39.875
Well, what I really want to do is this.
1141
01:55:39.875 --> 01:55:43.041
I want to raise a value error.
1142
01:55:43.041 --> 01:55:45.208
And we've seen the value errors before.
1143
01:55:45.208 --> 01:55:47.833
We've created value errors
accidentally before.
1144
01:55:47.833 --> 01:55:51.416
And generally, you and I have
tried to catch them if they happen.
1145
01:55:51.416 --> 01:55:55.375
Well, the flip side of this feature of
exceptions in a language like Python
1146
01:55:55.375 --> 01:55:58.125
is that you, the programmer,
can also raise exceptions
1147
01:55:58.125 --> 01:56:00.000
when something exceptional happens.
1148
01:56:00.000 --> 01:56:01.791
And you can even be more precise.
1149
01:56:01.791 --> 01:56:05.250
You don't have to raise a generic value
error and let the programmer figure out
1150
01:56:05.250 --> 01:56:06.083
what went wrong.
1151
01:56:06.083 --> 01:56:10.291
You can treat value error and all
exceptions in Python like functions
1152
01:56:10.291 --> 01:56:14.583
and actually pass to them an explanatory
message like, quote, unquote,
1153
01:56:14.583 --> 01:56:17.875
"Missing name," so that at least
the programmer, when they encounter
1154
01:56:17.875 --> 01:56:19.458
this error, knows, oh, I messed up.
1155
01:56:19.458 --> 01:56:22.833
I didn't make sure that
the user has a name.
1156
01:56:22.833 --> 01:56:25.125
And now, what do you want to do instead?
1157
01:56:25.125 --> 01:56:28.708
Well, now, if you're the programmer,
you could do something like this.
1158
01:56:28.708 --> 01:56:34.708
You could try to create a student
except if there's a value error.
1159
01:56:34.708 --> 01:56:37.208
Then you could handle it in some way.
1160
01:56:37.208 --> 01:56:39.500
And I'm going to wave my
hand with a dot, dot, dot,
1161
01:56:39.500 --> 01:56:40.791
at how you would handle it.
1162
01:56:40.791 --> 01:56:44.666
But you would handle it using try and
accept, just like we have in the past,
1163
01:56:44.666 --> 01:56:46.666
and that would allow
you, the programmer,
1164
01:56:46.666 --> 01:56:48.166
to try to create the student.
1165
01:56:48.166 --> 01:56:52.291
But if something goes wrong,
OK, I'll handle it nonetheless.
1166
01:56:52.291 --> 01:56:54.958
So what's new here, again,
is this raise keyword,
1167
01:56:54.958 --> 01:56:59.250
that just lets you and I
actually raise our own exceptions
1168
01:56:59.250 --> 01:57:00.458
to signal these errors.
1169
01:57:00.458 --> 01:57:03.000
Well, let me go back to
my code here, and I'm just
1170
01:57:03.000 --> 01:57:06.208
going to go ahead and not bother
trying or catching this error.
1171
01:57:06.208 --> 01:57:09.083
For now, we'll just focus
on raising it and assume
1172
01:57:09.083 --> 01:57:12.375
that, from our recon exceptions,
you could add try and accept
1173
01:57:12.375 --> 01:57:13.833
as needed in places.
1174
01:57:13.833 --> 01:57:16.916
Let me go back to the code here and
propose that something else could
1175
01:57:16.916 --> 01:57:18.416
go wrong with house.
1176
01:57:18.416 --> 01:57:19.791
If there is a name, we're good.
1177
01:57:19.791 --> 01:57:22.083
But if we're given a
house but it's invalid,
1178
01:57:22.083 --> 01:57:24.958
we should probably raise
an exception for that, too.
1179
01:57:24.958 --> 01:57:26.208
So what if we do this?
1180
01:57:26.208 --> 01:57:32.208
If house is not in the list containing
"Gryffindor," quote, unquote,
1181
01:57:32.208 --> 01:57:34.708
"Hufflepuff," quote, unquote--
1182
01:57:34.708 --> 01:57:38.875
let's see, "Ravenclaw," quote,
unquote, or "Slytherin,"
1183
01:57:38.875 --> 01:57:41.833
quote, unquote, then,
with my colon, let's
1184
01:57:41.833 --> 01:57:43.416
raise another type of value error.
1185
01:57:43.416 --> 01:57:45.458
But rather than raise
a generic value error,
1186
01:57:45.458 --> 01:57:49.750
let's pass in an argument,
quote, unquote, "Invalid house."
1187
01:57:49.750 --> 01:57:52.708
And so here we now see
a capability that we
1188
01:57:52.708 --> 01:57:55.916
can do with classes that
we can't with dictionaries.
1189
01:57:55.916 --> 01:58:00.541
If you add an attribute to a
dictionary, a key to a dictionary,
1190
01:58:00.541 --> 01:58:02.083
it's going in no matter what.
1191
01:58:02.083 --> 01:58:06.000
Even if the name is empty, even if the
house is a completely random string
1192
01:58:06.000 --> 01:58:07.833
of text that's not one
of these four houses,
1193
01:58:07.833 --> 01:58:09.333
it's going into that dictionary.
1194
01:58:09.333 --> 01:58:12.708
But with a class, and by
way of this init method,
1195
01:58:12.708 --> 01:58:17.750
you and I can now control exactly what's
going to be installed, if you will,
1196
01:58:17.750 --> 01:58:19.041
inside of this object.
1197
01:58:19.041 --> 01:58:22.458
You have a little more
control now over correctness.
1198
01:58:22.458 --> 01:58:26.583
And so now let me go ahead and scroll
back down to my terminal window
1199
01:58:26.583 --> 01:58:27.291
and clear it.
1200
01:58:27.291 --> 01:58:29.500
Let me run Python of student.py.
1201
01:58:29.500 --> 01:58:31.250
Let me type in something like Harry.
1202
01:58:31.250 --> 01:58:33.958
Let me type in Gryffindor,
Enter, and we see
1203
01:58:33.958 --> 01:58:35.666
that, indeed, Harry is from Gryffindor.
1204
01:58:35.666 --> 01:58:37.666
What if I made a mistake, though?
1205
01:58:37.666 --> 01:58:40.750
What if I ran Python of student.py
and typed Harry as the name,
1206
01:58:40.750 --> 01:58:44.416
but this time typed in Number
Four, Privet Drive, which
1207
01:58:44.416 --> 01:58:47.500
is where he grew up, instead
of his proper Hogwarts house.
1208
01:58:47.500 --> 01:58:51.333
Let me hit Enter now, and
now you see a value error.
1209
01:58:51.333 --> 01:58:54.458
But this isn't one that Python
generated for us, per se.
1210
01:58:54.458 --> 01:58:56.166
I raised this error.
1211
01:58:56.166 --> 01:58:59.666
And therefore, if I went in and wrote
more code in my get_student function,
1212
01:58:59.666 --> 01:59:04.666
I could also catch this error
with our usual try except syntax.
1213
01:59:04.666 --> 01:59:09.250
So all we have now is not just classes
in our toolkit, but even more powers
1214
01:59:09.250 --> 01:59:12.583
when it comes to exceptions, and
not just catching them ourselves
1215
01:59:12.583 --> 01:59:15.333
but raising them ourselves, too.
1216
01:59:15.333 --> 01:59:21.708
Any questions now on this use of
classes and init and now this ability
1217
01:59:21.708 --> 01:59:26.000
to raise exceptions when something goes
wrong inside of the initialization?
1218
01:59:26.000 --> 01:59:29.416
AUDIENCE: So what if the
user has a middle name--
1219
01:59:29.416 --> 01:59:31.291
name, middle name, and last name?
1220
01:59:31.291 --> 01:59:32.750
How would you fix that?
1221
01:59:32.750 --> 01:59:35.250
DAVID J. MALAN: Good question.
1222
01:59:35.250 --> 01:59:38.916
If you wanted the student to have a
first name, middle name, and last name,
1223
01:59:38.916 --> 01:59:41.041
we could do this in a
bunch of different ways.
1224
01:59:41.041 --> 01:59:44.458
The simplest, though, if-- let
me clear my screen here, and let
1225
01:59:44.458 --> 01:59:46.041
me just temporarily do this.
1226
01:59:46.041 --> 01:59:51.333
Let me propose that the init method take
in a first argument, a middle argument,
1227
01:59:51.333 --> 01:59:53.000
and a last argument.
1228
01:59:53.000 --> 01:59:57.833
And then what I think I would do down
here is ultimately have first = first,
1229
01:59:57.833 --> 02:00:00.916
and then I would do the same
thing for middle and last.
1230
02:00:00.916 --> 02:00:05.541
So middle and middle,
and then last and last.
1231
02:00:05.541 --> 02:00:08.583
And then what I would
have to do here is,
1232
02:00:08.583 --> 02:00:11.208
when I actually ask the
user for their name,
1233
02:00:11.208 --> 02:00:12.750
I might need to really go all out.
1234
02:00:12.750 --> 02:00:15.166
I might need to ask them
first for their first name
1235
02:00:15.166 --> 02:00:18.916
and store that in a variable called
first, and therefore pass in first.
1236
02:00:18.916 --> 02:00:21.833
I might similarly need to ask
them for their middle name
1237
02:00:21.833 --> 02:00:25.500
and store that in a variable and then
pass in a second argument, middle.
1238
02:00:25.500 --> 02:00:28.375
And then lastly, if you will,
let me go ahead and create
1239
02:00:28.375 --> 02:00:31.916
a third variable called last, get
the input for their last name,
1240
02:00:31.916 --> 02:00:34.041
and pass that in as well.
1241
02:00:34.041 --> 02:00:38.125
I could instead just use one input and
just ask them for their whole name.
1242
02:00:38.125 --> 02:00:42.583
So type in David Malan, Enter, or
David J. Malan-- all three of them,
1243
02:00:42.583 --> 02:00:45.666
and maybe I could use
Python's split function,
1244
02:00:45.666 --> 02:00:47.583
maybe a regular expression
to tease it apart.
1245
02:00:47.583 --> 02:00:49.625
That's probably going to
be messy because there's
1246
02:00:49.625 --> 02:00:52.083
going to be people who don't
have just two or three names.
1247
02:00:52.083 --> 02:00:53.458
They might have four or five.
1248
02:00:53.458 --> 02:00:55.958
So maybe sometimes it's better
to have multiple prompts.
1249
02:00:55.958 --> 02:00:58.291
But that's not a problem
because, with a class,
1250
02:00:58.291 --> 02:01:02.291
we have the expressiveness to
take in more arguments if we want.
1251
02:01:02.291 --> 02:01:04.041
We could even take a list if we wanted.
1252
02:01:04.041 --> 02:01:06.916
But I think we'd probably want to
have even more error checking then,
1253
02:01:06.916 --> 02:01:11.458
not just for name but for first,
and then maybe for middle, and then
1254
02:01:11.458 --> 02:01:12.208
maybe for last.
1255
02:01:12.208 --> 02:01:14.541
So it just is more and more
code, though there would be
1256
02:01:14.541 --> 02:01:17.041
ways to perhaps consolidate that, too.
1257
02:01:17.041 --> 02:01:22.500
Let me undo all of that and see if there
are other questions now on classes.
1258
02:01:22.500 --> 02:01:24.291
AUDIENCE: I assume
classes are something I
1259
02:01:24.291 --> 02:01:25.916
might do at the beginning of a project.
1260
02:01:25.916 --> 02:01:28.583
Can I just put them in a
different file and import them
1261
02:01:28.583 --> 02:01:32.083
into my project, or my
main code as needed?
1262
02:01:32.083 --> 02:01:33.208
DAVID J. MALAN: Absolutely.
1263
02:01:33.208 --> 02:01:34.208
A really good question.
1264
02:01:34.208 --> 02:01:36.083
You could imagine wanting
to use this student
1265
02:01:36.083 --> 02:01:40.666
class, not just in student.py but in
other files or other projects of yours.
1266
02:01:40.666 --> 02:01:44.458
And absolutely, you can create
your own library of classes
1267
02:01:44.458 --> 02:01:48.000
by putting the student class
in your own module or package,
1268
02:01:48.000 --> 02:01:50.583
per our discussion in the past
about libraries more generally.
1269
02:01:50.583 --> 02:01:52.250
And absolutely, you can do that.
1270
02:01:52.250 --> 02:01:54.833
And later today, what
we see is we've actually
1271
02:01:54.833 --> 02:01:58.166
been using classes-- you and I--
before, in third party libraries.
1272
02:01:58.166 --> 02:02:00.416
So you, too, can absolutely do the same.
1273
02:02:00.416 --> 02:02:03.666
How about one more question on classes?
1274
02:02:03.666 --> 02:02:06.541
AUDIENCE: Can you have
optional variables in classes?
1275
02:02:06.541 --> 02:02:09.666
And two, can you have your
own error names, like--
1276
02:02:09.666 --> 02:02:13.250
let's be egotistical and say
I want to raise Eric error?
1277
02:02:13.250 --> 02:02:16.166
DAVID J. MALAN: Short answer, yes.
1278
02:02:16.166 --> 02:02:18.750
These init functions are just
like Python functions more
1279
02:02:18.750 --> 02:02:21.000
generally, even though they're
special in that they're
1280
02:02:21.000 --> 02:02:23.666
going to get called
automatically by Python for you.
1281
02:02:23.666 --> 02:02:25.375
But if you wanted to
make house optional,
1282
02:02:25.375 --> 02:02:26.750
you could do something like this.
1283
02:02:26.750 --> 02:02:31.791
You could give it a default value
in the init function's signature
1284
02:02:31.791 --> 02:02:34.250
so to speak-- in that first
line of code on line two.
1285
02:02:34.250 --> 02:02:36.875
And that would allow me to
not have to pass in house.
1286
02:02:36.875 --> 02:02:39.750
In this case, I'm going to continue
to always pass in name and house,
1287
02:02:39.750 --> 02:02:41.500
but you could make things optional.
1288
02:02:41.500 --> 02:02:45.458
And yes, to your second question, if you
wanted to have your own error message,
1289
02:02:45.458 --> 02:02:50.791
like an Eric error, you could actually
create your own Eric error exception.
1290
02:02:50.791 --> 02:02:53.000
And we'll see, in a little
bit, that there's actually
1291
02:02:53.000 --> 02:02:58.041
a whole suite of exceptions that exist,
and you, too, can invent those as well.
1292
02:02:58.041 --> 02:03:01.375
Let me propose, though,
that we now introduce
1293
02:03:01.375 --> 02:03:06.250
one other aspect of this whereby we try
printing out what a student looks like.
1294
02:03:06.250 --> 02:03:08.916
At the moment, if I scroll
back down to my main function,
1295
02:03:08.916 --> 02:03:11.791
I'm still printing the student's
name and house very manually.
1296
02:03:11.791 --> 02:03:14.833
I'm going inside of the
object, doing student.name,
1297
02:03:14.833 --> 02:03:17.708
and I'm going inside of the object
again and getting student.house,
1298
02:03:17.708 --> 02:03:20.750
just to see where the student is from.
1299
02:03:20.750 --> 02:03:23.666
But wouldn't it be nice if I
could just print the student,
1300
02:03:23.666 --> 02:03:25.583
like I've been printing for weeks--
1301
02:03:25.583 --> 02:03:29.458
any, int, or float, or str,
or any other data type?
1302
02:03:29.458 --> 02:03:32.500
Well, let's see what happens if
I just try printing the student,
1303
02:03:32.500 --> 02:03:36.166
instead of manually going inside and
trying to create that sentence myself.
1304
02:03:36.166 --> 02:03:39.250
Well, in my terminal window-- let me
go ahead and run Python of student.py
1305
02:03:39.250 --> 02:03:40.166
again.
1306
02:03:40.166 --> 02:03:41.250
Let me type in Harry.
1307
02:03:41.250 --> 02:03:42.541
Let me type in Gryffindor.
1308
02:03:42.541 --> 02:03:50.333
And voila, Harry-- whoa, OK, main
student object at 0x102733e80.
1309
02:03:50.333 --> 02:03:51.916
Well, what is going on?
1310
02:03:51.916 --> 02:03:53.708
Well, if you were to
run the same code, you
1311
02:03:53.708 --> 02:03:56.000
might actually see something
different on your computer
1312
02:03:56.000 --> 02:03:57.208
in terms of that number.
1313
02:03:57.208 --> 02:04:01.958
But what you're really seeing is the
underlying representation, as a string,
1314
02:04:01.958 --> 02:04:03.625
of this specific object.
1315
02:04:03.625 --> 02:04:06.875
In particular, you're seeing where
in the computer's memory it is.
1316
02:04:06.875 --> 02:04:12.750
This number, 30x102733e80, refers
to, essentially, a specific location
1317
02:04:12.750 --> 02:04:14.541
in the computer's memory or RAM.
1318
02:04:14.541 --> 02:04:19.166
That's not really that interesting
for me or you or, generally speaking,
1319
02:04:19.166 --> 02:04:22.250
programmers, but it's just
the default way of describing,
1320
02:04:22.250 --> 02:04:25.791
via print, what this thing is.
1321
02:04:25.791 --> 02:04:28.250
But I can override this as well.
1322
02:04:28.250 --> 02:04:31.791
It turns out that there are
other special methods in Python
1323
02:04:31.791 --> 02:04:32.958
when it comes to classes--
1324
02:04:32.958 --> 02:04:36.375
not just underscore underscore,
init, underscore underscore,
1325
02:04:36.375 --> 02:04:40.541
but, continuing in that same
pattern, underscore underscore, str,
1326
02:04:40.541 --> 02:04:42.041
underscore underscore.
1327
02:04:42.041 --> 02:04:47.000
So this, too, is a special method that,
if you define it inside of your class,
1328
02:04:47.000 --> 02:04:50.541
Python will just automatically
call this function
1329
02:04:50.541 --> 02:04:56.541
for you any time some other function
wants to see your object as a string.
1330
02:04:56.541 --> 02:04:59.750
Print wants to see your
object as a string.
1331
02:04:59.750 --> 02:05:02.958
But by default, if you don't have
this method defined in your class,
1332
02:05:02.958 --> 02:05:06.416
it's going to print out that very
ugly esoteric incarnation thereof,
1333
02:05:06.416 --> 02:05:10.416
where it says main__.Student
object at 0x, dot, dot, dot.
1334
02:05:10.416 --> 02:05:13.250
Well, how can I then
define my own str function?
1335
02:05:13.250 --> 02:05:17.041
Well, here, back in VS Code,
let me propose that I go in
1336
02:05:17.041 --> 02:05:23.416
and define not just __ init, but let me
define a second function in this class
1337
02:05:23.416 --> 02:05:25.041
here, as follows--
1338
02:05:25.041 --> 02:05:28.416
def __ str __.
1339
02:05:28.416 --> 02:05:29.333
There are two.
1340
02:05:29.333 --> 02:05:32.541
Even though the font in VS Code is
putting the two underscore so close,
1341
02:05:32.541 --> 02:05:34.375
it just looks like a longer underscore.
1342
02:05:34.375 --> 02:05:37.583
There are indeed two there, on the
left and the right just like for init.
1343
02:05:37.583 --> 02:05:42.791
This one only takes one argument that,
by convention, is always called self
1344
02:05:42.791 --> 02:05:44.500
so that you have access to it.
1345
02:05:44.500 --> 02:05:47.708
And then, indented below
that after a colon,
1346
02:05:47.708 --> 02:05:51.125
I'm going to go ahead and create
a format string and return it.
1347
02:05:51.125 --> 02:05:53.500
So let me go ahead and return--
1348
02:05:53.500 --> 02:05:56.375
how about something generic
first like "a student."
1349
02:05:56.375 --> 02:05:58.916
So I'm not going to bother
even trying to figure out
1350
02:05:58.916 --> 02:06:00.625
what this student's name or house is.
1351
02:06:00.625 --> 02:06:02.875
I'm just going to always
return "a student."
1352
02:06:02.875 --> 02:06:08.666
Let me go back now to my earlier code,
which has print (student) on line 16.
1353
02:06:08.666 --> 02:06:13.041
Let me clear my terminal window and
rerun Python of student.py, Enter.
1354
02:06:13.041 --> 02:06:14.958
Type in Harry, type in Gryffindor.
1355
02:06:14.958 --> 02:06:17.333
Last time, I saw that
very cryptic output.
1356
02:06:17.333 --> 02:06:20.958
This time, I see, more
generically, "a student."
1357
02:06:20.958 --> 02:06:23.375
More readable but not very enlightening.
1358
02:06:23.375 --> 02:06:24.541
Which student is this?
1359
02:06:24.541 --> 02:06:31.791
Well, notice that the double underscore
str method takes in this self argument
1360
02:06:31.791 --> 02:06:36.708
by default. It's just the way the
Python authors designed this method.
1361
02:06:36.708 --> 02:06:41.833
It will always be passed a reference
to the current student object.
1362
02:06:41.833 --> 02:06:43.000
What do I mean by that?
1363
02:06:43.000 --> 02:06:45.916
When this line of code
on line 6 is called,
1364
02:06:45.916 --> 02:06:48.666
print, because it's hoping
it's going to get a string,
1365
02:06:48.666 --> 02:06:52.750
is going to trigger the underscore
underscore, str, underscore
1366
02:06:52.750 --> 02:06:54.458
underscore method to be called.
1367
02:06:54.458 --> 02:06:58.416
And Python, for you, automatically
is going to pass into that method
1368
02:06:58.416 --> 02:07:01.208
a reference to the object
that's trying to be
1369
02:07:01.208 --> 02:07:05.291
printed so that you, the programmer,
can do something like this.
1370
02:07:05.291 --> 02:07:08.000
Here's an f string with
double quotes as usual.
1371
02:07:08.000 --> 02:07:10.458
I'm going to use some curly
braces and say print out
1372
02:07:10.458 --> 02:07:14.250
self.name from self.house.
1373
02:07:14.250 --> 02:07:17.458
So there's nothing new
in what I've just done.
1374
02:07:17.458 --> 02:07:19.958
It's just an f string--
an f on the beginning,
1375
02:07:19.958 --> 02:07:22.708
two double quotes, a couple
of pairs of curly braces.
1376
02:07:22.708 --> 02:07:28.750
But because, automatically, this str
method gets passed self, so to speak,
1377
02:07:28.750 --> 02:07:32.333
a reference to the current object,
I can go inside of that object
1378
02:07:32.333 --> 02:07:33.083
and grab the name.
1379
02:07:33.083 --> 02:07:36.000
I can go inside that object
again and grab the house.
1380
02:07:36.000 --> 02:07:38.791
So now, when I go back
to my terminal window--
1381
02:07:38.791 --> 02:07:41.041
previously it just
printed out a student.
1382
02:07:41.041 --> 02:07:44.416
But now, if I run Python
of student.py, Enter--
1383
02:07:44.416 --> 02:07:48.083
type in Harry, type in Gryffindor,
and one more time hit Enter,
1384
02:07:48.083 --> 02:07:50.750
Harry is again from Gryffindor.
1385
02:07:50.750 --> 02:07:52.500
But if I run this yet again--
1386
02:07:52.500 --> 02:07:57.125
let's, for instance, do Draco
is from Slytherin, Enter.
1387
02:07:57.125 --> 02:07:58.625
Draco's from Slytherin.
1388
02:07:58.625 --> 02:08:04.291
Now it's customized to the specific
object that we're trying to print.
1389
02:08:04.291 --> 02:08:08.250
Questions on this function here--
1390
02:08:08.250 --> 02:08:11.666
this Dunder str method.
1391
02:08:11.666 --> 02:08:14.541
AUDIENCE: Is there anything else
that the underscore underscore,
1392
02:08:14.541 --> 02:08:16.666
str method can do?
1393
02:08:16.666 --> 02:08:19.416
The other question is, what's the
difference between str and repr.
1394
02:08:19.416 --> 02:08:20.750
DAVID J. MALAN: A good question.
1395
02:08:20.750 --> 02:08:24.458
So there are many other methods
that come with Python classes
1396
02:08:24.458 --> 02:08:26.041
that start with underscore underscore.
1397
02:08:26.041 --> 02:08:27.958
We're just scratching the
surface, and we'll pretty much
1398
02:08:27.958 --> 02:08:29.083
focus primarily on these.
1399
02:08:29.083 --> 02:08:30.833
But yes, there are
many others, and we'll
1400
02:08:30.833 --> 02:08:33.666
see at least one other
in just a little bit.
1401
02:08:33.666 --> 02:08:37.375
Among the others is one
called repr, which is
1402
02:08:37.375 --> 02:08:39.583
a representation of the Python object.
1403
02:08:39.583 --> 02:08:43.000
Generally speaking, the
underscore underscore, repr,
1404
02:08:43.000 --> 02:08:45.791
underscore underscore method
is meant for developers' eyes.
1405
02:08:45.791 --> 02:08:49.166
It typically has more information
than "Harry from Gryffindor."
1406
02:08:49.166 --> 02:08:53.083
It would also say what type of
object it is, like a student, capital
1407
02:08:53.083 --> 02:08:56.583
S, whereas underscore underscore, str,
underscore underscore is generally
1408
02:08:56.583 --> 02:08:58.125
meant for users--
1409
02:08:58.125 --> 02:09:01.500
the users of the program, and it's
meant to be even more user-friendly.
1410
02:09:01.500 --> 02:09:04.208
But both of those can be
overridden as you see fit.
1411
02:09:04.208 --> 02:09:08.041
Well, let me propose now that we pick
up where we've left off on student
1412
02:09:08.041 --> 02:09:11.958
and just add even more functionality,
but not just these special methods
1413
02:09:11.958 --> 02:09:15.166
like double underscore init
and double underscore str.
1414
02:09:15.166 --> 02:09:18.750
Let's create our own methods
because therein lies the real power
1415
02:09:18.750 --> 02:09:21.875
and flexibility of classes if
you and I as the programmers
1416
02:09:21.875 --> 02:09:25.291
can invent new functionality
that's specific to students.
1417
02:09:25.291 --> 02:09:29.125
For instance, students at
Hogwarts, over the time in school,
1418
02:09:29.125 --> 02:09:31.500
learn how to cast a
certain type of spell.
1419
02:09:31.500 --> 02:09:34.083
So when they say, Expecto
Patronum, something
1420
02:09:34.083 --> 02:09:36.083
comes out of their wand
that typically resembles
1421
02:09:36.083 --> 02:09:37.458
an animal or something like that.
1422
02:09:37.458 --> 02:09:40.000
It's a special spell that they
have to practice and practice.
1423
02:09:40.000 --> 02:09:44.041
So let's see if we can't store, not
just the student's name and their house,
1424
02:09:44.041 --> 02:09:47.041
but also their "patronus,"
what, actually, they
1425
02:09:47.041 --> 02:09:48.625
conjure when using this spell.
1426
02:09:48.625 --> 02:09:50.750
Well, let me go ahead and
clear my terminal window.
1427
02:09:50.750 --> 02:09:56.583
And in the top of my code here, in the
init method of Student, let me go ahead
1428
02:09:56.583 --> 02:10:00.875
and start expecting a third argument,
in addition to self, which automatically
1429
02:10:00.875 --> 02:10:02.833
gets passed in, called patronus.
1430
02:10:02.833 --> 02:10:06.125
And I'm not going to worry,
for now, on validating
1431
02:10:06.125 --> 02:10:10.041
the patronus from an official list
of valid patronuses, or patroni.
1432
02:10:10.041 --> 02:10:13.041
I'm instead going to go
ahead and just blindly assign
1433
02:10:13.041 --> 02:10:16.458
it to self.patronus
= patronus, and we're
1434
02:10:16.458 --> 02:10:19.083
going to let the user type
whatever they want for now.
1435
02:10:19.083 --> 02:10:21.333
But I could certainly
add more error checking
1436
02:10:21.333 --> 02:10:25.583
if I wanted to limit the patronus
to a specific list of them here.
1437
02:10:25.583 --> 02:10:29.000
Let me go ahead now and prompt
the user for this patronus,
1438
02:10:29.000 --> 02:10:33.666
as by-- in my get_student
function-- defining a variable
1439
02:10:33.666 --> 02:10:36.375
called patronus or anything
else, prompting the user
1440
02:10:36.375 --> 02:10:38.333
for input for their patronus.
1441
02:10:38.333 --> 02:10:42.708
And now I'm going to go ahead and
pass in that third variable here.
1442
02:10:42.708 --> 02:10:46.041
So again, similar in spirit to just
adding more and more attributes
1443
02:10:46.041 --> 02:10:47.875
to the class, I'm going
to pass in all three
1444
02:10:47.875 --> 02:10:49.791
of these values instead of just two.
1445
02:10:49.791 --> 02:10:52.333
I'm not going to do anything
interesting with that value yet.
1446
02:10:52.333 --> 02:10:55.583
But just to make sure I haven't made
things worse by breaking my code,
1447
02:10:55.583 --> 02:10:57.875
let me run Python of student.py.
1448
02:10:57.875 --> 02:10:59.083
I'll type in Harry.
1449
02:10:59.083 --> 02:11:00.416
I'll type in Gryffindor.
1450
02:11:00.416 --> 02:11:02.500
And it turns out his
patronus was a stag, .
1451
02:11:02.500 --> 02:11:06.125
And hit Enter I haven't seen
what his patronus is in my output
1452
02:11:06.125 --> 02:11:08.708
because I didn't change
my str method yet.
1453
02:11:08.708 --> 02:11:10.625
But at least I don't
have any syntax errors.
1454
02:11:10.625 --> 02:11:12.958
So at least I've not
made anything worse.
1455
02:11:12.958 --> 02:11:16.416
But suppose, now, I want to
have functionality, not just
1456
02:11:16.416 --> 02:11:19.708
for initializing a student
and printing out a student.
1457
02:11:19.708 --> 02:11:23.666
If my class is really meant
to be a student, what I can do
1458
02:11:23.666 --> 02:11:27.416
is not just remember information
about data about students.
1459
02:11:27.416 --> 02:11:31.291
What's powerful about classes,
unlike dictionaries alone,
1460
02:11:31.291 --> 02:11:36.708
is that classes can have, not just
variables or instance variables--
1461
02:11:36.708 --> 02:11:38.625
those attributes we keep creating.
1462
02:11:38.625 --> 02:11:42.458
They can also have
functions built in, a.k.a.
1463
02:11:42.458 --> 02:11:43.083
methods.
1464
02:11:43.083 --> 02:11:45.958
When a function is inside of a
class, it's called a "method,"
1465
02:11:45.958 --> 02:11:47.666
but it's still just a function.
1466
02:11:47.666 --> 02:11:51.000
At this point, we've seen two
functions already-- two methods--
1467
02:11:51.000 --> 02:11:54.958
called a double underscore
init and double underscore str,
1468
02:11:54.958 --> 02:11:57.958
but those are special methods
in that they just work.
1469
02:11:57.958 --> 02:12:01.416
If you define them, Python calls
them automatically for you.
1470
02:12:01.416 --> 02:12:04.958
But what if you wanted to create
more functionality for a student
1471
02:12:04.958 --> 02:12:09.000
so that your class really represents
this real world, or maybe "fantasy"
1472
02:12:09.000 --> 02:12:13.375
world notion of a student, where
students not only have names and houses
1473
02:12:13.375 --> 02:12:16.375
and patronuses; they
also have functionality.
1474
02:12:16.375 --> 02:12:22.541
They have actions they can perform like
casting a charm, a spell, magically.
1475
02:12:22.541 --> 02:12:26.333
Could we implement, therefore,
a function called charm, that
1476
02:12:26.333 --> 02:12:30.291
actually uses their magical knowledge?
1477
02:12:30.291 --> 02:12:33.791
Well, let's go ahead and define
our very own function as follows.
1478
02:12:33.791 --> 02:12:37.583
Let me clear my terminal window,
scroll back up to my student class.
1479
02:12:37.583 --> 02:12:40.916
And instead of creating yet
another function that's special,
1480
02:12:40.916 --> 02:12:45.416
with double underscores, I'm going
to invent my own function, or method,
1481
02:12:45.416 --> 02:12:46.916
inside of this class.
1482
02:12:46.916 --> 02:12:51.500
I want to give Harry and Hermione
and all of the other students
1483
02:12:51.500 --> 02:12:55.000
the ability to cast charms, so I'm
going to define a function that I
1484
02:12:55.000 --> 02:12:57.291
can completely, on my own, call charm.
1485
02:12:57.291 --> 02:12:59.125
I could call this
function anything I want.
1486
02:12:59.125 --> 02:13:01.708
But because it's a
method inside of a class,
1487
02:13:01.708 --> 02:13:06.083
the convention is that it's always going
to take at least one argument, called
1488
02:13:06.083 --> 02:13:11.000
self, by convention so that you
have access to the current object,
1489
02:13:11.000 --> 02:13:13.083
even if you don't plan
to use it, per se.
1490
02:13:13.083 --> 02:13:16.708
All right, let me go ahead and propose
that we implement charm in such a way
1491
02:13:16.708 --> 02:13:21.750
that the method returns an emoji
that's appropriate for each student's
1492
02:13:21.750 --> 02:13:23.125
patronus.
1493
02:13:23.125 --> 02:13:25.750
How to implement this-- well,
inside of the charm method,
1494
02:13:25.750 --> 02:13:29.041
let's go ahead and
match on self.patronus,
1495
02:13:29.041 --> 02:13:31.416
which is the instance variable
containing a string that
1496
02:13:31.416 --> 02:13:33.458
represents each student's patronus.
1497
02:13:33.458 --> 02:13:38.208
And in the case that it matches a stag,
for instance, for Harry, let's go ahead
1498
02:13:38.208 --> 02:13:40.333
and return maybe the closest emoji--
1499
02:13:40.333 --> 02:13:41.750
this horse here.
1500
02:13:41.750 --> 02:13:44.625
How about in the case of an otter?
1501
02:13:44.625 --> 02:13:49.708
Well, in that case, let's go ahead and
return, oh, maybe the closest match
1502
02:13:49.708 --> 02:13:52.583
to the otter, which
might be this emoji here.
1503
02:13:52.583 --> 02:13:57.000
And let's see, in the case of a--
for Ron, rather than Hermione--
1504
02:13:57.000 --> 02:14:02.791
a Jack Russell terrier,
let's go ahead and return--
1505
02:14:02.791 --> 02:14:04.625
don't have as many options here.
1506
02:14:04.625 --> 02:14:08.750
Why don't we go ahead and return the
cutest available dog in that case.
1507
02:14:08.750 --> 02:14:14.250
And in the case of no
patronus recognized,
1508
02:14:14.250 --> 02:14:16.375
as might cover someone
like Draco, let's go ahead
1509
02:14:16.375 --> 02:14:19.750
and use a default case using
the underscore as in the past,
1510
02:14:19.750 --> 02:14:22.791
and let's go ahead and return
for this-- oh, what should happen
1511
02:14:22.791 --> 02:14:24.250
if someone doesn't have a patronus?
1512
02:14:24.250 --> 02:14:26.541
Why don't we just see
a magical wand that
1513
02:14:26.541 --> 02:14:28.958
seems to fizzle out, as in this case?
1514
02:14:28.958 --> 02:14:31.333
All right, well, now, rather
than just print the student,
1515
02:14:31.333 --> 02:14:33.375
let's go about printing
their actual patronus.
1516
02:14:33.375 --> 02:14:36.000
So I'm going to go down
to my main function here.
1517
02:14:36.000 --> 02:14:39.250
I'm going to still get a student,
using the get_student function.
1518
02:14:39.250 --> 02:14:44.208
But rather than print student, let's go
ahead and declare "Expecto Patronum!"
1519
02:14:44.208 --> 02:14:46.791
printing out just that as pure text.
1520
02:14:46.791 --> 02:14:50.750
And now let's go ahead and print
out, not the student but, rather,
1521
02:14:50.750 --> 02:14:54.416
the return value of
their own charm method.
1522
02:14:54.416 --> 02:14:58.583
So let me go back down to my terminal
window and run Python of student.py
1523
02:14:58.583 --> 02:14:59.708
and Enter.
1524
02:14:59.708 --> 02:15:01.250
Name-- let's start with Harry.
1525
02:15:01.250 --> 02:15:03.500
He lives in Gryffindor.
1526
02:15:03.500 --> 02:15:05.083
Patronus is a stag.
1527
02:15:05.083 --> 02:15:06.291
And let's see--
1528
02:15:06.291 --> 02:15:08.125
Expecto Patronum!
1529
02:15:08.125 --> 02:15:10.708
And of course, we'd see the stag emoji.
1530
02:15:10.708 --> 02:15:13.333
What about someone like Draco,
who, at least in the books,
1531
02:15:13.333 --> 02:15:15.333
doesn't have a known patronus?
1532
02:15:15.333 --> 02:15:17.416
Well, let's go ahead and
clear my terminal window,
1533
02:15:17.416 --> 02:15:22.625
rerun Python of student.py, and this
time, let's type in Draco for name,
1534
02:15:22.625 --> 02:15:25.750
Slytherin for house,
and Patronus is unknown.
1535
02:15:25.750 --> 02:15:27.583
So I'm just going to
go ahead and hit Enter.
1536
02:15:27.583 --> 02:15:29.666
And now, Expecto Patronum!
1537
02:15:29.666 --> 02:15:32.916
And it just of sizzles instead.
1538
02:15:32.916 --> 02:15:38.208
Well, let me propose, now, that
we remove this patronus code just
1539
02:15:38.208 --> 02:15:41.833
to simplify our world and
focus on some of the other core
1540
02:15:41.833 --> 02:15:43.208
capabilities of classes.
1541
02:15:43.208 --> 02:15:44.958
So at the risk of
disappointing, I'm going
1542
02:15:44.958 --> 02:15:47.916
to get rid of all of these
beautiful emoji and charms,
1543
02:15:47.916 --> 02:15:51.875
and I'm going to go ahead and stop
asking the user now for their patronus.
1544
02:15:51.875 --> 02:15:54.708
And I'm going to stop
passing it into init here.
1545
02:15:54.708 --> 02:15:57.541
And I'm going to stop doing this here.
1546
02:15:57.541 --> 02:16:03.333
And I'm going to instead just go ahead
and restore our use of print student
1547
02:16:03.333 --> 02:16:06.541
here, and I'm going to go ahead
and get rid of patronus down here.
1548
02:16:06.541 --> 02:16:10.541
So just essentially undo all of
the fun charms we just created.
1549
02:16:10.541 --> 02:16:16.291
So we're now back at the point in the
story where we have a student class,
1550
02:16:16.291 --> 02:16:18.041
with only two methods--
1551
02:16:18.041 --> 02:16:19.416
init and str.
1552
02:16:19.416 --> 02:16:21.375
The first of those
takes, of course, self
1553
02:16:21.375 --> 02:16:25.125
as the first argument as it always will,
plus two more now-- name and house,
1554
02:16:25.125 --> 02:16:26.208
no more patronus.
1555
02:16:26.208 --> 02:16:28.041
We're validating name up here.
1556
02:16:28.041 --> 02:16:29.833
We're validating house down here.
1557
02:16:29.833 --> 02:16:32.375
And then we're assigning
name and house, respectively,
1558
02:16:32.375 --> 02:16:35.625
to two instance variables
called name and house also.
1559
02:16:35.625 --> 02:16:38.875
But we use self to get
access to the current object,
1560
02:16:38.875 --> 02:16:40.750
to store those values therein.
1561
02:16:40.750 --> 02:16:44.583
We then still have our str method
here, which takes one argument--
1562
02:16:44.583 --> 02:16:46.208
by default, self, and that's it.
1563
02:16:46.208 --> 02:16:49.750
And that function is going to
be called automatically any time
1564
02:16:49.750 --> 02:16:52.625
you want to convert a
student object to a string,
1565
02:16:52.625 --> 02:16:55.125
just like print might want to do here.
1566
02:16:55.125 --> 02:16:57.791
So let me go ahead and just make
sure I haven't broken anything.
1567
02:16:57.791 --> 02:16:59.750
Let me run Python of student.py.
1568
02:16:59.750 --> 02:17:00.916
I'll type in Harry.
1569
02:17:00.916 --> 02:17:03.041
I'll type in Gryffindor, Enter.
1570
02:17:03.041 --> 02:17:04.416
OK, we're back in business.
1571
02:17:04.416 --> 02:17:08.166
Gone are the charms and patronus,
but at least I'm back to a situation
1572
02:17:08.166 --> 02:17:10.250
where I have names and houses.
1573
02:17:10.250 --> 02:17:14.416
But it turns out, at the
moment, our use of classes
1574
02:17:14.416 --> 02:17:20.083
is not very robust, even though we
have this mechanism, very cleverly,
1575
02:17:20.083 --> 02:17:23.041
if I may, in our init
method of making sure
1576
02:17:23.041 --> 02:17:27.125
that we're validating name and house,
making sure that name is not blank,
1577
02:17:27.125 --> 02:17:32.625
and making sure that house is a valid
house among those four Hogwarts houses.
1578
02:17:32.625 --> 02:17:35.208
It turns out that
classes will still let me
1579
02:17:35.208 --> 02:17:38.541
get at those attributes, those
so-called instance variables,
1580
02:17:38.541 --> 02:17:40.750
using dot notation anyway.
1581
02:17:40.750 --> 02:17:44.500
Let me scroll down then and try
to do this a little adversarially.
1582
02:17:44.500 --> 02:17:48.500
Suppose that online 16 I go
ahead and call get_student,
1583
02:17:48.500 --> 02:17:52.541
which exists as before, and then I
store the return value in a student
1584
02:17:52.541 --> 02:17:54.791
variable-- again, on line 16.
1585
02:17:54.791 --> 02:18:00.291
That will ensure that get_student gets
called, which calls input and input.
1586
02:18:00.291 --> 02:18:02.916
And then it calls the
student constructor,
1587
02:18:02.916 --> 02:18:05.541
which invokes, automatically,
this init method.
1588
02:18:05.541 --> 02:18:08.458
So by way of how we've
laid out my code, we're
1589
02:18:08.458 --> 02:18:11.750
going to ensure that name is not
blank and house is definitely
1590
02:18:11.750 --> 02:18:12.916
one of those four values.
1591
02:18:12.916 --> 02:18:16.541
My error correction-- or
error checking is in place.
1592
02:18:16.541 --> 02:18:20.708
But if I'm a little adversarial,
I can still circumvent it.
1593
02:18:20.708 --> 02:18:25.541
Suppose that-- fine, going to require
me to type in Harry and Gryffindor?
1594
02:18:25.541 --> 02:18:28.083
I'm going to go ahead
and type in student.house
1595
02:18:28.083 --> 02:18:31.541
equals, quote, unquote,
"Number Four, Privet Drive,"
1596
02:18:31.541 --> 02:18:33.333
and you're not going
to be able to stop me.
1597
02:18:33.333 --> 02:18:34.083
Why?
1598
02:18:34.083 --> 02:18:36.708
Well, it turns out,
with classes and objects
1599
02:18:36.708 --> 02:18:40.375
thereof, you and I can still
access those instance variables
1600
02:18:40.375 --> 02:18:42.083
using this familiar dot notation.
1601
02:18:42.083 --> 02:18:45.125
That's how we began the story of
classes-- just setting these attributes
1602
02:18:45.125 --> 02:18:45.875
ourselves.
1603
02:18:45.875 --> 02:18:48.541
But you can also read
these attributes themselves
1604
02:18:48.541 --> 02:18:50.750
and change them later if you want.
1605
02:18:50.750 --> 02:18:55.833
And this will effectively circumvent the
if condition and the other if condition
1606
02:18:55.833 --> 02:19:00.000
in our init method because that
is only called when you first
1607
02:19:00.000 --> 02:19:02.125
create the student object.
1608
02:19:02.125 --> 02:19:04.541
There's nothing stopping
me, at the moment,
1609
02:19:04.541 --> 02:19:08.291
from just changing the
house or the name after.
1610
02:19:08.291 --> 02:19:12.541
So if I now clear my terminal
window and run Python of student.py,
1611
02:19:12.541 --> 02:19:16.208
I'll still type in Harry and
Gryffindor to meet my requirements
1612
02:19:16.208 --> 02:19:17.916
that the house be one of those four.
1613
02:19:17.916 --> 02:19:21.375
But when it's printed, notice,
I've still overridden it.
1614
02:19:21.375 --> 02:19:26.083
So it seems that, while classes do allow
us a little more control over the data
1615
02:19:26.083 --> 02:19:30.250
we're storing, it doesn't
necessarily prevent the user--
1616
02:19:30.250 --> 02:19:33.625
or rather the programmer-- be
it myself or maybe a colleague,
1617
02:19:33.625 --> 02:19:35.791
from still messing things up.
1618
02:19:35.791 --> 02:19:39.083
So here, too, in the spirit
of programming a little more
1619
02:19:39.083 --> 02:19:43.916
defensively, allow me to introduce
another feature of Python as well--
1620
02:19:43.916 --> 02:19:46.208
namely properties.
1621
02:19:46.208 --> 02:19:49.375
So a property is really
just an attribute
1622
02:19:49.375 --> 02:19:52.750
that has even more defense
mechanisms put into place,
1623
02:19:52.750 --> 02:19:58.208
a little more functionality implemented
by you to prevent programmers, like me
1624
02:19:58.208 --> 02:20:01.083
and you, from messing things
up like these attributes.
1625
02:20:01.083 --> 02:20:04.375
So again, a property is going to
be an attribute that you and I just
1626
02:20:04.375 --> 02:20:06.083
have more control over.
1627
02:20:06.083 --> 02:20:06.750
How?
1628
02:20:06.750 --> 02:20:09.791
We just write a little more code,
using some Python conventions.
1629
02:20:09.791 --> 02:20:14.333
And how we're going to do that is going
to use, in just a moment, a feature--
1630
02:20:14.333 --> 02:20:18.083
a keyword known as @property,
which is technically a function.
1631
02:20:18.083 --> 02:20:19.916
Property is a function in Python.
1632
02:20:19.916 --> 02:20:24.791
But we're about to see some new @ syntax
that allows you to decorate functions.
1633
02:20:24.791 --> 02:20:26.125
And this, too, is a term of art.
1634
02:20:26.125 --> 02:20:28.916
In the world of Python,
you can have decorators,
1635
02:20:28.916 --> 02:20:33.166
which are functions that modify
the behavior of other functions,
1636
02:20:33.166 --> 02:20:36.708
if you will, and we'll leave it at that
without going too much into the weeds.
1637
02:20:36.708 --> 02:20:39.291
And we'll see, by, example how
you can use these decorators,
1638
02:20:39.291 --> 02:20:41.541
specifically to define properties.
1639
02:20:41.541 --> 02:20:43.708
So let me go back to VS Code here.
1640
02:20:43.708 --> 02:20:46.791
And let me propose that I do this.
1641
02:20:46.791 --> 02:20:49.375
I'm going to go ahead and create--
1642
02:20:49.375 --> 02:20:54.291
how about a property
called house as follows.
1643
02:20:54.291 --> 02:21:00.833
Inside of my student class, I'm going
to go ahead-- and below my init method
1644
02:21:00.833 --> 02:21:03.958
and below my str method, I'm
going to go ahead and define
1645
02:21:03.958 --> 02:21:09.000
a function called house that takes, as
it always must, one argument at least,
1646
02:21:09.000 --> 02:21:10.041
called self.
1647
02:21:10.041 --> 02:21:15.458
And what I'm going to do
now is return self.house.
1648
02:21:15.458 --> 02:21:17.625
So I'm just going to
define a method called
1649
02:21:17.625 --> 02:21:23.375
house, whose sole purpose in life
is to return the value of house.
1650
02:21:23.375 --> 02:21:27.375
But I'm going to define one other
method, curiously also called house,
1651
02:21:27.375 --> 02:21:31.375
but that's going to take into,
as arguments, two values--
1652
02:21:31.375 --> 02:21:35.625
self as always and also
a value called house.
1653
02:21:35.625 --> 02:21:37.291
And I'm going to now do this.
1654
02:21:37.291 --> 02:21:40.375
1655
02:21:40.375 --> 02:21:43.958
I'm going to do self.house = house.
1656
02:21:43.958 --> 02:21:45.250
Now, what have I done?
1657
02:21:45.250 --> 02:21:47.625
Well, let me just temporarily
add some comments here.
1658
02:21:47.625 --> 02:21:50.166
In a moment, we're going to
start referring to this generally
1659
02:21:50.166 --> 02:21:51.083
as a getter.
1660
02:21:51.083 --> 02:21:54.083
And down here, I'm going to
refer to this as a setter.
1661
02:21:54.083 --> 02:21:56.875
And this is terminology frequently
see in the world of Java.
1662
02:21:56.875 --> 02:21:58.791
Some of you have
programmed in Java before.
1663
02:21:58.791 --> 02:22:01.875
But as the names imply,
a getter is a function
1664
02:22:01.875 --> 02:22:05.333
for a class that gets some attributes.
1665
02:22:05.333 --> 02:22:10.291
A setter is a function in some
class that sets some value.
1666
02:22:10.291 --> 02:22:14.125
And now, even though we're not done, and
there's a bit of a mistake in the code
1667
02:22:14.125 --> 02:22:17.916
I've already written, intuitively,
what we're going to do is this.
1668
02:22:17.916 --> 02:22:21.000
We're trying to prevent
programmers, myself included,
1669
02:22:21.000 --> 02:22:23.541
from circumventing my
error checking that I
1670
02:22:23.541 --> 02:22:25.666
put into place for name and house.
1671
02:22:25.666 --> 02:22:26.958
How can I do that?
1672
02:22:26.958 --> 02:22:29.500
Well, we don't have that many
building blocks in programming.
1673
02:22:29.500 --> 02:22:33.541
We have things like variables for data,
and we have functions for actions.
1674
02:22:33.541 --> 02:22:35.000
Well, why don't we do this?
1675
02:22:35.000 --> 02:22:39.958
Why don't we somehow require that,
in order to access an attribute,
1676
02:22:39.958 --> 02:22:41.375
you go through some function.
1677
02:22:41.375 --> 02:22:44.583
And let's require that, in
order to set some attribute,
1678
02:22:44.583 --> 02:22:46.291
you go through some function.
1679
02:22:46.291 --> 02:22:51.958
And conventionally, those functions are
called a getter function and a setter
1680
02:22:51.958 --> 02:22:52.708
function.
1681
02:22:52.708 --> 02:22:56.541
And why are we using functions or, in
this case, methods inside of a class?
1682
02:22:56.541 --> 02:23:00.000
Well, once you have functions,
those are just actions or verbs
1683
02:23:00.000 --> 02:23:01.583
that you and I can create ourselves.
1684
02:23:01.583 --> 02:23:05.208
We can put any error correction
I want in these functions
1685
02:23:05.208 --> 02:23:08.416
because it's code that's going
to get executed top to bottom.
1686
02:23:08.416 --> 02:23:15.541
So how can I now prevent the user from
setting the house to an invalid value?
1687
02:23:15.541 --> 02:23:20.500
Let me borrow some logic from before
rather than blindly do this-- just set
1688
02:23:20.500 --> 02:23:25.291
self.house equal to the house value
that's passed in-- let's add our error
1689
02:23:25.291 --> 02:23:26.166
checking there.
1690
02:23:26.166 --> 02:23:34.791
So if house is not in the following
list of Gryffindor or Hufflepuff
1691
02:23:34.791 --> 02:23:40.708
or Ravenclaw or
Slytherin, just as before,
1692
02:23:40.708 --> 02:23:44.541
let's go ahead and raise a value error,
just to signify that, uh-uh, something
1693
02:23:44.541 --> 02:23:45.291
has gone wrong.
1694
02:23:45.291 --> 02:23:46.333
I'll be more explicit.
1695
02:23:46.333 --> 02:23:50.541
I'll include a message like,
"invalid house," quote, unquote.
1696
02:23:50.541 --> 02:23:56.166
Otherwise, I'm going to proceed on,
now, line 21 to set self.house to house.
1697
02:23:56.166 --> 02:24:00.416
So I've just copied, if you will,
or retyped my error checking inside
1698
02:24:00.416 --> 02:24:02.333
of this so-called setter function.
1699
02:24:02.333 --> 02:24:03.666
Now, why have I done that?
1700
02:24:03.666 --> 02:24:07.458
Well, to be clear, whenever
the user or the programmer
1701
02:24:07.458 --> 02:24:12.375
writes code like this,
student.house equals, what's
1702
02:24:12.375 --> 02:24:16.166
about to happen magically
is Python will not just
1703
02:24:16.166 --> 02:24:19.916
let the programmer access student
house directly-- that attribute,
1704
02:24:19.916 --> 02:24:21.750
that instance variable, a.k.a.
1705
02:24:21.750 --> 02:24:22.875
self house.
1706
02:24:22.875 --> 02:24:26.833
It's instead going to
magically automatically call
1707
02:24:26.833 --> 02:24:28.791
this setter function for me.
1708
02:24:28.791 --> 02:24:30.708
How does Python know to do that?
1709
02:24:30.708 --> 02:24:35.666
Well, if it's see that, on the
left-hand side, there is self.house,
1710
02:24:35.666 --> 02:24:42.125
where house is the name of the getter or
setter, and then it sees an equal sign,
1711
02:24:42.125 --> 02:24:45.625
indicating assignment, that's just
enough of a visual clue to say,
1712
02:24:45.625 --> 02:24:46.208
wait a minute.
1713
02:24:46.208 --> 02:24:49.250
I'm not going to let you
access that attribute directly.
1714
02:24:49.250 --> 02:24:51.791
I'm going to use the setter instead.
1715
02:24:51.791 --> 02:24:52.291
Why?
1716
02:24:52.291 --> 02:24:54.291
Because the equal sign
means I'm trying to set.
1717
02:24:54.291 --> 02:24:58.250
I'm trying to assign a value from
right to left into that attribute.
1718
02:24:58.250 --> 02:25:00.500
So what Python's is
going to do automatically
1719
02:25:00.500 --> 02:25:02.083
is call this function for me.
1720
02:25:02.083 --> 02:25:06.708
And that's amazing because now I can
execute code-- an algorithm to check,
1721
02:25:06.708 --> 02:25:08.625
do I want to let the user--
1722
02:25:08.625 --> 02:25:12.041
the programmer set that
attribute to that value?
1723
02:25:12.041 --> 02:25:13.916
If not, I'm going to
raise a value error,
1724
02:25:13.916 --> 02:25:15.833
and you're just not going
to be able to do it.
1725
02:25:15.833 --> 02:25:17.500
If so fine.
1726
02:25:17.500 --> 02:25:19.416
I'll go ahead and set it for you.
1727
02:25:19.416 --> 02:25:21.958
But in order to do this, we
need a little more syntax.
1728
02:25:21.958 --> 02:25:25.375
And I'm going to get rid of my comment,
and I'm going to use that decorator.
1729
02:25:25.375 --> 02:25:29.583
I need to tell Python to
treat this method as a getter.
1730
02:25:29.583 --> 02:25:32.083
And then the syntax for the
setter is a little different.
1731
02:25:32.083 --> 02:25:34.500
You now say house.setter.
1732
02:25:34.500 --> 02:25:37.500
I wish one was getter
and the other was setter.
1733
02:25:37.500 --> 02:25:39.458
That's not the way they designed it.
1734
02:25:39.458 --> 02:25:44.291
When you want to define a getter, you
just say @property above the function.
1735
02:25:44.291 --> 02:25:48.833
And you name the function exactly
like you would like the property
1736
02:25:48.833 --> 02:25:51.000
to be called-- quote, unquote, "house."
1737
02:25:51.000 --> 02:25:54.416
Once you do that, you can
now use a new decorator
1738
02:25:54.416 --> 02:25:57.625
that's automatically created
for you called @house,
1739
02:25:57.625 --> 02:25:58.750
because I called it house.
1740
02:25:58.750 --> 02:26:01.416
And then you literally
say, @house.setter.
1741
02:26:01.416 --> 02:26:05.333
And this whole line, on
line 17, is a clue to Python
1742
02:26:05.333 --> 02:26:08.750
that here comes a function,
whose name is identical--
1743
02:26:08.750 --> 02:26:12.541
but notice that it takes two arguments--
both self, so you have access
1744
02:26:12.541 --> 02:26:15.208
to the contents of the object,
and house, which is just
1745
02:26:15.208 --> 02:26:20.041
going to be a str that comes from the
programmer from the human input return
1746
02:26:20.041 --> 02:26:23.958
value so that you can
set that value as well.
1747
02:26:23.958 --> 02:26:27.375
But there's one fix I
need to make now, here.
1748
02:26:27.375 --> 02:26:29.625
Everything else, I think, is still good.
1749
02:26:29.625 --> 02:26:31.250
However watch this.
1750
02:26:31.250 --> 02:26:34.500
I no longer need this error check here.
1751
02:26:34.500 --> 02:26:35.333
Why?
1752
02:26:35.333 --> 02:26:39.375
Because, if I scroll back
down to my code here,
1753
02:26:39.375 --> 02:26:46.250
I claimed a moment ago that code
like this, with student.house equals,
1754
02:26:46.250 --> 02:26:49.666
is going to automatically get
Python to call my setter for me.
1755
02:26:49.666 --> 02:26:50.666
Guess what?
1756
02:26:50.666 --> 02:26:55.875
Even up here, in my init method,
calling self.house equals
1757
02:26:55.875 --> 02:27:00.208
is also going to call my
setter method, which is amazing
1758
02:27:00.208 --> 02:27:06.083
because now I can keep all of my error
checking in one place in the setter,
1759
02:27:06.083 --> 02:27:10.833
and it will now get called either when
I create the object for the first time,
1760
02:27:10.833 --> 02:27:14.541
because of init, or
even if the programmer
1761
02:27:14.541 --> 02:27:19.375
tries to circumvent that init method
and change the value of this attribute,
1762
02:27:19.375 --> 02:27:21.041
my setter will also get called.
1763
02:27:21.041 --> 02:27:26.000
My setter will get called
any time I access .house.
1764
02:27:26.000 --> 02:27:28.458
But there's one fix I need to make.
1765
02:27:28.458 --> 02:27:32.958
Unfortunately, I have collided names.
1766
02:27:32.958 --> 02:27:38.458
Right now, if we go up here, on line
5, this is an instance variable.
1767
02:27:38.458 --> 02:27:42.333
It's a string inside of my self,
inside of the current student object,
1768
02:27:42.333 --> 02:27:43.083
called name.
1769
02:27:43.083 --> 02:27:46.958
And this is another instance
variable called house.
1770
02:27:46.958 --> 02:27:51.291
Unfortunately, if I have an instance
variable called name and house,
1771
02:27:51.291 --> 02:27:54.750
I cannot also have
functions called house.
1772
02:27:54.750 --> 02:27:55.916
They're going to collide.
1773
02:27:55.916 --> 02:27:57.041
You've got to decide.
1774
02:27:57.041 --> 02:27:59.250
Do you want the variable
to be called house?
1775
02:27:59.250 --> 02:28:01.708
Or do you want the function
to be called house?
1776
02:28:01.708 --> 02:28:04.208
Unfortunately, you can't
have both because now Python
1777
02:28:04.208 --> 02:28:06.041
is going to confuse one for the other.
1778
02:28:06.041 --> 02:28:09.291
So the conventional fix
for this is to do this--
1779
02:28:09.291 --> 02:28:15.250
to have the setter not store the
value that's passed in self.house,
1780
02:28:15.250 --> 02:28:19.125
but to use an almost identical name,
but to use a little indicator that
1781
02:28:19.125 --> 02:28:21.208
means you know doing this correctly.
1782
02:28:21.208 --> 02:28:23.666
You typically, by
convention, put an underscore
1783
02:28:23.666 --> 02:28:27.041
in front of the instance
variable's name.
1784
02:28:27.041 --> 02:28:31.208
And when you return it up here,
you similarly put an underscore.
1785
02:28:31.208 --> 02:28:36.541
So now, technically, my instance
variable is called _house,
1786
02:28:36.541 --> 02:28:41.791
but my property, which is a
fancier attribute, if you will,
1787
02:28:41.791 --> 02:28:44.916
is called house alone.
1788
02:28:44.916 --> 02:28:48.666
Huge amount of syntax, I know,
but it's a very powerful feature.
1789
02:28:48.666 --> 02:28:52.708
And again, this is why you can graduate
from dictionaries alone and have
1790
02:28:52.708 --> 02:28:55.500
so much more functionality
at your disposal.
1791
02:28:55.500 --> 02:28:58.916
Let me go ahead and clear my terminal
window and run Python of student.py,
1792
02:28:58.916 --> 02:29:00.458
Enter, name.
1793
02:29:00.458 --> 02:29:02.708
All right, let's go
ahead and type in Harry.
1794
02:29:02.708 --> 02:29:04.625
Let's go ahead and type in Gryffindor.
1795
02:29:04.625 --> 02:29:06.416
Crossing my fingers as always.
1796
02:29:06.416 --> 02:29:09.083
And now, look, "Invalid house."
1797
02:29:09.083 --> 02:29:10.250
This is a good thing.
1798
02:29:10.250 --> 02:29:10.750
Why?
1799
02:29:10.750 --> 02:29:13.958
Because, notice, in
my main function, I'm
1800
02:29:13.958 --> 02:29:19.125
still trying, maliciously, if
you will, to change Harry's house
1801
02:29:19.125 --> 02:29:21.000
to not be one of the four valid ones.
1802
02:29:21.000 --> 02:29:24.833
I'm trying to change it to his childhood
home of Number Four, Privet Drive.
1803
02:29:24.833 --> 02:29:29.208
But because Python knows that, wait
a minute, you're trying to assign--
1804
02:29:29.208 --> 02:29:30.708
that is, set a value--
1805
02:29:30.708 --> 02:29:34.208
and that value, a.k.a.
house, is now defined
1806
02:29:34.208 --> 02:29:38.333
as a property you're going to have to
go through the setter function instead
1807
02:29:38.333 --> 02:29:40.958
to even let you change that value.
1808
02:29:40.958 --> 02:29:44.208
And because I have
this raise ValueError.
1809
02:29:44.208 --> 02:29:46.916
If the house is not as
intended, you're not
1810
02:29:46.916 --> 02:29:49.458
going to be allowed to change
it to an invalid value.
1811
02:29:49.458 --> 02:29:52.458
So I'm protecting the data on the
way in, through the init method,
1812
02:29:52.458 --> 02:29:56.708
and I'm even defending the data
if you try to override it there.
1813
02:29:56.708 --> 02:29:59.208
So I think the only solution
for me, the programmer,
1814
02:29:59.208 --> 02:30:01.250
is, don't try to break my own code.
1815
02:30:01.250 --> 02:30:04.083
Let me remove that line because
it's just not going to work.
1816
02:30:04.083 --> 02:30:07.250
Let me run Python of student.py
and, again, type in Harry;
1817
02:30:07.250 --> 02:30:10.916
type in Gryffindor, Enter, and
Harry's indeed from Gryffindor.
1818
02:30:10.916 --> 02:30:15.708
If I did something incorrect,
like Harry from Number Four,
1819
02:30:15.708 --> 02:30:20.541
Privet Drive, Enter, we're again
going to see the value error
1820
02:30:20.541 --> 02:30:25.083
because my code just doesn't let
that value in via manual input now
1821
02:30:25.083 --> 02:30:28.541
or via that adversarial change.
1822
02:30:28.541 --> 02:30:29.958
All right, that was a lot.
1823
02:30:29.958 --> 02:30:35.208
But any question on properties?
1824
02:30:35.208 --> 02:30:37.125
AUDIENCE: Why we are
using getter then setter?
1825
02:30:37.125 --> 02:30:41.541
It's just for the purpose
so that we can find
1826
02:30:41.541 --> 02:30:43.791
that method, that function in our code.
1827
02:30:43.791 --> 02:30:46.833
DAVID J. MALAN: The reason that I'm
going through the trouble of defining
1828
02:30:46.833 --> 02:30:52.375
this getter or setter is because I want
to make sure that programmers cannot do
1829
02:30:52.375 --> 02:30:53.250
things like this.
1830
02:30:53.250 --> 02:30:55.500
If I'm going through the
trouble of validating
1831
02:30:55.500 --> 02:30:58.708
the attributes for
these student objects,
1832
02:30:58.708 --> 02:31:02.041
I don't want you to be able to go in
there and just change them at will.
1833
02:31:02.041 --> 02:31:04.625
I want to have some
control over that object
1834
02:31:04.625 --> 02:31:07.708
so that you can just trust that it's
going to be correct as designed.
1835
02:31:07.708 --> 02:31:11.041
So using a getter and
setter really just enables
1836
02:31:11.041 --> 02:31:15.416
Python to automatically detect when
you're trying to manually set a value.
1837
02:31:15.416 --> 02:31:18.333
The equal sign and the dot,
as I've highlighted here,
1838
02:31:18.333 --> 02:31:20.666
is enough of a clue to Python
to realize, wait a minute,
1839
02:31:20.666 --> 02:31:21.916
you're trying to set a value.
1840
02:31:21.916 --> 02:31:24.750
Let me see if this class
has a setter defined.
1841
02:31:24.750 --> 02:31:26.875
And if so, I'm going to
call that, and I'm not just
1842
02:31:26.875 --> 02:31:30.125
going to blindly assign the
value from right to left.
1843
02:31:30.125 --> 02:31:32.541
So it's just giving me more control.
1844
02:31:32.541 --> 02:31:35.041
Other questions on properties.
1845
02:31:35.041 --> 02:31:39.458
AUDIENCE: When we use getters,
we just have just one argument.
1846
02:31:39.458 --> 02:31:43.291
And if we use setters, it's
always going to be two arguments?
1847
02:31:43.291 --> 02:31:44.291
Is that normal?
1848
02:31:44.291 --> 02:31:45.291
DAVID J. MALAN: Correct.
1849
02:31:45.291 --> 02:31:47.833
It's always going to
be one argument-- self
1850
02:31:47.833 --> 02:31:52.625
for the getter, two arguments for
the setter-- self and something else.
1851
02:31:52.625 --> 02:31:55.375
And the intuition for that
is, if you're getting a value,
1852
02:31:55.375 --> 02:31:58.875
you don't need to pass anything else
in because you already know the object.
1853
02:31:58.875 --> 02:32:00.291
It's called student in this case.
1854
02:32:00.291 --> 02:32:02.791
So you're just going to get
the value of that property.
1855
02:32:02.791 --> 02:32:05.458
But if you want to set the
property to something else,
1856
02:32:05.458 --> 02:32:07.125
you've got to pass in that argument.
1857
02:32:07.125 --> 02:32:09.625
You've got to pass in the value
to which you want to set it.
1858
02:32:09.625 --> 02:32:11.291
So it's always 0 or 1.
1859
02:32:11.291 --> 02:32:17.541
However, you see it as 1 or 2 because,
again, any function inside of a class,
1860
02:32:17.541 --> 02:32:18.041
a.k.a.
1861
02:32:18.041 --> 02:32:22.291
a method, is going to be automatically
passed self so that you have access
1862
02:32:22.291 --> 02:32:25.208
to that current object in memory.
1863
02:32:25.208 --> 02:32:27.708
How about one other
question on properties?
1864
02:32:27.708 --> 02:32:31.791
AUDIENCE: Why didn't we use the
same underscore house init method?
1865
02:32:31.791 --> 02:32:33.125
DAVID J. MALAN: A good question.
1866
02:32:33.125 --> 02:32:36.750
So even though I'm using the
underscore house here, in my setter,
1867
02:32:36.750 --> 02:32:39.125
and the underscore house
here, in my getter,
1868
02:32:39.125 --> 02:32:41.958
I deliberately did not use it up here.
1869
02:32:41.958 --> 02:32:47.333
The reason for that is that, by
using self.house and this equal sign,
1870
02:32:47.333 --> 02:32:50.375
that's the same pattern that
I want Python to recognize.
1871
02:32:50.375 --> 02:32:53.208
I want Python to
automatically call the setter,
1872
02:32:53.208 --> 02:32:56.791
even when I'm passing in the
house via the init method.
1873
02:32:56.791 --> 02:33:01.458
If I were to change this to do this,
that would circumvent the setter,
1874
02:33:01.458 --> 02:33:05.041
and now there's no error
checking in init whatsoever.
1875
02:33:05.041 --> 02:33:06.333
So it's such a fine line.
1876
02:33:06.333 --> 02:33:10.041
The only thing standing between us and
error checking or no error checking
1877
02:33:10.041 --> 02:33:12.750
is the presence or absence
of this underscore.
1878
02:33:12.750 --> 02:33:14.625
But that's typically the convention.
1879
02:33:14.625 --> 02:33:17.375
By not using the
underscore there, make sure
1880
02:33:17.375 --> 02:33:19.708
that even that assignment
goes through the setter
1881
02:33:19.708 --> 02:33:22.416
so that, honestly, I, don't have
to copy paste the same error
1882
02:33:22.416 --> 02:33:23.583
checking in two places.
1883
02:33:23.583 --> 02:33:25.375
I can put it just in the setter.
1884
02:33:25.375 --> 02:33:28.791
So it's a better design, and that's
why I manually retyped it at first,
1885
02:33:28.791 --> 02:33:31.166
but then I deleted it from init.
1886
02:33:31.166 --> 02:33:34.625
Well, allow me to propose that we
make one other change to this file.
1887
02:33:34.625 --> 02:33:38.708
Might as well go ahead and define
a property for name as well.
1888
02:33:38.708 --> 02:33:41.458
And let me go ahead and do this--
maybe above the house property
1889
02:33:41.458 --> 02:33:45.041
just to keep things in the same
order as I defined them earlier.
1890
02:33:45.041 --> 02:33:47.375
Let me give myself another property.
1891
02:33:47.375 --> 02:33:49.125
This one is going to be called name.
1892
02:33:49.125 --> 02:33:51.583
It's going to take one argument
called self, as always.
1893
02:33:51.583 --> 02:33:55.625
And this one, very similarly, is
just going to return self._name.
1894
02:33:55.625 --> 02:33:57.333
So I'm going to
anticipate that I'm going
1895
02:33:57.333 --> 02:34:01.166
to have to rename name also so that
I don't have that same collision as
1896
02:34:01.166 --> 02:34:01.875
before.
1897
02:34:01.875 --> 02:34:05.541
But now let me go ahead
and define another setter--
1898
02:34:05.541 --> 02:34:06.750
this one for name.
1899
02:34:06.750 --> 02:34:09.416
So the convention is @name.setter.
1900
02:34:09.416 --> 02:34:10.375
Why name?
1901
02:34:10.375 --> 02:34:13.916
Because the property I just
created is called name.
1902
02:34:13.916 --> 02:34:18.833
So the getter and setter work in
conjunction in this way, if you will.
1903
02:34:18.833 --> 02:34:22.291
Let me go down under that name
setter and define another function,
1904
02:34:22.291 --> 02:34:23.375
also called name.
1905
02:34:23.375 --> 02:34:25.791
But the key thing here is
that it's not identical.
1906
02:34:25.791 --> 02:34:30.000
It's not the exact same function name
and the exact same number of arguments.
1907
02:34:30.000 --> 02:34:32.500
The setter, again,
takes a second argument.
1908
02:34:32.500 --> 02:34:34.250
And I can call it
anything I want, but I'm
1909
02:34:34.250 --> 02:34:36.750
going to call it name because
that's what's being passed in.
1910
02:34:36.750 --> 02:34:39.041
And I'm going to put
my error checking here.
1911
02:34:39.041 --> 02:34:43.541
If not name, just like we used to do,
let's go ahead and raise a value error,
1912
02:34:43.541 --> 02:34:48.750
and let's put an explanatory message
like "Missing name," quote, unquote.
1913
02:34:48.750 --> 02:34:55.250
Otherwise, let's go ahead and
update self._name to equal name.
1914
02:34:55.250 --> 02:34:58.500
And I don't have to change
init except to get rid
1915
02:34:58.500 --> 02:35:02.333
of this duplicate error
checking now because, again,
1916
02:35:02.333 --> 02:35:07.166
if I use self.name equals here
and self.house equals here
1917
02:35:07.166 --> 02:35:09.916
with no underscore, both
of those assignments
1918
02:35:09.916 --> 02:35:13.375
are going to go through my
two setter functions now.
1919
02:35:13.375 --> 02:35:15.416
Before we run this, let
me go ahead and remove
1920
02:35:15.416 --> 02:35:18.791
this adversarial code, which we know
won't work because we're catching it.
1921
02:35:18.791 --> 02:35:22.375
Let me go back down to my terminal
window and run Python of student.py,
1922
02:35:22.375 --> 02:35:22.916
Enter.
1923
02:35:22.916 --> 02:35:23.958
Let's type in Harry.
1924
02:35:23.958 --> 02:35:25.125
Let's type in Gryffindor.
1925
02:35:25.125 --> 02:35:26.708
And that seems to work.
1926
02:35:26.708 --> 02:35:29.458
Let's try though, again,
to run Python of student.py
1927
02:35:29.458 --> 02:35:32.708
with Harry from Number
Four, Privet Drive.
1928
02:35:32.708 --> 02:35:34.416
This will not work.
1929
02:35:34.416 --> 02:35:37.291
A value error with invalid
house, because that's not
1930
02:35:37.291 --> 02:35:38.791
one of the four Hogwarts houses.
1931
02:35:38.791 --> 02:35:41.041
And now, for good measure,
let's run it one more time.
1932
02:35:41.041 --> 02:35:42.583
And let's not even give it a name.
1933
02:35:42.583 --> 02:35:44.375
Let's just hit Enter when prompted.
1934
02:35:44.375 --> 02:35:45.875
I can type anything for the house.
1935
02:35:45.875 --> 02:35:47.958
I'll go ahead and still
give it Gryffindor, Enter.
1936
02:35:47.958 --> 02:35:52.958
And now we get another value error,
but this one is for missing name.
1937
02:35:52.958 --> 02:35:56.500
So we seem, now, to have all the
more of a defense mechanism in place
1938
02:35:56.500 --> 02:35:58.833
to ensure that name is as we expect.
1939
02:35:58.833 --> 02:36:01.000
It's got to have some
value that's not blank.
1940
02:36:01.000 --> 02:36:02.500
And house is as we expect.
1941
02:36:02.500 --> 02:36:05.000
It's got to have one
of those four values.
1942
02:36:05.000 --> 02:36:08.000
But at the risk of
bursting everyone's bubble
1943
02:36:08.000 --> 02:36:11.958
and making you wonder, why did
we just go through all of that,
1944
02:36:11.958 --> 02:36:18.875
unfortunately Python really focuses
on conventions, not hard constraints.
1945
02:36:18.875 --> 02:36:20.291
And by that, I mean this.
1946
02:36:20.291 --> 02:36:25.208
If I go back into my main function
after I've gotten a student on line 30
1947
02:36:25.208 --> 02:36:30.375
and I try to adversarially do something
like this-- student.house equals
1948
02:36:30.375 --> 02:36:33.625
"Number Four, Privet
Drive," we know this
1949
02:36:33.625 --> 02:36:37.416
won't work because my setter for
house is going to catch this.
1950
02:36:37.416 --> 02:36:38.333
Watch again.
1951
02:36:38.333 --> 02:36:40.833
Python of student.py.
1952
02:36:40.833 --> 02:36:42.041
Let's type in Harry.
1953
02:36:42.041 --> 02:36:44.541
Let's type in Gryffindor,
which will at least pass
1954
02:36:44.541 --> 02:36:46.958
our check that's induced by init.
1955
02:36:46.958 --> 02:36:52.041
But line 31 is going to trigger
the same setter to be called,
1956
02:36:52.041 --> 02:36:55.583
and we're going to raise a value
error saying "Invalid house."
1957
02:36:55.583 --> 02:36:58.250
Unfortunately, and if
some of you are already
1958
02:36:58.250 --> 02:37:02.375
thinking a little adversarially,
tragically, look what you can do.
1959
02:37:02.375 --> 02:37:05.500
You can change .house to be ._house.
1960
02:37:05.500 --> 02:37:06.041
Why?
1961
02:37:06.041 --> 02:37:10.416
Well, the instance variable
is now called _house.
1962
02:37:10.416 --> 02:37:13.125
The property is called
house, no underscore.
1963
02:37:13.125 --> 02:37:19.041
But the underlying attribute implemented
as an instance variable is still called
1964
02:37:19.041 --> 02:37:19.791
_house.
1965
02:37:19.791 --> 02:37:23.541
And tragically, Python of student.py.
1966
02:37:23.541 --> 02:37:25.000
Let's type in Harry.
1967
02:37:25.000 --> 02:37:27.375
Let's type in Gryffindor,
which is correct.
1968
02:37:27.375 --> 02:37:28.958
But watch what happens now.
1969
02:37:28.958 --> 02:37:30.333
Oh, my God.
1970
02:37:30.333 --> 02:37:32.000
We slip through.
1971
02:37:32.000 --> 02:37:35.708
So what was the point of
all of this emphasis from me
1972
02:37:35.708 --> 02:37:39.541
on doing things the "right way," the
Python quick way by having this getter
1973
02:37:39.541 --> 02:37:40.291
and setter?
1974
02:37:40.291 --> 02:37:43.208
Well, unlike languages
like Java, that just
1975
02:37:43.208 --> 02:37:46.208
prevent you from doing
things like this, Python
1976
02:37:46.208 --> 02:37:49.041
itself allows you to specify that
certain instance variables can
1977
02:37:49.041 --> 02:37:52.666
be public and accessible to
anyone's code, or protected,
1978
02:37:52.666 --> 02:37:56.791
or private, which means that no one else
should be able to change these values.
1979
02:37:56.791 --> 02:38:00.125
In the world of Python,
it's just the honor system.
1980
02:38:00.125 --> 02:38:02.416
It's not baked into the
language itself that there's
1981
02:38:02.416 --> 02:38:05.500
a notion of visibility, public or
private or even somewhere in between
1982
02:38:05.500 --> 02:38:06.250
protected.
1983
02:38:06.250 --> 02:38:08.208
Instead, you're on the honor system.
1984
02:38:08.208 --> 02:38:11.666
And the convention
generally is, if an instance
1985
02:38:11.666 --> 02:38:15.750
variable starts with an
underscore, please don't touch it.
1986
02:38:15.750 --> 02:38:16.708
Just don't.
1987
02:38:16.708 --> 02:38:19.750
That's on you if you touch
that variable and break things.
1988
02:38:19.750 --> 02:38:22.000
The underscore is meant
to signify a convention
1989
02:38:22.000 --> 02:38:24.583
that this is meant to be
"private," but it really just
1990
02:38:24.583 --> 02:38:25.875
means, please don't touch this.
1991
02:38:25.875 --> 02:38:27.958
Sometimes, if there's two
underscores, which you can use,
1992
02:38:27.958 --> 02:38:30.541
too, that's an even greater
effort by programmers to say,
1993
02:38:30.541 --> 02:38:32.000
really don't touch this.
1994
02:38:32.000 --> 02:38:35.166
But technically speaking, there's
nothing stopping you or me
1995
02:38:35.166 --> 02:38:38.875
from circumventing all of these
mechanisms, these properties,
1996
02:38:38.875 --> 02:38:40.125
these getters and setters.
1997
02:38:40.125 --> 02:38:42.250
We're ultimately just
on the honor system
1998
02:38:42.250 --> 02:38:46.291
not to do so when we see instance
variables prefixed with one,
1999
02:38:46.291 --> 02:38:48.041
or perhaps even two underscores.
2000
02:38:48.041 --> 02:38:50.583
All right, so this is
a lot all at once--
2001
02:38:50.583 --> 02:38:52.875
this Introduction to
object-oriented programming.
2002
02:38:52.875 --> 02:38:56.166
But it might come as quite a
surprise that, even though we
2003
02:38:56.166 --> 02:39:00.458
might have identified OOP
by name in weeks past,
2004
02:39:00.458 --> 02:39:05.458
we've all been using classes and
objects for weeks now in this class.
2005
02:39:05.458 --> 02:39:08.791
In fact, if you think back on one of the
very first things we did in this class,
2006
02:39:08.791 --> 02:39:12.083
we used integers and just
got integers from the user.
2007
02:39:12.083 --> 02:39:13.791
But if you haven't already--
2008
02:39:13.791 --> 02:39:17.500
if you go and dig into the
documentation for integers,
2009
02:39:17.500 --> 02:39:19.916
which, again, lives
at this URL here, you
2010
02:39:19.916 --> 02:39:26.166
would actually find that int itself
is and has been for weeks a class.
2011
02:39:26.166 --> 02:39:30.541
And in fact, this is the signature
of the constructor call for an int,
2012
02:39:30.541 --> 02:39:35.125
whereby you pass in x, like a number,
quote, unquote, "50" or, quote,
2013
02:39:35.125 --> 02:39:38.458
unquote, something else-- you
pass in optionally the base--
2014
02:39:38.458 --> 02:39:41.750
10 for decimal, 2 for
binary or anything else.
2015
02:39:41.750 --> 02:39:46.208
And that int function will actually
return to you, all this time,
2016
02:39:46.208 --> 02:39:49.625
an object of type int.
2017
02:39:49.625 --> 02:39:52.083
That is to say int is a class.
2018
02:39:52.083 --> 02:39:55.250
It is a template, a blueprint
for creating integers in memory.
2019
02:39:55.250 --> 02:39:59.125
And any time you and I have converted
a string, for, instance to an int,
2020
02:39:59.125 --> 02:40:03.958
you and I have been creating an
object of type int that was calling,
2021
02:40:03.958 --> 02:40:07.875
apparently, the underscore underscore,
init, underscore underscore method,
2022
02:40:07.875 --> 02:40:09.666
that someone else--
the authors of Python--
2023
02:40:09.666 --> 02:40:12.500
wrote to give us back
that proper integer.
2024
02:40:12.500 --> 02:40:16.541
Besides that, if you can believe
it, strs, strings in Python
2025
02:40:16.541 --> 02:40:20.000
have been classes since the
first week of this class as well.
2026
02:40:20.000 --> 02:40:22.000
If you look up the
documentation for a str,
2027
02:40:22.000 --> 02:40:26.541
which lives at a similar URL there, you
will find that, when you instantiate--
2028
02:40:26.541 --> 02:40:28.083
that is, create a str--
2029
02:40:28.083 --> 02:40:32.166
it takes, optionally, a
parameter called object here,
2030
02:40:32.166 --> 02:40:34.666
the default value of which
is just, quote, unquote,
2031
02:40:34.666 --> 02:40:38.000
which allows you to create, in effect,
an empty string, a blank string,
2032
02:40:38.000 --> 02:40:38.500
if you will.
2033
02:40:38.500 --> 02:40:42.541
But any time you and I have created
strs or even used explicitly
2034
02:40:42.541 --> 02:40:47.333
the str function, you are getting
back an object of type str.
2035
02:40:47.333 --> 02:40:52.375
Any time you and I have forced a string
to lowercase per the documentation,
2036
02:40:52.375 --> 02:40:58.041
using syntax like this, you and I
have been taking an object of type str
2037
02:40:58.041 --> 02:41:03.125
and forcing it all to lowercase
by calling a method called lower,
2038
02:41:03.125 --> 02:41:07.958
a method that the authors of
Python built into the str class,
2039
02:41:07.958 --> 02:41:11.041
but it's been there from the get-go,
so this notion of methods is not
2040
02:41:11.041 --> 02:41:11.833
even new today.
2041
02:41:11.833 --> 02:41:13.750
You wouldn't have been
doing it for this long.
2042
02:41:13.750 --> 02:41:17.416
If you've ever called strip to remove
the leading and the trailing whitespace
2043
02:41:17.416 --> 02:41:22.166
from a string in Python, you are calling
another method that came with Python--
2044
02:41:22.166 --> 02:41:23.750
written by the authors of Python.
2045
02:41:23.750 --> 02:41:26.000
And even though we didn't
call it a class at the time,
2046
02:41:26.000 --> 02:41:29.708
a str, all this time, has been a class.
2047
02:41:29.708 --> 02:41:33.541
And instances of strings
are, themselves, objects.
2048
02:41:33.541 --> 02:41:36.291
And those objects come
therefore with these functions
2049
02:41:36.291 --> 02:41:39.458
built in-- a.k.a. methods that
allow us to do things like force
2050
02:41:39.458 --> 02:41:42.791
to lowercase and strip whitespace
from the beginning and end.
2051
02:41:42.791 --> 02:41:43.958
Let's do another.
2052
02:41:43.958 --> 02:41:49.541
list-- any time you've created a
list, either syntactically with square
2053
02:41:49.541 --> 02:41:53.541
brackets or literally with L-I-S-T,
open parentheses, closed parentheses,
2054
02:41:53.541 --> 02:41:56.375
which is also possible, you
have been using a class.
2055
02:41:56.375 --> 02:41:59.833
If you go to the documentation
for list, at this similar URL
2056
02:41:59.833 --> 02:42:04.083
here, or more specifically, the
tutorial on lists here in Python,
2057
02:42:04.083 --> 02:42:07.208
you will see that a
list is and has been,
2058
02:42:07.208 --> 02:42:10.958
since the early weeks of
this class, a class itself.
2059
02:42:10.958 --> 02:42:16.166
And that list class takes, as
part of its initialization,
2060
02:42:16.166 --> 02:42:21.000
an optional iterable, something that
can be iterated over-- like 1, 2, 3,
2061
02:42:21.000 --> 02:42:24.125
or some list of values,
and you can then get back
2062
02:42:24.125 --> 02:42:27.416
a list containing those
same iterable values.
2063
02:42:27.416 --> 02:42:30.666
If you've ever appended something
to a list in this class,
2064
02:42:30.666 --> 02:42:32.583
as I have myself in
the past, you've been
2065
02:42:32.583 --> 02:42:37.416
using a method called append that comes
with the list class that, per the x
2066
02:42:37.416 --> 02:42:40.541
here, takes an argument that
allows you to append something
2067
02:42:40.541 --> 02:42:42.833
to the current list, a.k.a.
2068
02:42:42.833 --> 02:42:45.125
Self in the context of that method.
2069
02:42:45.125 --> 02:42:46.416
We can do this all day long.
2070
02:42:46.416 --> 02:42:49.625
If you've used a dictionary
or a dict in Python--
2071
02:42:49.625 --> 02:42:53.125
I've actually, all this time,
been calling them dict objects,
2072
02:42:53.125 --> 02:42:54.500
and that's for a reason.
2073
02:42:54.500 --> 02:42:57.250
dict itself is a class
in Python, if you pull up
2074
02:42:57.250 --> 02:42:59.166
its official documentation here.
2075
02:42:59.166 --> 02:43:03.375
And you'll see that it is defined,
indeed, as itself a class.
2076
02:43:03.375 --> 02:43:05.333
And that class comes
with methods as well.
2077
02:43:05.333 --> 02:43:07.291
And so any time we've
manipulated dictionaries,
2078
02:43:07.291 --> 02:43:11.916
we've been underneath the hood,
using all of those same methods.
2079
02:43:11.916 --> 02:43:14.208
And in fact, we can see this
if we're really curious.
2080
02:43:14.208 --> 02:43:17.375
Let me go back over here to VS Code.
2081
02:43:17.375 --> 02:43:20.250
And let me go ahead and create a
new file that, very simply, does
2082
02:43:20.250 --> 02:43:22.500
something play around with data types.
2083
02:43:22.500 --> 02:43:24.833
And let me go ahead and create
a new file, for instance,
2084
02:43:24.833 --> 02:43:30.666
called, say, type.py, just so that I
can poke around inside of some values.
2085
02:43:30.666 --> 02:43:34.333
And in type.py, I'm just
going to go ahead and do this.
2086
02:43:34.333 --> 02:43:38.375
I'm going to print out whatever
the type is of, say, the number 50.
2087
02:43:38.375 --> 02:43:41.500
And This is a function you've not
necessarily seen me use already,
2088
02:43:41.500 --> 02:43:44.500
and it's not one you would
frequently use in your own code.
2089
02:43:44.500 --> 02:43:46.958
There are other ways to
detect, if you need to,
2090
02:43:46.958 --> 02:43:48.625
what the type is of a variable.
2091
02:43:48.625 --> 02:43:53.791
But in this case, type of 50 is just
going to tell me and then print out
2092
02:43:53.791 --> 02:43:55.833
what the data type is of that value.
2093
02:43:55.833 --> 02:43:58.416
Now, hopefully, all of us
could guess that 50 is indeed
2094
02:43:58.416 --> 02:43:59.458
going to be an integer--
2095
02:43:59.458 --> 02:44:01.666
that is, an int, but we
can see it in this way.
2096
02:44:01.666 --> 02:44:04.541
And this, too, is what's powerful
about knowing a bit of programming.
2097
02:44:04.541 --> 02:44:06.416
If you want to know the
answer to a question,
2098
02:44:06.416 --> 02:44:08.208
just try it out, like I am here.
2099
02:44:08.208 --> 02:44:11.125
So let me go ahead and run
Python of type.py, Enter.
2100
02:44:11.125 --> 02:44:12.208
And there it is.
2101
02:44:12.208 --> 02:44:17.458
When you print out the type of the
number 50, you'll see on the screen,
2102
02:44:17.458 --> 02:44:20.625
in this cryptic syntax, class 'int.'
2103
02:44:20.625 --> 02:44:23.541
This is not something that you
probably want to show to the user.
2104
02:44:23.541 --> 02:44:26.625
But if you yourself just want to
poke around and see what's going on
2105
02:44:26.625 --> 02:44:29.166
or maybe use that
information somehow, it's
2106
02:44:29.166 --> 02:44:32.833
certainly at your disposal to
use this type function for that.
2107
02:44:32.833 --> 02:44:34.583
Let's change it around a little bit.
2108
02:44:34.583 --> 02:44:38.375
Instead of passing as the
argument to type 50, as an int,
2109
02:44:38.375 --> 02:44:41.291
let's type something also
familiar, like "hello, world,"
2110
02:44:41.291 --> 02:44:43.166
in double or single quotes.
2111
02:44:43.166 --> 02:44:45.541
Let me go back to my terminal
window, clear the screen,
2112
02:44:45.541 --> 02:44:47.208
and run Python of type.py again.
2113
02:44:47.208 --> 02:44:49.333
And now, voila, there it is.
2114
02:44:49.333 --> 02:44:52.750
All this time, a str is also a class.
2115
02:44:52.750 --> 02:44:54.333
We can do this a few more times, For.
2116
02:44:54.333 --> 02:44:54.833
Instance.
2117
02:44:54.833 --> 02:44:58.875
Let's go ahead and change "hello,
world" to just an empty list--
2118
02:44:58.875 --> 02:45:01.291
open square bracket,
closed square bracket.
2119
02:45:01.291 --> 02:45:03.375
And this is starting to
look a little cryptic,
2120
02:45:03.375 --> 02:45:04.791
but, again, notice what I'm doing.
2121
02:45:04.791 --> 02:45:06.708
In square brackets is an empty list.
2122
02:45:06.708 --> 02:45:08.083
We've done that before.
2123
02:45:08.083 --> 02:45:11.583
That is the sole argument
to this new type function.
2124
02:45:11.583 --> 02:45:14.666
And that's just being
passed to the print function
2125
02:45:14.666 --> 02:45:18.458
so that the return value of
type is the argument to print.
2126
02:45:18.458 --> 02:45:22.541
So if I now run this code,
Python of type.py, there it is.
2127
02:45:22.541 --> 02:45:24.458
A list is a class, too.
2128
02:45:24.458 --> 02:45:28.000
You might recall that I said that
you can also create an empty list
2129
02:45:28.000 --> 02:45:31.541
by literally doing list ().
2130
02:45:31.541 --> 02:45:34.375
This is a bit of an
inconsistency, as we can now
2131
02:45:34.375 --> 02:45:39.958
identify that int and str and now list--
they're technically all lowercase.
2132
02:45:39.958 --> 02:45:44.708
And I went to great lengths of creating
my student class to have that capital
2133
02:45:44.708 --> 02:45:46.583
S. That's a convention.
2134
02:45:46.583 --> 02:45:51.250
Because int and stir and list
and others come with Python,
2135
02:45:51.250 --> 02:45:55.416
they decided to make their built-in data
types-- even though they're classes--
2136
02:45:55.416 --> 02:45:56.375
all lowercase.
2137
02:45:56.375 --> 02:45:59.750
But the convention, the recommendation
in the Python community when creating
2138
02:45:59.750 --> 02:46:03.541
your classes is to capitalize
the first letter, as I did,
2139
02:46:03.541 --> 02:46:10.541
in something like Student, capital S.
But list () is identical to really just
2140
02:46:10.541 --> 02:46:12.041
two empty square brackets.
2141
02:46:12.041 --> 02:46:16.416
If I clear my screen and run type.py
again, you see the exact same thing.
2142
02:46:16.416 --> 02:46:17.750
The class is called list.
2143
02:46:17.750 --> 02:46:19.041
Let's do one more.
2144
02:46:19.041 --> 02:46:23.041
Let me change the list to be not
square brackets but curly braces.
2145
02:46:23.041 --> 02:46:24.125
We've done this before.
2146
02:46:24.125 --> 02:46:27.916
Any time I've done two curly braces with
nothing in between, this, of course,
2147
02:46:27.916 --> 02:46:31.458
is an empty dictionary, or
a dict object in Python.
2148
02:46:31.458 --> 02:46:32.750
Well, we can see that now.
2149
02:46:32.750 --> 02:46:35.500
Let me clear my screen, run
Python of type.py, Enter,
2150
02:46:35.500 --> 02:46:37.666
and there it is-- class 'dict."
2151
02:46:37.666 --> 02:46:39.458
It's been there this whole time.
2152
02:46:39.458 --> 02:46:42.333
We just didn't call it
a class until today.
2153
02:46:42.333 --> 02:46:44.625
I can similarly do this one explicitly.
2154
02:46:44.625 --> 02:46:48.500
Instead of two curly braces, let's
write out dict with two parentheses.
2155
02:46:48.500 --> 02:46:51.541
Now we have a lot of parentheses
again, like with list.
2156
02:46:51.541 --> 02:46:55.250
But this is just making even more
clear that the type of a dict object
2157
02:46:55.250 --> 02:46:58.333
is indeed the class, dict, itself.
2158
02:46:58.333 --> 02:47:02.375
So this is to say that, as new as
a lot of today's idea and syntax,
2159
02:47:02.375 --> 02:47:05.708
might be you've actually been using
it, perhaps unbeknownst to you,
2160
02:47:05.708 --> 02:47:06.750
for weeks now.
2161
02:47:06.750 --> 02:47:09.416
We now just have
terminology to describe what
2162
02:47:09.416 --> 02:47:10.958
it is we've been doing all this time.
2163
02:47:10.958 --> 02:47:14.166
And you now have the
expressiveness, with some practice,
2164
02:47:14.166 --> 02:47:18.791
to create your own classes, inside of
which are your own instance variables,
2165
02:47:18.791 --> 02:47:23.708
perhaps wrapped with those properties
and your own instance methods.
2166
02:47:23.708 --> 02:47:27.125
But it turns out there's other
types of methods in the world.
2167
02:47:27.125 --> 02:47:29.375
Thus far, I've been
deliberate in calling
2168
02:47:29.375 --> 02:47:34.375
all of our variables instance variables
and all of our methods instance
2169
02:47:34.375 --> 02:47:35.125
methods.
2170
02:47:35.125 --> 02:47:40.333
It turns out there's other types
of variables and methods out there,
2171
02:47:40.333 --> 02:47:42.875
and one of those is
called class methods.
2172
02:47:42.875 --> 02:47:48.333
It turns out that sometimes it's
not really necessary or sensible
2173
02:47:48.333 --> 02:47:52.333
to associate a function
with objects of a class,
2174
02:47:52.333 --> 02:47:55.000
but rather with the class itself.
2175
02:47:55.000 --> 02:47:59.833
An instance, or an object of a class,
is a very specific incarnation thereof.
2176
02:47:59.833 --> 02:48:03.333
Again, on that neighborhood that has
a lot of identical looking buildings,
2177
02:48:03.333 --> 02:48:06.458
but they're all a little bit different
because of different paint and such,
2178
02:48:06.458 --> 02:48:09.666
sometimes you might have
functionality related
2179
02:48:09.666 --> 02:48:13.541
to each of those houses that
isn't distinct or unique for any
2180
02:48:13.541 --> 02:48:14.333
of the houses.
2181
02:48:14.333 --> 02:48:17.375
It's functionality that's
going to be exactly the same no
2182
02:48:17.375 --> 02:48:19.208
matter the house in question.
2183
02:48:19.208 --> 02:48:21.541
Same in the world of
object-oriented programming.
2184
02:48:21.541 --> 02:48:24.375
Sometimes you want some
functionality, some action
2185
02:48:24.375 --> 02:48:29.500
to be associated with the class itself,
no matter what the specific object's
2186
02:48:29.500 --> 02:48:32.333
own values or instance variables are.
2187
02:48:32.333 --> 02:48:36.125
And for that, we have a
keyword called @classmethod.
2188
02:48:36.125 --> 02:48:38.458
This is another
decorator-- really, another
2189
02:48:38.458 --> 02:48:43.416
function-- that you can use to
specify that this method is not,
2190
02:48:43.416 --> 02:48:46.208
by default, implicitly
an instance method that
2191
02:48:46.208 --> 02:48:48.791
has access to self, the object itself.
2192
02:48:48.791 --> 02:48:51.750
This is a class method that's
not going to have access to self,
2193
02:48:51.750 --> 02:48:54.166
but it does know what class it's inside.
2194
02:48:54.166 --> 02:48:55.625
So what do I mean by this?
2195
02:48:55.625 --> 02:48:57.416
Well, let me go back to VS Code here.
2196
02:48:57.416 --> 02:49:01.291
And let me propose that we create
a new file this time implementing
2197
02:49:01.291 --> 02:49:04.958
the notion of a-- the sorting hat,
from the world of Harry Potter as well,
2198
02:49:04.958 --> 02:49:06.041
to stay on theme.
2199
02:49:06.041 --> 02:49:08.666
I'm going to go ahead
and run code of hat.py.
2200
02:49:08.666 --> 02:49:12.041
And in hat.py, let's implement
the notion of the sorting hat.
2201
02:49:12.041 --> 02:49:14.583
If unfamiliar in the
books and in the films
2202
02:49:14.583 --> 02:49:17.333
there is literally a
pointy hat that, when
2203
02:49:17.333 --> 02:49:20.916
a student put it's on their head,
that sorting hat, so to speak,
2204
02:49:20.916 --> 02:49:23.708
decides what house the
student is in-- whether it's
2205
02:49:23.708 --> 02:49:25.416
Gryffindor or something else.
2206
02:49:25.416 --> 02:49:29.500
So let's implement, in code, this
notion of a sorting hat such that,
2207
02:49:29.500 --> 02:49:33.250
when we pass to the sorting hat the
name of a student, like, quote, unquote,
2208
02:49:33.250 --> 02:49:36.083
"Harry" this sorting
hat, implemented in code,
2209
02:49:36.083 --> 02:49:40.041
will tell us what house
that student should be in.
2210
02:49:40.041 --> 02:49:41.750
Well, let's go ahead and do this.
2211
02:49:41.750 --> 02:49:46.708
In hat.py, first, let's go ahead
and define a class called hat,
2212
02:49:46.708 --> 02:49:49.333
and then let's get back
to implementing it itself.
2213
02:49:49.333 --> 02:49:52.041
And I find this to be a helpful
technique, not just with teaching
2214
02:49:52.041 --> 02:49:53.375
but when writing code.
2215
02:49:53.375 --> 02:49:55.291
I know I want a hat class.
2216
02:49:55.291 --> 02:49:57.541
I don't necessarily know
what I want it to do yet,
2217
02:49:57.541 --> 02:50:00.083
so I'm going to create this
placeholder, dot, dot, dot,
2218
02:50:00.083 --> 02:50:01.791
so I'll come back to that.
2219
02:50:01.791 --> 02:50:05.000
Let's now try to use this
class as though it existed.
2220
02:50:05.000 --> 02:50:08.250
And from there, I perhaps
can realize exactly what
2221
02:50:08.250 --> 02:50:11.625
functionality that class needs
to have to support my use case.
2222
02:50:11.625 --> 02:50:14.708
Let me go ahead and create a
variable called hat in all lowercase
2223
02:50:14.708 --> 02:50:17.500
and instantiate a hat object.
2224
02:50:17.500 --> 02:50:21.041
So no matter what the hat
class ends up looking like,
2225
02:50:21.041 --> 02:50:24.125
this is the common
syntax for instantiating
2226
02:50:24.125 --> 02:50:25.916
an object of a certain class.
2227
02:50:25.916 --> 02:50:28.958
In the past, we saw
student, all lowercase,
2228
02:50:28.958 --> 02:50:32.708
equals capital Student, open
parenthesis, close parentheses,
2229
02:50:32.708 --> 02:50:35.375
and then eventually, we added
in things like name and house.
2230
02:50:35.375 --> 02:50:39.375
For now, let's assume that the hat
is much simpler than a student,
2231
02:50:39.375 --> 02:50:41.500
and it only has sorting capabilities.
2232
02:50:41.500 --> 02:50:44.791
So I'm not going to even pass
any arguments there, too.
2233
02:50:44.791 --> 02:50:50.708
Let me assume that the sorting hat has
one function-- one method inside of it
2234
02:50:50.708 --> 02:50:51.583
called, sort.
2235
02:50:51.583 --> 02:50:59.666
And so if I do hat.sort ("Harry"), let's
propose that that prints out what house
2236
02:50:59.666 --> 02:51:01.458
that student should be in.
2237
02:51:01.458 --> 02:51:02.541
So that's it.
2238
02:51:02.541 --> 02:51:04.541
I'm going to encapsulate--
2239
02:51:04.541 --> 02:51:06.916
that is tuck away
inside of a hat class--
2240
02:51:06.916 --> 02:51:11.250
all of this requisite functionality, and
I'm going to print out onto the screen
2241
02:51:11.250 --> 02:51:12.458
what hat--
2242
02:51:12.458 --> 02:51:15.208
what house Harry belongs in.
2243
02:51:15.208 --> 02:51:20.166
Now I think I need to get into the weeds
of actually initializing this class.
2244
02:51:20.166 --> 02:51:22.041
Well, let me go ahead and do this.
2245
02:51:22.041 --> 02:51:24.791
If I don't care to parameterize hat--
2246
02:51:24.791 --> 02:51:29.083
I just want to, for
instance sort values,
2247
02:51:29.083 --> 02:51:31.916
let's go ahead and define
this function, sort, first.
2248
02:51:31.916 --> 02:51:35.291
So let's define sort, as
taking a first argument, self,
2249
02:51:35.291 --> 02:51:39.125
which is always going to be the case
when defining an instance method as
2250
02:51:39.125 --> 02:51:39.750
before.
2251
02:51:39.750 --> 02:51:43.750
But the sort method clearly takes one
argument from the programmer, me--
2252
02:51:43.750 --> 02:51:45.583
namely the student's name.
2253
02:51:45.583 --> 02:51:47.708
And again, we've seen
this dichotomy before.
2254
02:51:47.708 --> 02:51:52.083
Even though I'm trying to pass in one
argument, when I define the method,
2255
02:51:52.083 --> 02:51:55.166
it's got to take that many
arguments, plus one more--
2256
02:51:55.166 --> 02:51:58.208
self which is always going to be
automatically passed in by Python
2257
02:51:58.208 --> 02:51:59.583
first.
2258
02:51:59.583 --> 02:52:01.291
What do I want to do?
2259
02:52:01.291 --> 02:52:03.291
Well, let's go ahead and
do something like this.
2260
02:52:03.291 --> 02:52:06.166
2261
02:52:06.166 --> 02:52:12.375
Print, this name-- how
about "is in," "some house."
2262
02:52:12.375 --> 02:52:14.291
I'm going to, again, use
some placeholder code
2263
02:52:14.291 --> 02:52:18.000
for myself because I'm not quite sure
how to finish implementing this sorting
2264
02:52:18.000 --> 02:52:18.583
hat.
2265
02:52:18.583 --> 02:52:21.708
But I think that's enough to just
test where my code is at now.
2266
02:52:21.708 --> 02:52:25.625
Let me go ahead and run Python
of hat.py and hit Enter.
2267
02:52:25.625 --> 02:52:28.250
And it looks like, indeed,
Harry is in some house.
2268
02:52:28.250 --> 02:52:31.958
We're not done yet because it's
clearly not doing anything interesting,
2269
02:52:31.958 --> 02:52:35.791
but it at least is running
correctly with no errors.
2270
02:52:35.791 --> 02:52:40.125
Well, let's go ahead
now and decide where--
2271
02:52:40.125 --> 02:52:43.541
what house Harry should actually be
in by introducing a bit of randomness
2272
02:52:43.541 --> 02:52:45.625
and choosing a house randomly.
2273
02:52:45.625 --> 02:52:47.500
While I can do this in a few ways, Let.
2274
02:52:47.500 --> 02:52:48.875
Me go ahead and do this.
2275
02:52:48.875 --> 02:52:51.541
I need to have a list
of houses somewhere.
2276
02:52:51.541 --> 02:52:52.833
So where can I put that?
2277
02:52:52.833 --> 02:52:54.708
I could solve this
problem in different ways.
2278
02:52:54.708 --> 02:52:56.000
Let me propose that I do this.
2279
02:52:56.000 --> 02:52:58.833
Let me define a method called
init, as I've done before,
2280
02:52:58.833 --> 02:53:01.250
that takes in self,
but no other arguments.
2281
02:53:01.250 --> 02:53:04.583
And whenever the sorting hat
is instantiated, let's do this.
2282
02:53:04.583 --> 02:53:09.958
Let's create a houses instance variable,
plural, that equals this list--
2283
02:53:09.958 --> 02:53:16.958
Gryffindor, Hufflepuff,
Ravenclaw, Slytherin,
2284
02:53:16.958 --> 02:53:19.416
so the exact same list
that we've used before,
2285
02:53:19.416 --> 02:53:23.333
and I'm storing it in an instance
variable inside of this class.
2286
02:53:23.333 --> 02:53:26.708
I'm not taking any arguments
beyond self to init,
2287
02:53:26.708 --> 02:53:30.625
but I just need this list of
values somewhere, for instance.
2288
02:53:30.625 --> 02:53:32.250
So what can I do here?
2289
02:53:32.250 --> 02:53:36.416
Well, let me go ahead and replace
some house with the actual house.
2290
02:53:36.416 --> 02:53:37.541
Well, what could I do here?
2291
02:53:37.541 --> 02:53:39.583
Well, I want to put a house there.
2292
02:53:39.583 --> 02:53:42.083
Well, let's go ahead and
create a variable called house.
2293
02:53:42.083 --> 02:53:46.291
And if you think back to our discussion
of libraries, in the random module,
2294
02:53:46.291 --> 02:53:51.791
there is a function called choice
that, if you pass in a list of choices,
2295
02:53:51.791 --> 02:53:56.333
like self.houses, that will pick
a random house out of those four.
2296
02:53:56.333 --> 02:53:58.750
And then on line 7, I can pass it in.
2297
02:53:58.750 --> 02:54:01.916
If I want to tighten this up, let me
just go ahead and highlight that code,
2298
02:54:01.916 --> 02:54:03.125
get rid of the variable.
2299
02:54:03.125 --> 02:54:04.541
It's technically unnecessary.
2300
02:54:04.541 --> 02:54:06.666
And because the line of
code is still pretty short,
2301
02:54:06.666 --> 02:54:09.458
I'm OK with just putting
it all in one line.
2302
02:54:09.458 --> 02:54:13.000
But I could certainly use the
variable like I did a moment ago.
2303
02:54:13.000 --> 02:54:14.041
So what have I done?
2304
02:54:14.041 --> 02:54:20.708
In my init function, I have defined
a initialization of the object
2305
02:54:20.708 --> 02:54:23.875
that stores in self.houses
the list of four houses.
2306
02:54:23.875 --> 02:54:26.583
And then, in sort, I'm
accessing that same list,
2307
02:54:26.583 --> 02:54:30.041
but I'm randomly choosing
the set of houses there.
2308
02:54:30.041 --> 02:54:31.791
Now, why have I done it in this way?
2309
02:54:31.791 --> 02:54:33.750
This, too, is general convention.
2310
02:54:33.750 --> 02:54:35.916
Any time you have a list
of things that-- who knows?
2311
02:54:35.916 --> 02:54:38.000
Maybe will change over time.
2312
02:54:38.000 --> 02:54:41.541
Places like Harvard have constructed
new houses over the years,
2313
02:54:41.541 --> 02:54:43.958
so you might have to change
the list of available houses.
2314
02:54:43.958 --> 02:54:47.083
It didn't happen in seven books
or eight films of Harry Potter.
2315
02:54:47.083 --> 02:54:49.208
But you could imagine
maybe Hogwarts eventually
2316
02:54:49.208 --> 02:54:54.291
has a fifth house, so there's generally
some value in putting list of constants
2317
02:54:54.291 --> 02:54:57.333
toward the top of your file, toward
the top of the class so it's just
2318
02:54:57.333 --> 02:54:59.875
obvious what the list of values is.
2319
02:54:59.875 --> 02:55:02.750
You don't want to necessarily
tuck it away in some function,
2320
02:55:02.750 --> 02:55:06.375
like sort, especially if you
might want to use that function--
2321
02:55:06.375 --> 02:55:10.166
sorry, especially if you want to use
that list in multiple functions, not
2322
02:55:10.166 --> 02:55:10.708
just sort.
2323
02:55:10.708 --> 02:55:12.583
But if I kept adding to
this class, you might
2324
02:55:12.583 --> 02:55:15.083
want to use that same list of
houses in multiple functions.
2325
02:55:15.083 --> 02:55:20.541
So let's keep it in the object
itself by storing it in self.houses.
2326
02:55:20.541 --> 02:55:23.625
All right, well, we're about to change
the course of history here perhaps.
2327
02:55:23.625 --> 02:55:27.666
Let me do Python of hat.py, and I
think we're about to assign Harry
2328
02:55:27.666 --> 02:55:30.833
to one of those four houses randomly.
2329
02:55:30.833 --> 02:55:33.041
Huh, NameError.
2330
02:55:33.041 --> 02:55:35.041
Name 'random' is not defined.
2331
02:55:35.041 --> 02:55:38.416
Well, wait a minute,
where did I go wrong here?
2332
02:55:38.416 --> 02:55:43.541
Thinking back to our class on libraries,
why did my code break and not tell me
2333
02:55:43.541 --> 02:55:45.625
where Harry is to be?
2334
02:55:45.625 --> 02:55:48.708
AUDIENCE: You did not
import the random library.
2335
02:55:48.708 --> 02:55:49.708
DAVID J. MALAN: Exactly.
2336
02:55:49.708 --> 02:55:52.541
If the random library or module
is something I want to use,
2337
02:55:52.541 --> 02:55:55.583
I need to tell Python that
at the top of my file.
2338
02:55:55.583 --> 02:55:58.958
So let me go up here
and do import random.
2339
02:55:58.958 --> 02:56:01.708
And then, below that, let me go
ahead and clear my terminal window
2340
02:56:01.708 --> 02:56:02.416
and try again.
2341
02:56:02.416 --> 02:56:08.041
Python of hat.py, crossing my fingers,
seeing where Harry is going to end up.
2342
02:56:08.041 --> 02:56:12.041
And, OK, Harry as of now is
officially in Hufflepuff,
2343
02:56:12.041 --> 02:56:14.500
despite everything you've read or seen.
2344
02:56:14.500 --> 02:56:15.875
Well, let's run this again.
2345
02:56:15.875 --> 02:56:19.833
Let me clear my window and run Python
of hat.py, and now he's in Ravenclaw.
2346
02:56:19.833 --> 02:56:21.541
That's consistent with using random.
2347
02:56:21.541 --> 02:56:23.375
Let's clear that and run it again.
2348
02:56:23.375 --> 02:56:25.458
He's still in Ravenclaw,
but that could happen,
2349
02:56:25.458 --> 02:56:26.833
even though there's four choices.
2350
02:56:26.833 --> 02:56:27.625
Let's do it again.
2351
02:56:27.625 --> 02:56:29.250
Hufflepuff-- back in Hufflepuff.
2352
02:56:29.250 --> 02:56:31.666
We can't seem to get the right answer.
2353
02:56:31.666 --> 02:56:34.375
Now he's in Gryffindor, albeit randomly.
2354
02:56:34.375 --> 02:56:37.208
So we seem to have a program that,
based on these limited tests,
2355
02:56:37.208 --> 02:56:41.166
seems to be assigning
Harry to a house randomly.
2356
02:56:41.166 --> 02:56:45.416
Now I'm somewhat lazily just
letting sort print out this value.
2357
02:56:45.416 --> 02:56:50.375
I could do something else, like return
a string, and then let me, on line 13,
2358
02:56:50.375 --> 02:56:51.916
do the printing for me.
2359
02:56:51.916 --> 02:56:57.000
But for now, I think we have an
example of a class called hat that,
2360
02:56:57.000 --> 02:57:00.125
nonetheless, applies some of our
lessons learned thus far today,
2361
02:57:00.125 --> 02:57:03.333
where I've created a class--
because a sorting hat is, frankly--
2362
02:57:03.333 --> 02:57:06.250
well, I was about to say
real world entity, but really
2363
02:57:06.250 --> 02:57:07.875
a fantasy world entity.
2364
02:57:07.875 --> 02:57:11.791
And indeed, that's a, perhaps, common
heuristic or mental model to have.
2365
02:57:11.791 --> 02:57:16.041
When should you use a class to
represent something in your code?
2366
02:57:16.041 --> 02:57:20.625
Very often, when you're trying to
represent some real world entity
2367
02:57:20.625 --> 02:57:25.791
or fantasy world entity, like a student,
which is something in the real world,
2368
02:57:25.791 --> 02:57:28.625
like a sorting hat,
which, OK, doesn't exist,
2369
02:57:28.625 --> 02:57:32.166
but hat's certainly do, so quite
reasonable to have a class for hat.
2370
02:57:32.166 --> 02:57:36.500
And that's not always the case that
classes represent real world entities.
2371
02:57:36.500 --> 02:57:42.291
But we've seen thus far that int
and stir and list and dict-- these
2372
02:57:42.291 --> 02:57:44.666
are all structures that you
might have in the real world.
2373
02:57:44.666 --> 02:57:47.416
We have integers and strings
of text and other things.
2374
02:57:47.416 --> 02:57:50.833
So it rather makes sense to represent
even those things, more technically,
2375
02:57:50.833 --> 02:57:52.625
using a class as well.
2376
02:57:52.625 --> 02:57:56.291
You could use just a dictionary
to represent a student or a hat.
2377
02:57:56.291 --> 02:58:00.875
But again, with classes come all
this and even more functionality.
2378
02:58:00.875 --> 02:58:06.791
But I honestly am not using classes
in, really, the "right way" here.
2379
02:58:06.791 --> 02:58:07.416
Why?
2380
02:58:07.416 --> 02:58:10.625
Well, in the world of Harry
Potter there really is only,
2381
02:58:10.625 --> 02:58:12.791
to my knowledge, one sorting hat.
2382
02:58:12.791 --> 02:58:17.000
And yet, here I have gone and
implemented a class called hat.
2383
02:58:17.000 --> 02:58:20.333
And again, a class is like
a blueprint, a template,
2384
02:58:20.333 --> 02:58:24.625
a mold that allows you to create
one or more objects thereof.
2385
02:58:24.625 --> 02:58:27.041
Now, most of my programs Thus
far have been pretty simple,
2386
02:58:27.041 --> 02:58:28.750
and I've just created one student.
2387
02:58:28.750 --> 02:58:31.208
But certainly, if I spent
more time and wrote more code,
2388
02:58:31.208 --> 02:58:33.583
you could imagine
writing one program that
2389
02:58:33.583 --> 02:58:36.208
has a list of students--
many more students
2390
02:58:36.208 --> 02:58:38.708
than just the one we keep demonstrating.
2391
02:58:38.708 --> 02:58:40.625
Yet it would be a little weird--
2392
02:58:40.625 --> 02:58:43.291
it's a little inconsistent
with the real or the fantasy
2393
02:58:43.291 --> 02:58:48.041
world of Harry Potter to instantiate
one, two, three or more sorting hats.
2394
02:58:48.041 --> 02:58:49.708
There really is just one.
2395
02:58:49.708 --> 02:58:51.875
Really one singleton,
if you will, which is
2396
02:58:51.875 --> 02:58:54.458
a term of art in a lot of
contexts of programming.
2397
02:58:54.458 --> 02:58:58.125
So let me propose that we actually
improve the design of the sorting hat
2398
02:58:58.125 --> 02:59:03.083
so that we don't have to instantiate
a sorting hat because right now this
2399
02:59:03.083 --> 02:59:08.791
is kind of allowing me to do something
like hat 1 = hat, hat 2 = hat, hat 3 =,
2400
02:59:08.791 --> 02:59:09.458
and so forth.
2401
02:59:09.458 --> 02:59:11.291
I don't really need that capability.
2402
02:59:11.291 --> 02:59:15.250
I really just need to represent
the sorting hat with a class,
2403
02:59:15.250 --> 02:59:17.125
but I don't really
need to instantiate it.
2404
02:59:17.125 --> 02:59:17.625
Why?
2405
02:59:17.625 --> 02:59:18.708
Because it already exists.
2406
02:59:18.708 --> 02:59:19.625
I need just one.
2407
02:59:19.625 --> 02:59:23.041
So it turns out, in
Python, that, up until now,
2408
02:59:23.041 --> 02:59:25.833
we've been using, as I keep
calling them, instance methods--
2409
02:59:25.833 --> 02:59:30.416
writing functions inside of classes that
are automatically passed a reference
2410
02:59:30.416 --> 02:59:32.375
to self, the current object.
2411
02:59:32.375 --> 02:59:34.375
But sometimes you just don't need that.
2412
02:59:34.375 --> 02:59:37.083
Sometimes it suffices to
just know what the class is
2413
02:59:37.083 --> 02:59:40.875
and assume that there might not
even be any objects of that class.
2414
02:59:40.875 --> 02:59:45.666
So in this sense, you can use a
class really as a container for data
2415
02:59:45.666 --> 02:59:50.291
and/or functionality that is just
somehow conceptually related--
2416
02:59:50.291 --> 02:59:52.791
things related to a sorting hat.
2417
02:59:52.791 --> 02:59:56.083
And there's this other decorator
or function called @classmethod
2418
02:59:56.083 --> 02:59:57.750
that allows us to do just this.
2419
02:59:57.750 --> 02:59:59.375
So let me go back to my code here.
2420
02:59:59.375 --> 03:00:05.041
And let me propose that, if I'm not
going to instantiate multiple houses,
2421
03:00:05.041 --> 03:00:07.916
I don't really need this init
method because that's really
2422
03:00:07.916 --> 03:00:12.250
meant to initialize specific objects
from that blueprint, that template,
2423
03:00:12.250 --> 03:00:13.083
that mold.
2424
03:00:13.083 --> 03:00:14.708
So let me get rid of this.
2425
03:00:14.708 --> 03:00:17.791
But if I get rid of this, I
no longer have access to self.
2426
03:00:17.791 --> 03:00:22.750
But that's OK because it turns out,
in addition to their existing class
2427
03:00:22.750 --> 03:00:26.416
methods, there are also what
we might call class variables.
2428
03:00:26.416 --> 03:00:30.791
And class variables exist
within the class itself.
2429
03:00:30.791 --> 03:00:33.833
And there's just one
copy of that variable
2430
03:00:33.833 --> 03:00:35.958
for all of the objects thereof.
2431
03:00:35.958 --> 03:00:40.291
They all share, if you will, the
same variable-- be it an int or str
2432
03:00:40.291 --> 03:00:41.750
or, in this case, a list.
2433
03:00:41.750 --> 03:00:49.041
So what I've done here is define, inside
of my hat class, in a class variable
2434
03:00:49.041 --> 03:00:50.208
called houses--
2435
03:00:50.208 --> 03:00:53.083
I don't say self because
self is no longer relevant.
2436
03:00:53.083 --> 03:00:54.791
Self refers to specific objects.
2437
03:00:54.791 --> 03:00:58.166
I want a variable inside
of this class, a.k.a.
2438
03:00:58.166 --> 03:01:00.791
A class variable that equals that list.
2439
03:01:00.791 --> 03:01:03.708
Because it's inside of
this hat, now, class,
2440
03:01:03.708 --> 03:01:07.208
I can use that list in
any of my functions.
2441
03:01:07.208 --> 03:01:08.958
I've only got one now, called sort.
2442
03:01:08.958 --> 03:01:11.791
But if I had more, it would be
accessible to all of those methods
2443
03:01:11.791 --> 03:01:12.666
as well.
2444
03:01:12.666 --> 03:01:15.791
And with sort, it also
doesn't really make sense
2445
03:01:15.791 --> 03:01:19.250
to sort within a specific
sorting hat because, again, I
2446
03:01:19.250 --> 03:01:20.500
only want there to be one.
2447
03:01:20.500 --> 03:01:25.875
So I can actually specify that this is
class method by saying @classmethod.
2448
03:01:25.875 --> 03:01:28.083
And I don't pass in self anymore.
2449
03:01:28.083 --> 03:01:33.250
I actually, by convention, pass in
a reference to the class itself.
2450
03:01:33.250 --> 03:01:35.250
It's typically written as cls.
2451
03:01:35.250 --> 03:01:35.791
Why?
2452
03:01:35.791 --> 03:01:40.916
Well, if you wrote C-L-A-S-S, that
would actually conflict with the keyword
2453
03:01:40.916 --> 03:01:42.791
"class" that we keep using up here.
2454
03:01:42.791 --> 03:01:47.125
So the world realized that, oops, we
can't reuse that same phrase here.
2455
03:01:47.125 --> 03:01:48.833
So let's just call this class.
2456
03:01:48.833 --> 03:01:51.541
This is useful in some
contexts including this one.
2457
03:01:51.541 --> 03:01:52.041
Why?
2458
03:01:52.041 --> 03:01:54.125
Well, notice what I can now do.
2459
03:01:54.125 --> 03:01:56.916
I can now change self to be just class.
2460
03:01:56.916 --> 03:01:57.541
Why?
2461
03:01:57.541 --> 03:02:02.708
Because houses now-- not an instance
variable, accessible via self.houses.
2462
03:02:02.708 --> 03:02:07.041
It is now a class variable,
accessible via class.houses,
2463
03:02:07.041 --> 03:02:10.375
or technically cls.houses in this case.
2464
03:02:10.375 --> 03:02:12.666
But now the final flourish is this.
2465
03:02:12.666 --> 03:02:19.208
Now, I don't have to instantiate any hat
objects as I used to on here, line 13.
2466
03:02:19.208 --> 03:02:22.833
I can just use functionality
that comes with this class.
2467
03:02:22.833 --> 03:02:24.791
So I'm going to delete
that line altogether.
2468
03:02:24.791 --> 03:02:32.416
I'm going to capitalize the hat on
this new line 13 and just say hat.sort,
2469
03:02:32.416 --> 03:02:33.333
("Harry").
2470
03:02:33.333 --> 03:02:34.750
So what have I done?
2471
03:02:34.750 --> 03:02:38.541
I've not bothered instantiating
an object of type, hat.
2472
03:02:38.541 --> 03:02:42.791
I am just accessing a class
method inside of the hat class
2473
03:02:42.791 --> 03:02:43.666
that-- you know what?
2474
03:02:43.666 --> 03:02:45.208
Is just going to work.
2475
03:02:45.208 --> 03:02:46.708
This is how class methods work.
2476
03:02:46.708 --> 03:02:51.041
You use the name of the class, capital
letter and all, dot method name,
2477
03:02:51.041 --> 03:02:53.041
passing in any arguments you want.
2478
03:02:53.041 --> 03:02:57.000
Python is going to automatically
pass in some variable via which
2479
03:02:57.000 --> 03:03:00.625
you can refer to that
class in that function
2480
03:03:00.625 --> 03:03:02.458
that you've implemented
inside of that class
2481
03:03:02.458 --> 03:03:04.125
so that I can do something like this.
2482
03:03:04.125 --> 03:03:07.833
It's not that I want a variable called
houses locally in this function,
2483
03:03:07.833 --> 03:03:12.541
I want the variable called houses that's
associated with this current class
2484
03:03:12.541 --> 03:03:17.166
so I can still access this same
list that I defined on line 6.
2485
03:03:17.166 --> 03:03:21.500
And now, if I go back down here to
my terminal and run Python of hat.py,
2486
03:03:21.500 --> 03:03:24.875
Enter, Harry is still
in Hufflepuff once more.
2487
03:03:24.875 --> 03:03:27.041
Harry is still in Hufflepuff once more.
2488
03:03:27.041 --> 03:03:31.250
Harry is back in Gryffindor,
at least randomly.
2489
03:03:31.250 --> 03:03:36.333
Questions, now, on these class
variables or these class methods,
2490
03:03:36.333 --> 03:03:41.416
which are in contrast with instance
variables and instance methods.
2491
03:03:41.416 --> 03:03:43.875
And the one thing, at least,
that's a little strange
2492
03:03:43.875 --> 03:03:47.666
here is that, even though there's
a decorator called @classmethod,
2493
03:03:47.666 --> 03:03:51.291
there is not one called @instancemethod.
2494
03:03:51.291 --> 03:03:55.666
A method is just automatically a
so-called "instant method" when
2495
03:03:55.666 --> 03:03:57.541
you define it without any decorator.
2496
03:03:57.541 --> 03:04:00.291
AUDIENCE: Can you have a
class inside another class?
2497
03:04:00.291 --> 03:04:01.291
DAVID J. MALAN: You can.
2498
03:04:01.291 --> 03:04:03.666
You can define one
class inside of another.
2499
03:04:03.666 --> 03:04:06.041
Generally speaking, this
isn't done, but there
2500
03:04:06.041 --> 03:04:08.416
are cases where it can
be helpful, especially
2501
03:04:08.416 --> 03:04:10.625
for larger, more sophisticated programs.
2502
03:04:10.625 --> 03:04:13.541
So yes, it is possible.
2503
03:04:13.541 --> 03:04:14.750
Other questions.
2504
03:04:14.750 --> 03:04:17.958
AUDIENCE: The question
was about the self.houses.
2505
03:04:17.958 --> 03:04:25.083
When we remove it and we pass
data, variable is created itself,
2506
03:04:25.083 --> 03:04:27.458
s why we remove the self?
2507
03:04:27.458 --> 03:04:29.625
DAVID J. MALAN: So in
the previous examples--
2508
03:04:29.625 --> 03:04:32.625
both of the hat demonstration
and also all of the student
2509
03:04:32.625 --> 03:04:36.708
demonstrations-- we
were creating a student
2510
03:04:36.708 --> 03:04:40.625
object by calling Student, capital S,
open parenthesis, close parenthesis,
2511
03:04:40.625 --> 03:04:43.125
with, eventually, name
and a house passed in.
2512
03:04:43.125 --> 03:04:46.833
And then we were using the
double underscore init method
2513
03:04:46.833 --> 03:04:52.708
to initialize the self.name and
the self.house instance variables
2514
03:04:52.708 --> 03:04:55.333
therein to those respective values.
2515
03:04:55.333 --> 03:04:57.750
In this latest version
of the sorting hat,
2516
03:04:57.750 --> 03:05:02.166
I haven't bothered with self anywhere,
only because, conceptually, I
2517
03:05:02.166 --> 03:05:05.708
don't need or want there to
be multiple hats in the world.
2518
03:05:05.708 --> 03:05:11.041
I'm just using the class as a container
to bundle up this list of houses,
2519
03:05:11.041 --> 03:05:12.500
this sorting functionality.
2520
03:05:12.500 --> 03:05:14.875
Maybe eventually all add
more functionality to it.
2521
03:05:14.875 --> 03:05:15.875
But that's it.
2522
03:05:15.875 --> 03:05:18.833
And so sometimes you can use
object-oriented programming
2523
03:05:18.833 --> 03:05:22.458
in this somewhat different way when
you want there to be functionality
2524
03:05:22.458 --> 03:05:25.333
but it's not specific
to any one specific hat.
2525
03:05:25.333 --> 03:05:29.416
It's specific to the sorting hat itself.
2526
03:05:29.416 --> 03:05:33.958
How about one other question now, on
these class variables or methods-- just
2527
03:05:33.958 --> 03:05:37.000
another way of using object-oriented
programming but to solve
2528
03:05:37.000 --> 03:05:38.541
a somewhat different problem?
2529
03:05:38.541 --> 03:05:40.916
AUDIENCE: Well, what's the
difference between the class
2530
03:05:40.916 --> 03:05:44.833
hat and a function of hat?
2531
03:05:44.833 --> 03:05:46.166
DAVID J. MALAN: A good question.
2532
03:05:46.166 --> 03:05:48.500
So why are we using a
class at all and not just
2533
03:05:48.500 --> 03:05:51.916
having a file called hat.py
with a variable called
2534
03:05:51.916 --> 03:05:54.500
houses and a function called sort?
2535
03:05:54.500 --> 03:05:56.166
Why are we adding this complexity?
2536
03:05:56.166 --> 03:05:58.958
In this particular case, we
don't necessarily need to.
2537
03:05:58.958 --> 03:06:01.166
I could absolutely go in here.
2538
03:06:01.166 --> 03:06:02.833
I could get rid of the class.
2539
03:06:02.833 --> 03:06:05.625
I could undo this indentation.
2540
03:06:05.625 --> 03:06:07.666
I could get rid of this decorator.
2541
03:06:07.666 --> 03:06:09.541
And I could get rid of hat dot.
2542
03:06:09.541 --> 03:06:12.291
And I could just do
this and additionally
2543
03:06:12.291 --> 03:06:14.208
let's say, let's get rid of class here.
2544
03:06:14.208 --> 03:06:15.833
Let's get rid of class here.
2545
03:06:15.833 --> 03:06:19.833
And now run Python of hat.py,
Enter, and it still works.
2546
03:06:19.833 --> 03:06:22.125
Put Harry in the wrong house,
but that's what we have--
2547
03:06:22.125 --> 03:06:23.208
what happens randomly.
2548
03:06:23.208 --> 03:06:24.500
That's fine, too.
2549
03:06:24.500 --> 03:06:27.750
What we're introducing today, by
way of object-oriented programming,
2550
03:06:27.750 --> 03:06:30.208
is just a different way
of modeling the world.
2551
03:06:30.208 --> 03:06:32.916
It's not really compelling
with an example like this,
2552
03:06:32.916 --> 03:06:35.125
frankly, that's relatively simple.
2553
03:06:35.125 --> 03:06:36.125
It's not very complex.
2554
03:06:36.125 --> 03:06:37.416
There's not much functionality.
2555
03:06:37.416 --> 03:06:40.875
Honestly, the version that we just typed
up-- these 10 lines-- this is fine.
2556
03:06:40.875 --> 03:06:42.208
This solves this problem.
2557
03:06:42.208 --> 03:06:45.708
But as our code gets longer, as we
start collaborating with other people,
2558
03:06:45.708 --> 03:06:48.666
as the problems we're trying to solve
with code get more sophisticated,
2559
03:06:48.666 --> 03:06:52.125
you're going to find that
your code gets messy quickly.
2560
03:06:52.125 --> 03:06:55.458
And you're going to find that you have a
huge number of functions, for instance,
2561
03:06:55.458 --> 03:06:56.500
in one file.
2562
03:06:56.500 --> 03:06:59.958
And some of them are related to each
other, but some of them are not.
2563
03:06:59.958 --> 03:07:02.625
Well, at that point, wouldn't
it be nice to just organize them
2564
03:07:02.625 --> 03:07:03.500
a little differently?
2565
03:07:03.500 --> 03:07:06.500
And in the world of Harry Potter,
let's have a class for student;
2566
03:07:06.500 --> 03:07:09.916
let's have a class for Professor;
let's have a class for the sorting hat;
2567
03:07:09.916 --> 03:07:11.708
let's have a class for something else.
2568
03:07:11.708 --> 03:07:14.625
And so once your world gets
much more complicated than some
2569
03:07:14.625 --> 03:07:17.791
of the demonstrations we do here
in class when we want to focus
2570
03:07:17.791 --> 03:07:21.500
on individual ideas, object-oriented
programming is just a way
2571
03:07:21.500 --> 03:07:24.083
of encapsulating related data--
2572
03:07:24.083 --> 03:07:27.291
that is, variables-- related
functionality-- that is, methods--
2573
03:07:27.291 --> 03:07:29.583
inside of things that have names.
2574
03:07:29.583 --> 03:07:31.625
These things are called classes.
2575
03:07:31.625 --> 03:07:33.750
So it's just another
way to solve problems.
2576
03:07:33.750 --> 03:07:36.125
And when we focused on libraries
a couple of weeks back,
2577
03:07:36.125 --> 03:07:38.791
that, too, was another
solution to the same problem.
2578
03:07:38.791 --> 03:07:42.291
You could define your own modules
or packages, put some of your data
2579
03:07:42.291 --> 03:07:44.708
and/or functionality in
there, and that's fine, too.
2580
03:07:44.708 --> 03:07:48.000
And sometimes which one
you should use overlaps.
2581
03:07:48.000 --> 03:07:51.333
If you're familiar with Venn
diagrams, the overlapping region
2582
03:07:51.333 --> 03:07:54.791
might mean that you could use a class;
you could use a module or a package;
2583
03:07:54.791 --> 03:07:57.500
you could just use a single local file.
2584
03:07:57.500 --> 03:07:59.916
Over time, you'll develop
an instinct and maybe even
2585
03:07:59.916 --> 03:08:04.291
a personal preference
for which tool to use.
2586
03:08:04.291 --> 03:08:08.416
All right, let me propose, now, that we
apply this same idea of a class method
2587
03:08:08.416 --> 03:08:10.583
to clean up one other thing as well.
2588
03:08:10.583 --> 03:08:16.041
Let me close that hat.py and reopen
student.py as we left it earlier,
2589
03:08:16.041 --> 03:08:19.500
and let me go ahead and
simplify it just a little bit.
2590
03:08:19.500 --> 03:08:22.750
I'm going to go ahead and
get rid of the properties,
2591
03:08:22.750 --> 03:08:24.666
not because there's
anything, wrong with them,
2592
03:08:24.666 --> 03:08:27.416
but just because I want us to
focus on some of the key ideas
2593
03:08:27.416 --> 03:08:29.333
when we began with this program.
2594
03:08:29.333 --> 03:08:31.750
So I'm going to go ahead
and keep main as well.
2595
03:08:31.750 --> 03:08:35.583
I'm not going to adversarially try
to change Henry's address there.
2596
03:08:35.583 --> 03:08:38.666
I'm going to instead go ahead,
though, and just print the student.
2597
03:08:38.666 --> 03:08:41.750
But this is the thing I
want to focus on here.
2598
03:08:41.750 --> 03:08:46.541
This, in our previous student
examples, was a missed opportunity
2599
03:08:46.541 --> 03:08:48.125
to clean up my code.
2600
03:08:48.125 --> 03:08:49.708
Well, what do I mean by that?
2601
03:08:49.708 --> 03:08:51.750
Well, up here at the
top of this file-- even
2602
03:08:51.750 --> 03:08:53.958
though I've simplified it, but
getting rid of the properties
2603
03:08:53.958 --> 03:08:55.708
and all of that error
checking-- because I
2604
03:08:55.708 --> 03:08:58.708
want to focus on the essence of this
class now-- just the student's name
2605
03:08:58.708 --> 03:09:00.875
and the house and the printing thereof.
2606
03:09:00.875 --> 03:09:05.000
This is, by nature of classes
in object-oriented programming,
2607
03:09:05.000 --> 03:09:09.666
theoretically, all of my
student-specific functionality.
2608
03:09:09.666 --> 03:09:14.083
That is to say, if I have functionality
and data related to a student, you,
2609
03:09:14.083 --> 03:09:16.583
the programmer, my
colleague, would assume
2610
03:09:16.583 --> 03:09:18.875
that it's all bundled
up, encapsulated, so
2611
03:09:18.875 --> 03:09:20.791
to speak, inside of the student class.
2612
03:09:20.791 --> 03:09:24.208
And yet, if you scroll
down further, what is this?
2613
03:09:24.208 --> 03:09:27.458
There's a function called get_student
that just exists elsewhere
2614
03:09:27.458 --> 03:09:31.166
in this file that prompts the user for
a name, prompts the user for a house,
2615
03:09:31.166 --> 03:09:34.083
creates the student
object, and then returns.
2616
03:09:34.083 --> 03:09:35.500
That's not wrong.
2617
03:09:35.500 --> 03:09:36.208
It works.
2618
03:09:36.208 --> 03:09:39.041
And we saw many, many
times it kept working.
2619
03:09:39.041 --> 03:09:42.208
But this is a little
weird because, if this
2620
03:09:42.208 --> 03:09:45.208
is a function that
helps you get a student,
2621
03:09:45.208 --> 03:09:48.916
helps you get the name of a student
and the house of a student, why isn't
2622
03:09:48.916 --> 03:09:51.125
that functionality in the class itself?
2623
03:09:51.125 --> 03:09:54.291
After all, as my code gets more and
more complicated and does more things,
2624
03:09:54.291 --> 03:09:56.125
I'm going to be looking
at the student class
2625
03:09:56.125 --> 03:09:57.750
for all student-related functionality.
2626
03:09:57.750 --> 03:10:00.625
I'm not going to be scrolling down,
expecting that, oh, maybe there's
2627
03:10:00.625 --> 03:10:04.708
some other student functionality
just randomly later in this file.
2628
03:10:04.708 --> 03:10:05.958
So it's not wrong.
2629
03:10:05.958 --> 03:10:09.625
But this is, again, evidence
of maybe bad design--
2630
03:10:09.625 --> 03:10:11.541
not so much with this small program.
2631
03:10:11.541 --> 03:10:14.458
But this is an example,
again, of code smell.
2632
03:10:14.458 --> 03:10:16.000
Something smells a little off here.
2633
03:10:16.000 --> 03:10:17.791
This is probably going
to get us in trouble
2634
03:10:17.791 --> 03:10:20.791
by separating related functionality.
2635
03:10:20.791 --> 03:10:24.541
So again it's a design principle,
not a correctness concern.
2636
03:10:24.541 --> 03:10:27.958
But class methods allow
us to address this, too.
2637
03:10:27.958 --> 03:10:29.791
Let me go ahead and do this.
2638
03:10:29.791 --> 03:10:32.666
I'm going to delete get_student
all together, leaving
2639
03:10:32.666 --> 03:10:35.458
only main as my other function here.
2640
03:10:35.458 --> 03:10:38.541
And inside of my student
class, I'm going to do this.
2641
03:10:38.541 --> 03:10:42.375
I'm going to define a function,
even more simply called, get.
2642
03:10:42.375 --> 03:10:45.291
And by nature of how
class methods work, it's
2643
03:10:45.291 --> 03:10:47.625
going to take in the
name of the class itself
2644
03:10:47.625 --> 03:10:49.833
or a reference thereto as an argument.
2645
03:10:49.833 --> 03:10:53.083
And I'm going to move the functionality
from get_student into the student
2646
03:10:53.083 --> 03:10:53.583
class.
2647
03:10:53.583 --> 03:10:58.750
And I'm going to do this-- name equals
input, quote, unquote, name, house
2648
03:10:58.750 --> 03:11:01.708
equals input, quote, unquote, house.
2649
03:11:01.708 --> 03:11:04.291
And then what this
function is going to do
2650
03:11:04.291 --> 03:11:11.333
is return a new student object by
calling class, which, again, is just
2651
03:11:11.333 --> 03:11:14.250
an automatically passed-in
reference to the class
2652
03:11:14.250 --> 03:11:17.666
itself, passing in name and house.
2653
03:11:17.666 --> 03:11:22.916
And I will admit this syntax seems a
little strange that now I'm calling cls
2654
03:11:22.916 --> 03:11:24.375
and I'm passing in these arguments.
2655
03:11:24.375 --> 03:11:26.125
But let me do one final fix here.
2656
03:11:26.125 --> 03:11:28.041
Let me go to the top of
this function and more
2657
03:11:28.041 --> 03:11:31.125
explicitly say this is a class method.
2658
03:11:31.125 --> 03:11:34.541
This solves a potential chicken
and the egg problem, so to speak,
2659
03:11:34.541 --> 03:11:37.291
whereby one needs to come
before the other, potentially.
2660
03:11:37.291 --> 03:11:39.250
So what am I doing here?
2661
03:11:39.250 --> 03:11:43.916
Inside of my student class, I
now have a function called get.
2662
03:11:43.916 --> 03:11:47.333
It is, I shall claim, a class
method what does that mean.
2663
03:11:47.333 --> 03:11:53.000
It just means I can call this method
without instantiating a student
2664
03:11:53.000 --> 03:11:54.125
object first.
2665
03:11:54.125 --> 03:11:57.333
Therein lies the potential
chicken and the egg problem.
2666
03:11:57.333 --> 03:12:00.375
And if unfamiliar, that's an expression,
meaning, well, and did the world
2667
03:12:00.375 --> 03:12:03.291
have chickens first that laid
eggs, or was there an egg
2668
03:12:03.291 --> 03:12:06.958
that then yielded the chickens,
but how did the egg get there?
2669
03:12:06.958 --> 03:12:08.791
It's this weird, circular problem.
2670
03:12:08.791 --> 03:12:10.208
And that's what we're facing here.
2671
03:12:10.208 --> 03:12:16.375
It would be weird if you had to create
a student object in order to call get,
2672
03:12:16.375 --> 03:12:19.791
in order to get another student object.
2673
03:12:19.791 --> 03:12:20.875
That sounds messy.
2674
03:12:20.875 --> 03:12:24.833
Let's just get a student
via a class method
2675
03:12:24.833 --> 03:12:29.125
that, by definition, does not require
you to create a student object first.
2676
03:12:29.125 --> 03:12:33.166
Just like the hat, in its final
form, we use the hat class
2677
03:12:33.166 --> 03:12:35.833
to just say Hat, capital H, dot sort.
2678
03:12:35.833 --> 03:12:37.458
We didn't need to create a hat first.
2679
03:12:37.458 --> 03:12:39.708
We just used the class itself.
2680
03:12:39.708 --> 03:12:41.791
So what am I going to do here now?
2681
03:12:41.791 --> 03:12:43.041
Let me go down to main.
2682
03:12:43.041 --> 03:12:46.791
And instead of saying get_student,
notice what I can now do.
2683
03:12:46.791 --> 03:12:51.625
Student.get, and everything
else can stay the same.
2684
03:12:51.625 --> 03:12:55.416
All I've done now is I've
migrated all of my logic
2685
03:12:55.416 --> 03:12:58.541
from get_student, which was
this own standalone function,
2686
03:12:58.541 --> 03:13:01.291
but clearly related to students by name.
2687
03:13:01.291 --> 03:13:05.458
I've moved the same
code, really, to inside
2688
03:13:05.458 --> 03:13:09.458
of the student class in a more
simply named function called get.
2689
03:13:09.458 --> 03:13:11.541
But I could still call
it get_student if I want.
2690
03:13:11.541 --> 03:13:14.791
It just seems a little redundant to
call it get_student in a student class,
2691
03:13:14.791 --> 03:13:16.291
so I'm simplifying.
2692
03:13:16.291 --> 03:13:20.208
So I have a method called get,
but I'm calling it a class method
2693
03:13:20.208 --> 03:13:22.333
to avoid that chicken
and the egg problem.
2694
03:13:22.333 --> 03:13:27.958
I want to be able to call a get without
having a student object in my universe
2695
03:13:27.958 --> 03:13:28.791
already.
2696
03:13:28.791 --> 03:13:32.041
And the syntax for that is @classmethod.
2697
03:13:32.041 --> 03:13:35.333
The convention is to give this
method at least one argument,
2698
03:13:35.333 --> 03:13:38.291
by convention called cls for
class, which is just going
2699
03:13:38.291 --> 03:13:40.125
to be a reference to the class itself.
2700
03:13:40.125 --> 03:13:43.500
Lines 11 and 12 are identical
to what they've always been.
2701
03:13:43.500 --> 03:13:46.666
And get_student-- the only
new syntax here is this,
2702
03:13:46.666 --> 03:13:50.500
but this, again, is one of the features
of object-oriented programming.
2703
03:13:50.500 --> 03:13:57.791
You can now instantiate a student object
by just using cls that's passed in.
2704
03:13:57.791 --> 03:14:00.583
I technically could
use Student, capital S,
2705
03:14:00.583 --> 03:14:03.333
but it turns out I'm doing what's
more conventional because this
2706
03:14:03.333 --> 03:14:07.458
will both solve and avoid problems down
the line with more complicated code.
2707
03:14:07.458 --> 03:14:11.916
This line here, on line 13, just means
create an object of the current class.
2708
03:14:11.916 --> 03:14:12.708
What class is that?
2709
03:14:12.708 --> 03:14:13.791
Well, whatever cls is.
2710
03:14:13.791 --> 03:14:17.458
Well, that, by definition of how it
all works, is going to be student.
2711
03:14:17.458 --> 03:14:22.250
And I want you to initialize it,
as always, with name and house.
2712
03:14:22.250 --> 03:14:25.666
So now, scrolling down, my code is this.
2713
03:14:25.666 --> 03:14:27.875
And this is just nice to read.
2714
03:14:27.875 --> 03:14:31.791
You perhaps have to acquire a taste for
this-- and I sound a little odd saying,
2715
03:14:31.791 --> 03:14:32.750
this is nice to read.
2716
03:14:32.750 --> 03:14:36.125
But indeed, student.get just
tells me what's going on.
2717
03:14:36.125 --> 03:14:37.375
I'm going to get a student.
2718
03:14:37.375 --> 03:14:40.958
I don't need a separate function written
by me called get_student in the file
2719
03:14:40.958 --> 03:14:41.750
itself.
2720
03:14:41.750 --> 03:14:44.875
The get functionality
is built into the class.
2721
03:14:44.875 --> 03:14:47.416
All my student-related
code now is together.
2722
03:14:47.416 --> 03:14:51.041
So let me go down to my terminal window
and run Python of student.py, Enter.
2723
03:14:51.041 --> 03:14:52.083
Let's type in Harry.
2724
03:14:52.083 --> 03:14:53.416
Let's type in Gryffindor.
2725
03:14:53.416 --> 03:14:54.958
And we're back to where we began.
2726
03:14:54.958 --> 03:15:00.541
But, but, but everything related to
students, now, is in this here class.
2727
03:15:00.541 --> 03:15:04.500
The only other thing in the file
is main and this conditional
2728
03:15:04.500 --> 03:15:08.458
that we always use to avoid accidentally
executing main when we're making
2729
03:15:08.458 --> 03:15:10.833
a module or a package or the like.
2730
03:15:10.833 --> 03:15:13.541
So again, a solution to a problem--
2731
03:15:13.541 --> 03:15:16.875
not a big one in the case of a
relatively small program, but one
2732
03:15:16.875 --> 03:15:19.041
that you will eventually
encounter as your programs
2733
03:15:19.041 --> 03:15:24.083
get longer and longer, with more
and more entities to represent.
2734
03:15:24.083 --> 03:15:29.000
Questions now on this
use of a class method.
2735
03:15:29.000 --> 03:15:32.291
MICHAEL: Does the class have to be
defined before the main function,
2736
03:15:32.291 --> 03:15:34.458
in terms of the order of the program?
2737
03:15:34.458 --> 03:15:36.083
DAVID J. MALAN: A really good question.
2738
03:15:36.083 --> 03:15:37.291
So when in doubt, let's try this.
2739
03:15:37.291 --> 03:15:38.666
So let's try to change the order.
2740
03:15:38.666 --> 03:15:41.208
Let's move main to the top,
which I've often encouraged.
2741
03:15:41.208 --> 03:15:44.208
So let's go ahead and,
above the class, do this.
2742
03:15:44.208 --> 03:15:47.000
And notice now that,
technically, line two
2743
03:15:47.000 --> 03:15:51.458
is mentioning student, which does
not exist until line 6 and below.
2744
03:15:51.458 --> 03:15:54.666
Let me go ahead and clear my terminal
and run Python of student.py.
2745
03:15:54.666 --> 03:15:55.875
So far, so good.
2746
03:15:55.875 --> 03:15:58.750
Harry-- Gryffindor, OK.
2747
03:15:58.750 --> 03:16:00.458
Indeed, Harry's from Gryffindor.
2748
03:16:00.458 --> 03:16:03.541
The reason, Michael, it
does not matter in this case
2749
03:16:03.541 --> 03:16:06.791
is because we're not actually
calling main until the very end.
2750
03:16:06.791 --> 03:16:10.375
And just as in the past, that means that
Python has a chance to read everything,
2751
03:16:10.375 --> 03:16:11.625
top to bottom, left to right.
2752
03:16:11.625 --> 03:16:13.083
So everything exists.
2753
03:16:13.083 --> 03:16:17.458
I would say, generally classes are
defined at the top of the file.
2754
03:16:17.458 --> 03:16:21.916
However, it would be even maybe
cleaner to move the Classes definition
2755
03:16:21.916 --> 03:16:25.083
to its own file and then
import it, so essentially
2756
03:16:25.083 --> 03:16:28.750
to make reusable code by putting
it into your own module or package
2757
03:16:28.750 --> 03:16:31.083
so that not just this
program but many others
2758
03:16:31.083 --> 03:16:33.541
can use that definition
of student as well.
2759
03:16:33.541 --> 03:16:37.750
Other questions now on classes,
class methods, or the like.
2760
03:16:37.750 --> 03:16:44.833
AUDIENCE: I wanted to ask, is there
a way to declare all the possible--
2761
03:16:44.833 --> 03:16:47.583
all the possible
attributes of the class?
2762
03:16:47.583 --> 03:16:51.000
Because it looks so inconsistent.
2763
03:16:51.000 --> 03:16:52.750
DAVID J. MALAN: Well,
so my takeaway there
2764
03:16:52.750 --> 03:16:55.208
is this is Python's approach
to these principles.
2765
03:16:55.208 --> 03:16:57.791
Different languages, like Java,
just take a different approach
2766
03:16:57.791 --> 03:16:59.666
but have very similar features.
2767
03:16:59.666 --> 03:17:01.500
The syntax just tends to vary.
2768
03:17:01.500 --> 03:17:05.208
And this is how the Python community
chose to implement this idea.
2769
03:17:05.208 --> 03:17:08.750
The right mental model, ultimately,
is that these instance variables,
2770
03:17:08.750 --> 03:17:14.958
instant methods belong to or
operate on specific objects--
2771
03:17:14.958 --> 03:17:17.750
a specific student, a specific hat.
2772
03:17:17.750 --> 03:17:22.500
Class variables and class methods
operate on the entire class
2773
03:17:22.500 --> 03:17:26.125
itself or, in turn, all
objects of that class, which
2774
03:17:26.125 --> 03:17:30.000
we've not seen a demonstration of,
but it's a higher level concept.
2775
03:17:30.000 --> 03:17:32.583
So it turns out, besides
these class methods,
2776
03:17:32.583 --> 03:17:35.625
which are distinct from those
instance methods, which, to be fair,
2777
03:17:35.625 --> 03:17:39.375
do not have their own decorator-- they
just are, by default, instance method,
2778
03:17:39.375 --> 03:17:42.458
there's yet other types of Methods
You can have in classes in Python.
2779
03:17:42.458 --> 03:17:44.875
They tend to be called static
methods, and they, too,
2780
03:17:44.875 --> 03:17:48.041
come with another decorator
called @static method, which
2781
03:17:48.041 --> 03:17:49.666
is a rabbit hole we won't go down.
2782
03:17:49.666 --> 03:17:52.125
But realize that there is
yet other functionality
2783
03:17:52.125 --> 03:17:54.750
that you can leverage within
object-oriented programming.
2784
03:17:54.750 --> 03:17:59.041
But what we thought we'd do is focus
really on some final core features
2785
03:17:59.041 --> 03:18:02.166
that you see not just in Python
but other languages as well.
2786
03:18:02.166 --> 03:18:05.750
And perhaps one of the most compelling
features of object-oriented programming
2787
03:18:05.750 --> 03:18:09.375
that we haven't yet used explicitly--
though it turns out we've seen
2788
03:18:09.375 --> 03:18:11.458
implicitly over the past weeks--
2789
03:18:11.458 --> 03:18:13.625
is this notion of inheritance.
2790
03:18:13.625 --> 03:18:16.250
It turns out, via
object-oriented programming,
2791
03:18:16.250 --> 03:18:19.666
there's actually an opportunity
to design your classes
2792
03:18:19.666 --> 03:18:25.125
in a hierarchical fashion, whereby
you can have one class inherit from
2793
03:18:25.125 --> 03:18:30.833
or borrow attributes-- that is,
methods or variables from another class
2794
03:18:30.833 --> 03:18:33.291
if they all have those in common.
2795
03:18:33.291 --> 03:18:35.083
So what do I mean by this here?
2796
03:18:35.083 --> 03:18:39.708
Well, let me propose that we
implement, over in VS Code here,
2797
03:18:39.708 --> 03:18:42.250
a brand new file called wizard.py.
2798
03:18:42.250 --> 03:18:45.583
Let me go ahead and
run code of wizard.py.
2799
03:18:45.583 --> 03:18:50.333
And then let's start as before,
defining a class called student.
2800
03:18:50.333 --> 03:18:54.416
And let's go ahead and first define
the underscore underscore, init method,
2801
03:18:54.416 --> 03:18:57.291
which of course, is minimally going
to take an argument traditionally
2802
03:18:57.291 --> 03:18:58.000
called self.
2803
03:18:58.000 --> 03:19:01.375
And in this case, let's also have it
take as before a name and a house.
2804
03:19:01.375 --> 03:19:03.875
And then in this init
method, let's go ahead
2805
03:19:03.875 --> 03:19:08.208
and assign the instance
variables-- self.name = name,
2806
03:19:08.208 --> 03:19:11.208
and self.house = house.
2807
03:19:11.208 --> 03:19:13.375
Let's assume that there's
some other functionality
2808
03:19:13.375 --> 03:19:15.083
in this class as well-- dot, dot, dot.
2809
03:19:15.083 --> 03:19:18.416
But let's move on now to implementing
the notion of a professor
2810
03:19:18.416 --> 03:19:20.208
in the wizarding world as well.
2811
03:19:20.208 --> 03:19:24.625
So for this class,
let's call it Professor.
2812
03:19:24.625 --> 03:19:26.958
And a professor, let's
say, is also going
2813
03:19:26.958 --> 03:19:29.416
to have its own initialization method.
2814
03:19:29.416 --> 03:19:30.708
So __ init.
2815
03:19:30.708 --> 03:19:32.000
It's going to take self--
2816
03:19:32.000 --> 03:19:33.791
always as the first argument.
2817
03:19:33.791 --> 03:19:35.500
A professor also has a name.
2818
03:19:35.500 --> 03:19:37.166
So we'll pass that in second, too.
2819
03:19:37.166 --> 03:19:39.958
And even though some
professors are heads of houses,
2820
03:19:39.958 --> 03:19:42.041
let's assume that a professor
is really identified
2821
03:19:42.041 --> 03:19:45.500
by their name and their subject
area-- the class that they teach.
2822
03:19:45.500 --> 03:19:48.041
So we'll call this
third argument, subject.
2823
03:19:48.041 --> 03:19:51.791
Now, as before, let's go ahead
and assign self.name = name,
2824
03:19:51.791 --> 03:19:56.083
and let's assign
self.subject = subject here.
2825
03:19:56.083 --> 03:19:59.541
And as before, let's assume that there's
some more functionality associated
2826
03:19:59.541 --> 03:20:01.083
with professors as well.
2827
03:20:01.083 --> 03:20:05.625
Well, what do you notice already
here in my definitions of students
2828
03:20:05.625 --> 03:20:07.333
and professors?
2829
03:20:07.333 --> 03:20:11.750
Typically, we're a bit reluctant to
allow for any redundancy in our code.
2830
03:20:11.750 --> 03:20:16.041
And here, I feel like my init method
is taking a name for students;
2831
03:20:16.041 --> 03:20:18.625
my init method is also taking
a name for a professor;
2832
03:20:18.625 --> 03:20:22.458
and I have these identical lines
of code, like self.name = name.
2833
03:20:22.458 --> 03:20:25.208
And this is only going to get
exacerbated if I now go and add
2834
03:20:25.208 --> 03:20:26.083
some error checking.
2835
03:20:26.083 --> 03:20:29.916
So for instance, how
about if not name, we
2836
03:20:29.916 --> 03:20:32.083
should probably be in the
habit of raising something
2837
03:20:32.083 --> 03:20:36.208
like a value error in an explanatory
message, like "Missing name."
2838
03:20:36.208 --> 03:20:37.166
And you know what?
2839
03:20:37.166 --> 03:20:39.750
If a professor is missing
their name, I should probably
2840
03:20:39.750 --> 03:20:41.291
copy, paste that code down here.
2841
03:20:41.291 --> 03:20:43.666
And that's where red
flags should be going off,
2842
03:20:43.666 --> 03:20:45.916
whereby, as soon as you
start copy pasting code,
2843
03:20:45.916 --> 03:20:50.208
there's probably a better way so that
we can write the code once and perhaps
2844
03:20:50.208 --> 03:20:51.500
reuse it in some way.
2845
03:20:51.500 --> 03:20:54.916
And here, too, object-oriented
programming offers a solution.
2846
03:20:54.916 --> 03:20:58.041
It turns out that object-oriented
programming in Python
2847
03:20:58.041 --> 03:21:03.458
also supports inheritance, whereby
you can define multiple classes that
2848
03:21:03.458 --> 03:21:05.125
somehow relate to one another.
2849
03:21:05.125 --> 03:21:07.833
They don't need to exist
in parallel in this way.
2850
03:21:07.833 --> 03:21:10.375
There could actually be
some hierarchy between them.
2851
03:21:10.375 --> 03:21:12.333
So for instance, in
the wizarding world, we
2852
03:21:12.333 --> 03:21:15.583
could argue that both a student and a
professor are, at the end of the day,
2853
03:21:15.583 --> 03:21:16.291
Wizards.
2854
03:21:16.291 --> 03:21:19.666
So maybe what we should really
define is a third class,
2855
03:21:19.666 --> 03:21:21.958
for instance, called
wizard, that has any
2856
03:21:21.958 --> 03:21:25.916
of the common attributes for
students and professors alike.
2857
03:21:25.916 --> 03:21:27.791
And for now, we've kept
it relatively simple.
2858
03:21:27.791 --> 03:21:30.666
The only thing they have in
common is a name and a name,
2859
03:21:30.666 --> 03:21:32.458
in student and professor, respectively.
2860
03:21:32.458 --> 03:21:35.416
So why don't we minimally
factor that out first?
2861
03:21:35.416 --> 03:21:37.083
All right, so let me go ahead here.
2862
03:21:37.083 --> 03:21:39.708
And just to keep things organized,
at the top of my file, let's
2863
03:21:39.708 --> 03:21:41.833
define a third class called Wizard.
2864
03:21:41.833 --> 03:21:45.208
And a wizard will have its
own initialization method.
2865
03:21:45.208 --> 03:21:49.541
So def __init__(self), as always.
2866
03:21:49.541 --> 03:21:52.333
And a wizard, let's say
for now, is only going
2867
03:21:52.333 --> 03:21:55.625
to be initialized with
their name in this way.
2868
03:21:55.625 --> 03:21:58.791
And now, I'm going to go ahead and
do some of that error checking.
2869
03:21:58.791 --> 03:22:02.708
So if not name will raise a
value error in the wizard class.
2870
03:22:02.708 --> 03:22:06.041
Otherwise, we'll go ahead
and do self name equals name,
2871
03:22:06.041 --> 03:22:09.666
and, heck, dot, dot, dot, maybe
some other functionality as well.
2872
03:22:09.666 --> 03:22:13.041
But not a subject, which is specific
to professors, and not a house,
2873
03:22:13.041 --> 03:22:15.166
which I've claimed is
specific to students.
2874
03:22:15.166 --> 03:22:18.375
Now, I think we can
begin to maybe remove
2875
03:22:18.375 --> 03:22:20.833
some of the redundancies
in our other classes here.
2876
03:22:20.833 --> 03:22:24.416
So for instance, down
with student, why don't I
2877
03:22:24.416 --> 03:22:28.750
go ahead and remove this error
checking here and remove this error--
2878
03:22:28.750 --> 03:22:32.166
this assignment of self.name
= name because I'm already
2879
03:22:32.166 --> 03:22:33.291
doing that in Wizard.
2880
03:22:33.291 --> 03:22:35.958
And similarly, down here, in
Professor, why don't I do the same?
2881
03:22:35.958 --> 03:22:37.583
Let's get rid of the error checking.
2882
03:22:37.583 --> 03:22:41.083
Let's get rid of self.name = name
because, again, I'm doing that already
2883
03:22:41.083 --> 03:22:43.041
up there for Wizard as well.
2884
03:22:43.041 --> 03:22:45.916
But at the moment, even though
they're all in the same file,
2885
03:22:45.916 --> 03:22:50.666
I haven't told Python that a student is
a wizard and a professor is a wizard.
2886
03:22:50.666 --> 03:22:53.000
So I really need to
link these two together.
2887
03:22:53.000 --> 03:22:57.500
And the way you can prescribe
inheritance, whereby one class should
2888
03:22:57.500 --> 03:23:00.041
inherit from another,
or conversely, one class
2889
03:23:00.041 --> 03:23:03.541
should descend from
another-- we can do this.
2890
03:23:03.541 --> 03:23:05.125
I can say class Student.
2891
03:23:05.125 --> 03:23:08.416
But before the colon, I can
go in and say in parentheses,
2892
03:23:08.416 --> 03:23:13.916
a student inherits from, or is a
subclass of wizard, which, conversely,
2893
03:23:13.916 --> 03:23:16.666
is the superclass of the student class.
2894
03:23:16.666 --> 03:23:19.583
So this just means that, when
I define a student class,
2895
03:23:19.583 --> 03:23:23.958
go ahead and inherit all of the
characteristics of a wizard as well.
2896
03:23:23.958 --> 03:23:26.875
And I'm going to do the
same thing for Professor.
2897
03:23:26.875 --> 03:23:30.166
So (Wizard) after the
class name Professor,
2898
03:23:30.166 --> 03:23:33.833
and that's going to give me access
to some of that same functionality.
2899
03:23:33.833 --> 03:23:37.458
But because my student
class and my professor class
2900
03:23:37.458 --> 03:23:39.416
still have their same
init methods, those
2901
03:23:39.416 --> 03:23:41.291
are the methods that
are going to get called.
2902
03:23:41.291 --> 03:23:44.916
Whenever I create a student in code
or I create a professor in code,
2903
03:23:44.916 --> 03:23:47.625
I need to somehow
explicitly say that I also
2904
03:23:47.625 --> 03:23:51.916
want to use the functionality in
the Wizard class's init method.
2905
03:23:51.916 --> 03:23:54.916
And the way to do this
in Python is as follows.
2906
03:23:54.916 --> 03:23:56.958
Let me go into my init
method for Student,
2907
03:23:56.958 --> 03:23:59.958
and let me call super,
with no arguments, which
2908
03:23:59.958 --> 03:24:03.333
is a reference to the
superclass of this class.
2909
03:24:03.333 --> 03:24:05.916
So if this class is Student,
the superclass-- that is,
2910
03:24:05.916 --> 03:24:07.791
the parent class-- is Wizard.
2911
03:24:07.791 --> 03:24:13.875
So super() will have the effect
of accessing the superclass.
2912
03:24:13.875 --> 03:24:17.750
And then I'm going to go ahead and
explicitly call its init method,
2913
03:24:17.750 --> 03:24:21.416
and I'm going to pass to
the Wizard's init method
2914
03:24:21.416 --> 03:24:24.958
the name that the student's
init method was passed.
2915
03:24:24.958 --> 03:24:28.000
And I'm going to go ahead and
do the same down here in Wizard.
2916
03:24:28.000 --> 03:24:29.666
This is one line of copy, paste.
2917
03:24:29.666 --> 03:24:32.375
But I think I'm OK with
it here because it's still
2918
03:24:32.375 --> 03:24:34.291
allowing me to do all
of the name assignment
2919
03:24:34.291 --> 03:24:37.000
and the error checking up
in the Wizard class instead.
2920
03:24:37.000 --> 03:24:42.250
I think we're OK now by just
calling super.init for both student
2921
03:24:42.250 --> 03:24:43.416
and Professor alike.
2922
03:24:43.416 --> 03:24:47.208
Now, admittedly, this syntax is
definitely out there-- the fact
2923
03:24:47.208 --> 03:24:49.541
that we're calling super
in parentheses and dots
2924
03:24:49.541 --> 03:24:52.458
and underscore underscore on the
left and the right of init here,
2925
03:24:52.458 --> 03:24:54.875
but it's just a combination
of these two ideas.
2926
03:24:54.875 --> 03:25:00.916
super() is a way of programmatically
accessing a current class's parent
2927
03:25:00.916 --> 03:25:05.291
class, or superclass, and __init, of
course, is just referring to, now,
2928
03:25:05.291 --> 03:25:08.041
that class's own initialization method.
2929
03:25:08.041 --> 03:25:09.958
Now, per the dot, dot,
dot-- so there could be
2930
03:25:09.958 --> 03:25:11.500
a lot more going on in these classes.
2931
03:25:11.500 --> 03:25:15.208
But what's nice now is
that Wizard as a class
2932
03:25:15.208 --> 03:25:18.291
is taking care of all of the
assignment of a wizard's name,
2933
03:25:18.291 --> 03:25:20.458
whether that wizard is a
student or a professor.
2934
03:25:20.458 --> 03:25:22.416
And it's even doing some
error checking to make
2935
03:25:22.416 --> 03:25:25.125
sure the name was actually passed in.
2936
03:25:25.125 --> 03:25:29.166
Meanwhile, student is inheriting
all of that functionality
2937
03:25:29.166 --> 03:25:32.333
and using it by calling the
superclass's own init method.
2938
03:25:32.333 --> 03:25:35.041
But it's additionally taking
the house, that's presumably
2939
03:25:35.041 --> 03:25:37.375
passed into the student
constructor function,
2940
03:25:37.375 --> 03:25:40.833
and assigning it to its own
instance variable-- self.house,
2941
03:25:40.833 --> 03:25:44.458
and similarly, professor,
or restoring in self.subject
2942
03:25:44.458 --> 03:25:47.875
the subject that was passed
into that one as well.
2943
03:25:47.875 --> 03:25:49.916
Now, how might we use these classes?
2944
03:25:49.916 --> 03:25:53.500
Well, we'll continue to wave our hands
with a little bit of detail here.
2945
03:25:53.500 --> 03:25:56.750
But at the bottom of this file, or
any other file that imports this one,
2946
03:25:56.750 --> 03:25:58.833
I could now write code like this.
2947
03:25:58.833 --> 03:26:01.208
I could create a student
variable and assign
2948
03:26:01.208 --> 03:26:03.458
it the return value of the
student constructor call.
2949
03:26:03.458 --> 03:26:07.625
and maybe that student is named
Harry and that student's house,
2950
03:26:07.625 --> 03:26:10.041
for instance, might be Gryffindor.
2951
03:26:10.041 --> 03:26:14.750
And meanwhile, I might do something like
this. professor = Professor over here.
2952
03:26:14.750 --> 03:26:18.541
And notice, the lowercase S on
the left, capital S on the right.
2953
03:26:18.541 --> 03:26:21.875
Same for professor on the left--
lowercase and uppercase on the right
2954
03:26:21.875 --> 03:26:22.750
respectively.
2955
03:26:22.750 --> 03:26:25.625
Professor, quote, unquote,
"Severus," and how
2956
03:26:25.625 --> 03:26:30.500
about Defense Against the
Dark Arts will be his subject?
2957
03:26:30.500 --> 03:26:32.791
And meanwhile, if we
want, more generically,
2958
03:26:32.791 --> 03:26:37.291
just a wizard, who, at the moment
is neither student nor professor
2959
03:26:37.291 --> 03:26:39.375
teaching classes actively,
we could even do that.
2960
03:26:39.375 --> 03:26:43.458
We could do wizard = Wizard in
capital W on the right-hand side
2961
03:26:43.458 --> 03:26:45.708
of the equal sign, because
it's the name of the class.
2962
03:26:45.708 --> 03:26:47.166
And someone like Albus--
2963
03:26:47.166 --> 03:26:49.958
passing in only Albus's name--
2964
03:26:49.958 --> 03:26:55.708
not a house, not a subject, because, in
this case, he's known only as a wizard.
2965
03:26:55.708 --> 03:26:58.750
Meanwhile, with each of these
calls, this line of code
2966
03:26:58.750 --> 03:27:01.833
here will ensure that the init method
for the wizard class is called.
2967
03:27:01.833 --> 03:27:05.583
This line of code here will ensure
that the init method of the student
2968
03:27:05.583 --> 03:27:09.250
class and, in turn, the init method
of the superclass wizard is called.
2969
03:27:09.250 --> 03:27:11.541
And then lastly, on
this final line of code,
2970
03:27:11.541 --> 03:27:15.625
will this syntax ensure that the
init method of the professor class
2971
03:27:15.625 --> 03:27:20.375
is called, which, in turn, calls the
init method of the superclass as well.
2972
03:27:20.375 --> 03:27:24.083
Any questions now on
this idea of inheritance,
2973
03:27:24.083 --> 03:27:29.125
which is a key feature of a lot of
object-oriented programming languages?
2974
03:27:29.125 --> 03:27:33.458
2975
03:27:33.458 --> 03:27:36.708
MICHAEL: From what I've seen so far, a
lot of times, there's a lot of nesting.
2976
03:27:36.708 --> 03:27:38.375
If you do super, does it go one up?
2977
03:27:38.375 --> 03:27:44.375
Is there any situation where it's nested
in another class as well, above Wizard,
2978
03:27:44.375 --> 03:27:44.875
let's say?
2979
03:27:44.875 --> 03:27:46.500
DAVID J. MALAN: A really good question.
2980
03:27:46.500 --> 03:27:48.375
If you were to have a
super superclass-- so
2981
03:27:48.375 --> 03:27:52.833
your hierarchy is even taller
than the two levels of hierarchy
2982
03:27:52.833 --> 03:27:56.625
that we currently have, absolutely.
2983
03:27:56.625 --> 03:27:59.250
What's nice about inheritance,
as the name implies, is,
2984
03:27:59.250 --> 03:28:02.583
just as you might have inherited
certain traits as a human
2985
03:28:02.583 --> 03:28:06.000
from your grandfather and
grandmother or your great-grandfather
2986
03:28:06.000 --> 03:28:08.333
or great-grandmother,
some of those properties
2987
03:28:08.333 --> 03:28:13.083
can actually trickle down to you
in the context of code as well.
2988
03:28:13.083 --> 03:28:17.291
So when you descend from another class--
2989
03:28:17.291 --> 03:28:21.541
that is, when you subclass a
superclass or a super superclass,
2990
03:28:21.541 --> 03:28:23.958
you actually do inherit
all of the functionality,
2991
03:28:23.958 --> 03:28:26.625
not just from one level above
you but from two or three,
2992
03:28:26.625 --> 03:28:29.583
so you can indeed access some
of that functionality as well.
2993
03:28:29.583 --> 03:28:32.666
And you can even override it if
you want some of these classes
2994
03:28:32.666 --> 03:28:35.750
to behave a little bit
differently than others.
2995
03:28:35.750 --> 03:28:38.125
Other questions on inheritance.
2996
03:28:38.125 --> 03:28:39.875
AUDIENCE: So it's
similar to the last one,
2997
03:28:39.875 --> 03:28:43.125
but can you have two
parents on the same level?
2998
03:28:43.125 --> 03:28:44.750
DAVID J. MALAN: A really good question.
2999
03:28:44.750 --> 03:28:49.875
So there are ways to implement
descendants from multiple parents.
3000
03:28:49.875 --> 03:28:53.125
And there's different ways to do this,
not just in Python but other languages.
3001
03:28:53.125 --> 03:28:57.916
We've kept things simple here, though,
by having a single inheritance path.
3002
03:28:57.916 --> 03:28:58.666
A good question.
3003
03:28:58.666 --> 03:29:01.291
How about one more
question on inheritance?
3004
03:29:01.291 --> 03:29:07.208
AUDIENCE: Can we have multiple
arguments in super.__init?
3005
03:29:07.208 --> 03:29:11.000
DAVID J. MALAN: Yes, but in this case,
I'm only passing a name on line 18,
3006
03:29:11.000 --> 03:29:13.666
and I'm only passing in name on line 10.
3007
03:29:13.666 --> 03:29:14.166
Why?
3008
03:29:14.166 --> 03:29:18.208
Because, on line 2, when I define
the init method for the Wizard class,
3009
03:29:18.208 --> 03:29:20.375
I only expect a single argument.
3010
03:29:20.375 --> 03:29:23.583
But I could absolutely have
other common functionality.
3011
03:29:23.583 --> 03:29:24.750
I could add in a patronus.
3012
03:29:24.750 --> 03:29:27.125
If both students and
professors have patronuses
3013
03:29:27.125 --> 03:29:30.958
that can come out of their wands,
I could have two arguments instead.
3014
03:29:30.958 --> 03:29:33.708
We've been using this feature
of object-oriented programming
3015
03:29:33.708 --> 03:29:37.000
now for quite some time
in the form of exceptions.
3016
03:29:37.000 --> 03:29:40.625
Indeed, if you look at the official
documentation for exceptions in Python,
3017
03:29:40.625 --> 03:29:43.208
you'll see that there's not even
the ones we've seen in class,
3018
03:29:43.208 --> 03:29:44.541
like value error and others.
3019
03:29:44.541 --> 03:29:48.666
There's any number of others as
well, but they are all, themselves,
3020
03:29:48.666 --> 03:29:50.125
hierarchical in nature.
3021
03:29:50.125 --> 03:29:54.416
This is just a subset of the available
exceptions that come built into Python.
3022
03:29:54.416 --> 03:29:58.125
And you can actually, as a programmer,
create your own exceptions as well.
3023
03:29:58.125 --> 03:30:01.916
But as this chart here
captures hierarchically,
3024
03:30:01.916 --> 03:30:06.125
all exceptions we've seen
thus far actually descend from
3025
03:30:06.125 --> 03:30:09.041
or inherit from superclasses already.
3026
03:30:09.041 --> 03:30:10.833
So for instance, at
the bottom of this list
3027
03:30:10.833 --> 03:30:13.250
here is ValueError, which
we've seen quite a bit.
3028
03:30:13.250 --> 03:30:17.583
And if you follow the line straight up
on this ascii rendition of this chart,
3029
03:30:17.583 --> 03:30:22.083
you'll see that ValueError has a parent,
class, or superclass, called exception.
3030
03:30:22.083 --> 03:30:25.958
And the exception class, meanwhile, has
a parent class called base exception.
3031
03:30:25.958 --> 03:30:28.125
Why did the authors of Python do this?
3032
03:30:28.125 --> 03:30:32.958
Well, it turns out that, whether you
have a value error or a key error
3033
03:30:32.958 --> 03:30:35.416
or an assertion error
or any number of others,
3034
03:30:35.416 --> 03:30:40.250
there's a lot of functionality common
to all of those types of errors
3035
03:30:40.250 --> 03:30:42.458
that you want--
3036
03:30:42.458 --> 03:30:44.958
that you want a programmer
to be able to use.
3037
03:30:44.958 --> 03:30:48.375
And so it turns out that the authors
of Python decided, you know what?
3038
03:30:48.375 --> 03:30:51.791
Let's not have a dozen
or more different classes
3039
03:30:51.791 --> 03:30:55.166
that all just have copy,
pasted similar functionality.
3040
03:30:55.166 --> 03:30:57.250
Let's create this
hierarchy so that, even
3041
03:30:57.250 --> 03:31:00.875
though the exceptions toward the
bottom of this list are very precise,
3042
03:31:00.875 --> 03:31:02.291
they at least inherit--
3043
03:31:02.291 --> 03:31:05.583
that is, borrow some very
common functionality up above.
3044
03:31:05.583 --> 03:31:09.541
So it turns out that, when you use the
Try and the Accept keyword in Python,
3045
03:31:09.541 --> 03:31:13.291
generally speaking, we've tried
to catch very specific exceptions,
3046
03:31:13.291 --> 03:31:14.291
like ValueError.
3047
03:31:14.291 --> 03:31:17.458
But technically, you could
capture the parents or even
3048
03:31:17.458 --> 03:31:20.375
the grandparent exception
for a given exception,
3049
03:31:20.375 --> 03:31:23.500
especially if you're not necessarily
sure which one is going to get raised.
3050
03:31:23.500 --> 03:31:27.041
Or, better yet, there could be
many exceptions that get raised,
3051
03:31:27.041 --> 03:31:28.958
but you want to handle
them all the same,
3052
03:31:28.958 --> 03:31:31.000
and you don't want to
necessarily enumerate them
3053
03:31:31.000 --> 03:31:33.041
in parentheses, separated by commas.
3054
03:31:33.041 --> 03:31:36.625
You want to say you want to handle
all exceptions of a certain superclass
3055
03:31:36.625 --> 03:31:38.416
in much the same way.
3056
03:31:38.416 --> 03:31:40.833
So this has been latent this
whole time, any time we've
3057
03:31:40.833 --> 03:31:45.166
seen or used or caught or, now, raised
exceptions, and built into Python
3058
03:31:45.166 --> 03:31:46.041
is this hierarchy.
3059
03:31:46.041 --> 03:31:47.916
And if you were to invent
your own exception,
3060
03:31:47.916 --> 03:31:50.250
generally, you wouldn't
want to start from scratch.
3061
03:31:50.250 --> 03:31:54.041
You would want to descend from-- that
is, subclass, one of these existing
3062
03:31:54.041 --> 03:31:58.958
exceptions and add your own twist on
it, your own functionality as well.
3063
03:31:58.958 --> 03:32:02.208
Well, there's one final feature
of object oriented programming
3064
03:32:02.208 --> 03:32:04.750
that we'd like to share
with you today, and then it
3065
03:32:04.750 --> 03:32:08.291
will perhaps be quite the eye opener
as to what you can really do now
3066
03:32:08.291 --> 03:32:10.208
that you have classes at your disposal.
3067
03:32:10.208 --> 03:32:12.791
And this, too, surprise,
has been a feature
3068
03:32:12.791 --> 03:32:15.458
you and I have been taking
for granted for weeks now.
3069
03:32:15.458 --> 03:32:18.250
This has just worked, but
it's been implemented in a way
3070
03:32:18.250 --> 03:32:20.208
that you can now leverage yourself.
3071
03:32:20.208 --> 03:32:23.166
It turns out that Python, and
some other languages, too,
3072
03:32:23.166 --> 03:32:27.041
support this notion of
operator overloading, whereby
3073
03:32:27.041 --> 03:32:32.250
you can take very common symbols, like
plus or minus or other such syntax
3074
03:32:32.250 --> 03:32:37.416
on the keyboard, and you can implement
your own interpretation thereof.
3075
03:32:37.416 --> 03:32:40.791
Plus does not have to equal addition.
3076
03:32:40.791 --> 03:32:43.291
And minus does not have
to equal subtraction.
3077
03:32:43.291 --> 03:32:45.708
And in fact, you and I have
already seen another context
3078
03:32:45.708 --> 03:32:48.541
in which plus means something else.
3079
03:32:48.541 --> 03:32:52.750
Plus has not always, in
Python, meant addition, per se.
3080
03:32:52.750 --> 03:32:56.458
What else has Python used plus for?
3081
03:32:56.458 --> 03:32:57.458
AUDIENCE: Concatenation?
3082
03:32:57.458 --> 03:32:58.875
DAVID J. MALAN: For concatenation.
3083
03:32:58.875 --> 03:33:02.875
For joining two strings, for adding
to a list can you use plus as well.
3084
03:33:02.875 --> 03:33:07.666
So plus has actually been, funny enough,
overloaded by the authors of Python
3085
03:33:07.666 --> 03:33:08.541
for us.
3086
03:33:08.541 --> 03:33:12.000
And so we can use the same symbol
in much the same way as addition
3087
03:33:12.000 --> 03:33:15.250
but with different data types to
solve slightly different problems.
3088
03:33:15.250 --> 03:33:18.250
Well, let me propose that we
go back over to VS Code here,
3089
03:33:18.250 --> 03:33:22.250
and let me go ahead and create a
new final file called vault.py.
3090
03:33:22.250 --> 03:33:24.000
So code of vault.py.
3091
03:33:24.000 --> 03:33:27.416
And let me propose that we
implement the idea of a vault
3092
03:33:27.416 --> 03:33:30.541
at Gringotts, keeping on
theme, wherein there's
3093
03:33:30.541 --> 03:33:32.291
a bank in the world of Harry Potter.
3094
03:33:32.291 --> 03:33:35.083
And within this bank,
families and individuals
3095
03:33:35.083 --> 03:33:38.958
have vaults containing all sorts
of money in the wizarding world.
3096
03:33:38.958 --> 03:33:41.583
And the type of money that exists
in the world of Harry Potter
3097
03:33:41.583 --> 03:33:44.791
are coins called galleons
and sickles and Knuts,
3098
03:33:44.791 --> 03:33:47.083
and those are in
descending order of value.
3099
03:33:47.083 --> 03:33:49.750
And so inside of a vault might
be a whole bunch of coins--
3100
03:33:49.750 --> 03:33:53.000
gold, silver, and bronze, essentially,
each in those denominations,
3101
03:33:53.000 --> 03:33:53.833
tucked away.
3102
03:33:53.833 --> 03:33:58.083
So how can I go about implementing,
first of all, the idea of a vault
3103
03:33:58.083 --> 03:34:02.375
so that I can store, for instance,
for Harry Potter, how much coinage
3104
03:34:02.375 --> 03:34:05.916
is in his family's vault,
or for Ron Weasley the same?
3105
03:34:05.916 --> 03:34:08.291
Well, let me go ahead
and vault.py and first
3106
03:34:08.291 --> 03:34:12.291
create a class called Vault, essentially
meant to represent a bank vault.
3107
03:34:12.291 --> 03:34:15.000
Perfect, another real
world, or fantasy world,
3108
03:34:15.000 --> 03:34:17.500
entity that I want to
represent with code.
3109
03:34:17.500 --> 03:34:19.958
I could use a tuple or
a list or a dictionary.
3110
03:34:19.958 --> 03:34:23.083
But again, I'm going to get a lot
more functionality with classes,
3111
03:34:23.083 --> 03:34:26.125
and we'll see one final
flourish with operators.
3112
03:34:26.125 --> 03:34:28.875
Inside of this vault class,
let's go ahead and do this.
3113
03:34:28.875 --> 03:34:32.625
Let me define my init method,
taking its first argument of self.
3114
03:34:32.625 --> 03:34:35.625
And let me define three
arguments to this.
3115
03:34:35.625 --> 03:34:38.541
When you create a
vault, in my code here,
3116
03:34:38.541 --> 03:34:41.750
I want to be able to initialize it with
some number of galleons, some number
3117
03:34:41.750 --> 03:34:43.375
of sickles and, some number of Knuts.
3118
03:34:43.375 --> 03:34:46.791
I want the user, the programmer,
to be able to pass in one or more
3119
03:34:46.791 --> 03:34:47.833
of those values ideally.
3120
03:34:47.833 --> 03:34:50.083
But they can be optional,
so I'll give them defaults.
3121
03:34:50.083 --> 03:34:53.000
So let's go ahead and define
a parameter called galleons,
3122
03:34:53.000 --> 03:34:57.041
whose default value will be 0; sickles,
whose default value will also be 0;
3123
03:34:57.041 --> 03:35:00.083
and knuts, whose default
value will be 0 as well.
3124
03:35:00.083 --> 03:35:04.541
So the programmer can pass in one or
two or three or even none of those,
3125
03:35:04.541 --> 03:35:07.125
and they'll all have
some implied defaults.
3126
03:35:07.125 --> 03:35:10.500
How do I want to remember those
values that are passed in?
3127
03:35:10.500 --> 03:35:11.541
Well, let me do this.
3128
03:35:11.541 --> 03:35:14.583
self.galleons = galleons.
3129
03:35:14.583 --> 03:35:17.416
And self.sickles = sickles.
3130
03:35:17.416 --> 03:35:20.708
And self.knuts = knuts.
3131
03:35:20.708 --> 03:35:24.541
And so I could add some error checking,
especially if you don't pass in.
3132
03:35:24.541 --> 03:35:27.708
A number I could turn these into
properties to do even more validation.
3133
03:35:27.708 --> 03:35:30.791
But let's keep it simple and, as
always, focus only on the new ideas.
3134
03:35:30.791 --> 03:35:33.291
So I'm just going to trust that
these values were passed in,
3135
03:35:33.291 --> 03:35:36.708
and I'm going to immediately assign
them to these instance variables.
3136
03:35:36.708 --> 03:35:38.416
What, now, do I want to do?
3137
03:35:38.416 --> 03:35:41.208
Well, let's come up with
a way of printing out
3138
03:35:41.208 --> 03:35:44.000
what is in someone's vault, ultimately.
3139
03:35:44.000 --> 03:35:45.291
But first let's do this.
3140
03:35:45.291 --> 03:35:50.083
Let's create a vault for the Potters by
creating, via assignment, a new vault.
3141
03:35:50.083 --> 03:35:55.125
And let's say that the potters have
100 galleons, 50 sickles, and 24 knuts.
3142
03:35:55.125 --> 03:35:58.541
And that's in that vault. And let's
print out, for instance, potter.
3143
03:35:58.541 --> 03:36:00.916
All right, let's run this
code and see how it works now.
3144
03:36:00.916 --> 03:36:05.083
Let me go ahead and run
Python of vault.py, Enter.
3145
03:36:05.083 --> 03:36:06.083
Seems to work.
3146
03:36:06.083 --> 03:36:08.041
No syntax errors or anything else.
3147
03:36:08.041 --> 03:36:10.791
But this is not very enlightening.
3148
03:36:10.791 --> 03:36:15.291
How do I fix this, thinking
back to what we've done before?
3149
03:36:15.291 --> 03:36:17.958
AUDIENCE: You have to use the __str.
3150
03:36:17.958 --> 03:36:18.958
DAVID J. MALAN: Exactly.
3151
03:36:18.958 --> 03:36:22.291
I need to use one of those special
methods that comes with classes
3152
03:36:22.291 --> 03:36:26.625
and define for myself how I want
a vault to be printed as a string.
3153
03:36:26.625 --> 03:36:27.916
So let me go ahead and do that.
3154
03:36:27.916 --> 03:36:32.541
Let me define the str method taking
in self as its sole argument here.
3155
03:36:32.541 --> 03:36:34.500
And let's just return
a very simple string
3156
03:36:34.500 --> 03:36:36.833
that just reveals what's in the vault.
3157
03:36:36.833 --> 03:36:39.958
So I'm going to return
a formatted f string,
3158
03:36:39.958 --> 03:36:44.166
inside of which is self.galleons
and then the word galleon,
3159
03:36:44.166 --> 03:36:45.875
so I know which those are.
3160
03:36:45.875 --> 03:36:49.458
Then let's do self.sickles, and
let's output the word, sickles.
3161
03:36:49.458 --> 03:36:53.083
And then lastly let's output
self.knuts, and then knuts here.
3162
03:36:53.083 --> 03:36:58.041
So I know, in this string, just
how many of each of those coins
3163
03:36:58.041 --> 03:37:01.666
I have in this particular family's
vault. All right, let me go ahead
3164
03:37:01.666 --> 03:37:06.166
and run Python of vault.py, changing
nothing else except the str method.
3165
03:37:06.166 --> 03:37:11.625
And now, , we, see indeed that Harry has
100 galleons, 50 sickles, and 25 knuts.
3166
03:37:11.625 --> 03:37:13.541
All right, well, let's
do one thing more here.
3167
03:37:13.541 --> 03:37:16.875
Below that, let's go ahead
and define a Weasley variable.
3168
03:37:16.875 --> 03:37:20.583
And Ron never seemed to have quite as
much money in the vault as did Harry.
3169
03:37:20.583 --> 03:37:24.541
So let's say that the Weasley
vault will have 25, 50, and 100.
3170
03:37:24.541 --> 03:37:27.041
So I'll just reverse the
order of those denominations,
3171
03:37:27.041 --> 03:37:29.541
rather than Harry's 100, 50, 25.
3172
03:37:29.541 --> 03:37:33.125
And now let me go ahead and
print Weasley like this.
3173
03:37:33.125 --> 03:37:37.291
And let's go ahead and clear my
terminal window, run Python of vault.py.
3174
03:37:37.291 --> 03:37:40.541
This time, that str method
will be invoked twice, once
3175
03:37:40.541 --> 03:37:42.416
for each of those vault objects.
3176
03:37:42.416 --> 03:37:44.666
And we'll see, indeed, that
the first one for Harry
3177
03:37:44.666 --> 03:37:48.125
has got 100, 50, and 25,
respectively, versus Ron's 25, 50,
3178
03:37:48.125 --> 03:37:50.583
and 100, respectively.
3179
03:37:50.583 --> 03:37:52.458
But now let's do something interesting.
3180
03:37:52.458 --> 03:37:56.333
Suppose that you wanted to combine
the contents of two vaults,
3181
03:37:56.333 --> 03:37:58.875
be it Harry's and Ron's
or any other two people.
3182
03:37:58.875 --> 03:38:02.083
How would you go about
doing this in code?
3183
03:38:02.083 --> 03:38:06.000
Well, if I wanted to combine the
vaults for someone, I could do this.
3184
03:38:06.000 --> 03:38:08.333
Well, I could do galleons equals--
3185
03:38:08.333 --> 03:38:13.958
let's do potter.galleons
+ weasley.galleons.
3186
03:38:13.958 --> 03:38:16.250
That gives me a variable
called galleons that has
3187
03:38:16.250 --> 03:38:19.041
the sum of Harry and Ron's galleons.
3188
03:38:19.041 --> 03:38:25.333
Let's next do sickles =
potter.sickles + weasley.sickles.
3189
03:38:25.333 --> 03:38:31.916
And then lastly, let's do knuts
= potter.knuts + weasley.knuts.
3190
03:38:31.916 --> 03:38:32.958
I've got three variables.
3191
03:38:32.958 --> 03:38:34.666
What can I now do with these values?
3192
03:38:34.666 --> 03:38:37.833
Well, let's create a third--
a new vault. Total will
3193
03:38:37.833 --> 03:38:41.416
be the name of this variable equals
a new vault, Capital V, notice.
3194
03:38:41.416 --> 03:38:43.916
And now, let's pass in
those three new variables--
3195
03:38:43.916 --> 03:38:47.000
galleons, sickles, and knuts.
3196
03:38:47.000 --> 03:38:49.625
And that's it, and let's
print out this total vault.
3197
03:38:49.625 --> 03:38:52.750
So we should now see three
vaults-- one for Harry, for Ron,
3198
03:38:52.750 --> 03:38:55.208
and the combination--
the addition of the two.
3199
03:38:55.208 --> 03:38:58.875
Let me go ahead and rerun Python
of vault.py, and there we have it.
3200
03:38:58.875 --> 03:39:04.875
What was 100, 50, 25 and 25, 50, and
100, combined through addition now,
3201
03:39:04.875 --> 03:39:08.250
is 125, 100, 125.
3202
03:39:08.250 --> 03:39:10.541
So pretty straightforward,
using techniques from weeks
3203
03:39:10.541 --> 03:39:14.541
ago, where we're just declaring a few
new variables and doing some addition.
3204
03:39:14.541 --> 03:39:18.208
But wouldn't it be cool if I
could do something like this?
3205
03:39:18.208 --> 03:39:20.541
Wouldn't it be cool if
I could just somehow,
3206
03:39:20.541 --> 03:39:24.541
not manually create my own vault and
do all of this annoying math up here--
3207
03:39:24.541 --> 03:39:30.541
what if I could just do potter + weasley
and get rid of all of this logic here?
3208
03:39:30.541 --> 03:39:34.750
Wouldn't it be nice if I
overload the operator--
3209
03:39:34.750 --> 03:39:37.666
we know as plus, just
like str does, just
3210
03:39:37.666 --> 03:39:42.041
like list does-- to allow me
to add two vaults together
3211
03:39:42.041 --> 03:39:43.875
on the left and the right.
3212
03:39:43.875 --> 03:39:48.250
Well, it turns out in Python and
through operator overloading,
3213
03:39:48.250 --> 03:39:50.250
there is a way to do just this.
3214
03:39:50.250 --> 03:39:52.708
If you consult the
documentation, there's
3215
03:39:52.708 --> 03:39:56.750
this and so many other special
methods that come with classes.
3216
03:39:56.750 --> 03:39:59.916
The third one we'll see
here is this one here--
3217
03:39:59.916 --> 03:40:03.291
__add__.
3218
03:40:03.291 --> 03:40:05.458
And you'll see that it very
generically is described
3219
03:40:05.458 --> 03:40:09.333
in the documentation is working for
any object, be it a vault or str
3220
03:40:09.333 --> 03:40:10.791
or a list or something else.
3221
03:40:10.791 --> 03:40:13.458
By convention, it's going to take
a first argument called self,
3222
03:40:13.458 --> 03:40:17.083
and then it's going to take some other
argument, by convention, called other.
3223
03:40:17.083 --> 03:40:20.083
self, in effect, is going
to be referring to whatever
3224
03:40:20.083 --> 03:40:22.250
object is on the left of a plus sign.
3225
03:40:22.250 --> 03:40:24.166
other is going to be
referring to whatever
3226
03:40:24.166 --> 03:40:26.291
is on the right-hand
side of a plus sign,
3227
03:40:26.291 --> 03:40:30.333
thereby giving us a way of
describing, in code, the operand
3228
03:40:30.333 --> 03:40:35.416
on the left and the operand on the
right of the operator, plus, in between.
3229
03:40:35.416 --> 03:40:37.958
That is to say, if I go
back to VS Code here,
3230
03:40:37.958 --> 03:40:42.083
what I'm trying to do is
implement support for this.
3231
03:40:42.083 --> 03:40:45.041
Well, let me try, without
writing any other code just yet--
3232
03:40:45.041 --> 03:40:47.541
Python of vault.py, Enter--
3233
03:40:47.541 --> 03:40:53.541
TypeError: unsupported operand
type(s) for +: 'Vault' and 'vault.'
3234
03:40:53.541 --> 03:40:56.750
That is to say Python, at this
moment, does not know what
3235
03:40:56.750 --> 03:40:59.083
it means to add two vaults together.
3236
03:40:59.083 --> 03:41:00.500
You and I might have an instinct.
3237
03:41:00.500 --> 03:41:03.958
Probably want to combine the galleons
and the sickles and the knuts
3238
03:41:03.958 --> 03:41:04.791
respectively.
3239
03:41:04.791 --> 03:41:05.958
Python doesn't know that.
3240
03:41:05.958 --> 03:41:08.291
It just knows that you have
a new class called Vault.
3241
03:41:08.291 --> 03:41:10.250
But let's teach Python to do this.
3242
03:41:10.250 --> 03:41:12.166
Let me clear my terminal window.
3243
03:41:12.166 --> 03:41:15.208
Let me scroll back up to the class
itself, where, at the moment,
3244
03:41:15.208 --> 03:41:18.250
I only have two special
methods-- init and str.
3245
03:41:18.250 --> 03:41:20.458
But let's add this third.
3246
03:41:20.458 --> 03:41:26.625
Let me go into the class here and define
__add__ and then specify its first
3247
03:41:26.625 --> 03:41:30.791
parameter as self, as before, and then
a second parameter for this particular
3248
03:41:30.791 --> 03:41:32.583
method called, by convention, other.
3249
03:41:32.583 --> 03:41:34.833
Now, as always, I could name
those parameters anything
3250
03:41:34.833 --> 03:41:37.000
I want, but I'm going to
stick with convention here.
3251
03:41:37.000 --> 03:41:39.041
And now, inside of
this method, am I going
3252
03:41:39.041 --> 03:41:42.375
to have to now add together
the contents of two vaults?
3253
03:41:42.375 --> 03:41:43.333
Well, what two vaults?
3254
03:41:43.333 --> 03:41:46.291
Well, if we scroll down to our
goal at hand, the goal, of course,
3255
03:41:46.291 --> 03:41:48.583
is to add this vault plus
this other vault-- potter
3256
03:41:48.583 --> 03:41:50.208
plus weasley, respectively.
3257
03:41:50.208 --> 03:41:53.958
Well, it turns out, in Python, that,
when you do overload an operator like
3258
03:41:53.958 --> 03:41:56.291
plus, what's going to
happen automatically,
3259
03:41:56.291 --> 03:42:01.666
as soon as Python sees that, is it's
going to call that __add__ method,
3260
03:42:01.666 --> 03:42:04.041
and it's going to pass
into it to arguments--
3261
03:42:04.041 --> 03:42:07.333
whatever the operand is on the
left-- potter, in this case--
3262
03:42:07.333 --> 03:42:10.666
and whatever the operand is on
the right-- weasley, in this case.
3263
03:42:10.666 --> 03:42:15.500
And those values are going to get passed
in as self and other, respectively.
3264
03:42:15.500 --> 03:42:18.375
What that means is that we can
access their contents up here
3265
03:42:18.375 --> 03:42:20.458
in our implementation of add as follows.
3266
03:42:20.458 --> 03:42:24.125
Let me go ahead and define a local
variable called galleons and set that
3267
03:42:24.125 --> 03:42:27.500
equal to, for instance,
the sum of self.galleons--
3268
03:42:27.500 --> 03:42:30.125
whatever's in Potter's
vault in this case,
3269
03:42:30.125 --> 03:42:33.041
plus whatever is in Wesley's
vault in this case, which
3270
03:42:33.041 --> 03:42:35.000
would be other.galleons.
3271
03:42:35.000 --> 03:42:36.708
Let me do the same for sickles.
3272
03:42:36.708 --> 03:42:40.125
self.sickles + other.sickles.
3273
03:42:40.125 --> 03:42:41.958
And let me lastly do that for knuts.
3274
03:42:41.958 --> 03:42:45.791
So self.knuts + other.knuts.
3275
03:42:45.791 --> 03:42:47.625
But at the end of the
day, I'm going to need
3276
03:42:47.625 --> 03:42:51.500
to return a brand new bigger vault
that contains all of those contents
3277
03:42:51.500 --> 03:42:52.250
together.
3278
03:42:52.250 --> 03:42:56.791
And if we ultimately want to assign that
bigger vault to a variable like total
3279
03:42:56.791 --> 03:43:01.083
here, on the left, we'd better
return a value from this add method.
3280
03:43:01.083 --> 03:43:04.791
So I'm going to go ahead and give myself
a brand new vault, as by returning
3281
03:43:04.791 --> 03:43:08.916
capital Vault, which of course, is going
to call my vault function into which
3282
03:43:08.916 --> 03:43:11.750
I can now pass some of those
initialization arguments.
3283
03:43:11.750 --> 03:43:13.625
Well, how many galleon,
sickles, and knuts do
3284
03:43:13.625 --> 03:43:15.541
I want this brand new vault to contain?
3285
03:43:15.541 --> 03:43:19.666
Well, I want it to contain this
many galleons this many sickles,
3286
03:43:19.666 --> 03:43:21.291
and this many knuts.
3287
03:43:21.291 --> 03:43:23.708
So ultimately, what we're
doing in this implementation
3288
03:43:23.708 --> 03:43:27.791
of add is adding together those
galleons, sickles, and knuts, passing
3289
03:43:27.791 --> 03:43:30.958
them to the vault function so that
we get a brand new bigger vault,
3290
03:43:30.958 --> 03:43:33.833
and return that altogether.
3291
03:43:33.833 --> 03:43:36.041
So now I've defined
this new special method
3292
03:43:36.041 --> 03:43:42.250
called add that should now just
make plus work for two vaults.
3293
03:43:42.250 --> 03:43:42.833
Let's see.
3294
03:43:42.833 --> 03:43:46.750
Let me run down to my terminal window,
Python of vault.py and hit Enter.
3295
03:43:46.750 --> 03:43:51.958
And voila, and now we've implemented
an overloaded operator, plus,
3296
03:43:51.958 --> 03:43:54.166
to do what you and I
as humans would hope
3297
03:43:54.166 --> 03:43:56.458
would be the case when you
add two vaults together.
3298
03:43:56.458 --> 03:43:58.916
But I've now written the
code more specifically
3299
03:43:58.916 --> 03:44:04.500
to teach Python what it means
concretely to add two vaults together.
3300
03:44:04.500 --> 03:44:06.708
And it's with very
similar code in effect,
3301
03:44:06.708 --> 03:44:09.541
underneath the hood, that Python
is doing this for two strings,
3302
03:44:09.541 --> 03:44:13.875
to concatenate them together, to joining
two lists into a new list with list,
3303
03:44:13.875 --> 03:44:16.875
and so many other classes as well.
3304
03:44:16.875 --> 03:44:22.166
Any questions now on operator
overloading or this example here.
3305
03:44:22.166 --> 03:44:23.875
AUDIENCE: How would
you go about creating
3306
03:44:23.875 --> 03:44:31.833
a function for adding a student and
a vault for two separate classes?
3307
03:44:31.833 --> 03:44:33.250
Would that be possible?
3308
03:44:33.250 --> 03:44:35.125
DAVID J. MALAN: Let me
see what happens here.
3309
03:44:35.125 --> 03:44:36.041
I don't know offhand.
3310
03:44:36.041 --> 03:44:36.875
Let's do this.
3311
03:44:36.875 --> 03:44:38.541
Let's create a str and see what happens.
3312
03:44:38.541 --> 03:44:40.875
If I add Potter plus a str--
3313
03:44:40.875 --> 03:44:41.500
str object.
3314
03:44:41.500 --> 03:44:42.666
Yeah, so it would work.
3315
03:44:42.666 --> 03:44:44.708
I'm just figuring this
out as I go here, Eric.
3316
03:44:44.708 --> 03:44:47.000
So just to be clear,
what I did was I just
3317
03:44:47.000 --> 03:44:48.958
changed weasley to str
just to see what would
3318
03:44:48.958 --> 03:44:54.166
happen when I add a vault plus a
str, and it will work, theoretically.
3319
03:44:54.166 --> 03:44:54.791
Why?
3320
03:44:54.791 --> 03:45:01.541
Because so long as the type of value on
the left has an add method implemented,
3321
03:45:01.541 --> 03:45:04.291
other can be any type that you want.
3322
03:45:04.291 --> 03:45:06.583
You just have to decide
and code what it's
3323
03:45:06.583 --> 03:45:09.625
going to mean conceptually to
add a vault plus a string, which,
3324
03:45:09.625 --> 03:45:13.458
in this case, probably doesn't make
any sense at all, but it's possible.
3325
03:45:13.458 --> 03:45:15.208
It's going to be the
operand on the left.
3326
03:45:15.208 --> 03:45:16.250
And I'm inferring that.
3327
03:45:16.250 --> 03:45:17.958
I did not know the answer a moment ago.
3328
03:45:17.958 --> 03:45:22.916
I'm inferring that because what I got
was an attribute error here on line 11
3329
03:45:22.916 --> 03:45:27.125
because Python did not like
this. other.galleons didn't work,
3330
03:45:27.125 --> 03:45:29.666
but I could make it work
by figuring something out.
3331
03:45:29.666 --> 03:45:30.791
Really good question.
3332
03:45:30.791 --> 03:45:32.291
Didn't know that one myself.
3333
03:45:32.291 --> 03:45:35.916
Other questions on operator overloading?
3334
03:45:35.916 --> 03:45:38.291
AUDIENCE: Can you define
new operators in Python?
3335
03:45:38.291 --> 03:45:39.666
DAVID J. MALAN: I don't think so.
3336
03:45:39.666 --> 03:45:44.583
There is a very long but precise list
of operators that you can overload.
3337
03:45:44.583 --> 03:45:48.500
I do not believe you can
assign arbitrary characters
3338
03:45:48.500 --> 03:45:50.541
to be operators in Python.
3339
03:45:50.541 --> 03:45:52.458
Let me defer to Carter in the chat to--
3340
03:45:52.458 --> 03:45:55.166
OK, I'm seeing two of my colleagues
are saying, no, not possible.
3341
03:45:55.166 --> 03:45:57.166
So I'm going to go with
my first instinct, no.
3342
03:45:57.166 --> 03:45:58.583
Otherwise, that'd be kind of cool.
3343
03:45:58.583 --> 03:46:00.875
You could make emoji do
whatever you want to.
3344
03:46:00.875 --> 03:46:04.000
How about one final question
on operator overloading?
3345
03:46:04.000 --> 03:46:08.416
AUDIENCE: Is that the only
operation you can do as far as--
3346
03:46:08.416 --> 03:46:10.041
can you do a subtraction as well?
3347
03:46:10.041 --> 03:46:11.041
DAVID J. MALAN: You can.
3348
03:46:11.041 --> 03:46:13.583
You can do so many others let me.
3349
03:46:13.583 --> 03:46:16.583
If, Carter, you don't mind
pulling up this URL here--
3350
03:46:16.583 --> 03:46:19.750
so this link here-- special
method names and today's slides,
3351
03:46:19.750 --> 03:46:23.208
you'll see a long list of all of
the operators that you can overload.
3352
03:46:23.208 --> 03:46:26.583
You can do less than, equals
than, plus equals, minus equals.
3353
03:46:26.583 --> 03:46:29.291
Pretty much any symbol you've
seen me type on the screen
3354
03:46:29.291 --> 03:46:33.166
can be overloaded in
the context of classes.
3355
03:46:33.166 --> 03:46:35.541
So even though, today,
we focused entirely
3356
03:46:35.541 --> 03:46:38.625
on object-oriented programming, this
is a technique that we've been using,
3357
03:46:38.625 --> 03:46:41.791
really, since the first week of
the class because those ints,
3358
03:46:41.791 --> 03:46:45.291
those strs, those floats, those
lists, those dictionaries,
3359
03:46:45.291 --> 03:46:49.083
and so much more were already underneath
the hood this whole time-- classes
3360
03:46:49.083 --> 03:46:50.125
and objects thereof.
3361
03:46:50.125 --> 03:46:52.041
But you now, as a
programmer, have the ability
3362
03:46:52.041 --> 03:46:55.583
to create your own classes with your
own instance or class variables,
3363
03:46:55.583 --> 03:46:58.875
with your own instance or class
methods, with your own properties,
3364
03:46:58.875 --> 03:47:01.958
and even with your own custom
behavior for operators.
3365
03:47:01.958 --> 03:47:04.625
So ultimately, you can
absolutely continue
3366
03:47:04.625 --> 03:47:09.291
using those simple tuples or lists or
those dictionaries or other structures
3367
03:47:09.291 --> 03:47:09.791
as well.
3368
03:47:09.791 --> 03:47:13.416
But object-oriented programming, and
with it, classes and now these objects
3369
03:47:13.416 --> 03:47:15.208
is just another tool in your toolkit.
3370
03:47:15.208 --> 03:47:18.041
And daresay, as your code
gets more sophisticated
3371
03:47:18.041 --> 03:47:20.041
and your problems get
bigger, you'll find
3372
03:47:20.041 --> 03:47:23.708
that being able to model these real
world or even fantasy world entities
3373
03:47:23.708 --> 03:47:26.458
with classes and related
data and functionality
3374
03:47:26.458 --> 03:47:30.583
will ultimately just allow you to define
code that's not just correct but ever
3375
03:47:30.583 --> 03:47:32.833
well-designed as well.
3376
03:47:32.833 --> 03:47:35.541
This was CS50.
3377
03:47:35.541 --> 03:47:41.000