WEBVTT

00:00:00.790 --> 00:00:03.190
The following content is
provided under a Creative

00:00:03.190 --> 00:00:04.730
Commons license.

00:00:04.730 --> 00:00:07.030
Your support will help
MIT OpenCourseWare

00:00:07.030 --> 00:00:11.390
continue to offer high-quality
educational resources for free.

00:00:11.390 --> 00:00:13.990
To make a donation or
view additional materials

00:00:13.990 --> 00:00:17.880
from hundreds of MIT courses,
visit MIT OpenCourseWare

00:00:17.880 --> 00:00:18.840
at ocw.mit.edu.

00:00:31.789 --> 00:00:33.830
ANA BELL: All right,
everyone, let's get started.

00:00:37.670 --> 00:00:40.100
So today is going to
be the second lecture

00:00:40.100 --> 00:00:41.600
on object-oriented programming.

00:00:41.600 --> 00:00:44.930
So just a quick recap of
last time-- on Monday,

00:00:44.930 --> 00:00:47.907
we saw-- we were
introduced to this idea

00:00:47.907 --> 00:00:49.490
of object-oriented
programming, and we

00:00:49.490 --> 00:00:52.230
saw these things called
abstract data types.

00:00:52.230 --> 00:00:54.350
And these abstract data
types we implemented

00:00:54.350 --> 00:00:57.170
through Python classes.

00:00:57.170 --> 00:01:01.400
And they allowed us to
create our own data types

00:01:01.400 --> 00:01:06.001
that sort of abstracted a
general object of our choosing,

00:01:06.001 --> 00:01:06.500
right?

00:01:06.500 --> 00:01:09.099
So we've used lists
before, for example.

00:01:09.099 --> 00:01:10.640
But with abstract
data types, we were

00:01:10.640 --> 00:01:15.110
able to create objects
that were of our own types.

00:01:15.110 --> 00:01:16.430
We saw the coordinate example.

00:01:16.430 --> 00:01:17.804
And then at the
end of the class,

00:01:17.804 --> 00:01:19.104
we saw the fraction example.

00:01:19.104 --> 00:01:21.020
So today we're going to
talk a little bit more

00:01:21.020 --> 00:01:23.060
about object-oriented
programming and classes.

00:01:23.060 --> 00:01:25.730
We're going to see
a few more examples.

00:01:25.730 --> 00:01:32.000
And we're going to talk about
a few other nuances of classes,

00:01:32.000 --> 00:01:35.217
talk about information
hiding and class variables.

00:01:35.217 --> 00:01:36.800
And in the second
half of the lecture,

00:01:36.800 --> 00:01:39.480
we're going to talk about
the idea of inheritance.

00:01:39.480 --> 00:01:42.710
So we're going to use
object-oriented programming

00:01:42.710 --> 00:01:44.877
to simulate how real life works.

00:01:44.877 --> 00:01:46.460
So in real life, you
have inheritance.

00:01:46.460 --> 00:01:49.010
And in object-oriented
programming,

00:01:49.010 --> 00:01:52.480
you can also simulate that.

00:01:52.480 --> 00:01:54.219
OK, so the first
few slides are going

00:01:54.219 --> 00:01:56.760
to be a little bit of recap just
to make sure that everyone's

00:01:56.760 --> 00:01:58.920
on the same page
before I introduce

00:01:58.920 --> 00:02:02.130
a couple of new concepts
related to classes.

00:02:02.130 --> 00:02:05.010
So recall that when--
in the last lecture,

00:02:05.010 --> 00:02:07.240
we talked about
writing code from two

00:02:07.240 --> 00:02:08.490
different perspectives, right?

00:02:08.490 --> 00:02:12.450
The first was from someone who
wanted to implement a class.

00:02:12.450 --> 00:02:17.810
So implementing the class meant
defining your own object type.

00:02:17.810 --> 00:02:21.210
So you defined the object type
when you defined the class.

00:02:21.210 --> 00:02:24.680
And then you decided what
data attributes you wanted

00:02:24.680 --> 00:02:26.510
to define in your object.

00:02:26.510 --> 00:02:29.180
So what data makes
up the object?

00:02:29.180 --> 00:02:32.740
What is the object, OK?

00:02:32.740 --> 00:02:34.500
In addition to data
attributes, we also

00:02:34.500 --> 00:02:37.800
saw these things called methods.

00:02:37.800 --> 00:02:40.110
And methods were
ways to tell someone

00:02:40.110 --> 00:02:41.827
how to use your data type.

00:02:41.827 --> 00:02:44.160
So what are ways that someone
can interact with the data

00:02:44.160 --> 00:02:45.489
type, OK?

00:02:45.489 --> 00:02:47.280
So that's from the
point of view of someone

00:02:47.280 --> 00:02:49.650
who wants to write
their own object type.

00:02:49.650 --> 00:02:53.470
So you're implementing a class.

00:02:53.470 --> 00:02:56.536
And the other perspective
was to write code

00:02:56.536 --> 00:02:57.910
from the point of
view of someone

00:02:57.910 --> 00:03:01.450
who wanted to use a class
that was already written, OK?

00:03:01.450 --> 00:03:04.690
So this involved creating
instances of objects.

00:03:04.690 --> 00:03:07.980
So you're using the object type.

00:03:07.980 --> 00:03:09.660
Once you created
instances of objects,

00:03:09.660 --> 00:03:11.320
you were able to do
operations on them.

00:03:11.320 --> 00:03:13.560
So you were able to see
what methods whoever

00:03:13.560 --> 00:03:15.420
implemented the class added.

00:03:15.420 --> 00:03:17.160
And then, you can
use those methods

00:03:17.160 --> 00:03:24.450
in order to do operations
with your instances.

00:03:24.450 --> 00:03:27.080
So just looking at the
coordinate example we

00:03:27.080 --> 00:03:29.060
saw last time, a little
bit more in detail

00:03:29.060 --> 00:03:32.590
about what that meant--
so we had a class

00:03:32.590 --> 00:03:36.800
definition of an object
type, which included deciding

00:03:36.800 --> 00:03:38.950
what the class name was.

00:03:38.950 --> 00:03:40.990
And the class name
basically told

00:03:40.990 --> 00:03:43.780
Python what type of an
object this was, OK?

00:03:43.780 --> 00:03:45.610
In this case, we
decided we wanted

00:03:45.610 --> 00:03:49.060
to name a coordinate-- we wanted
to create a Coordinate object.

00:03:49.060 --> 00:03:51.460
And the type of this
object was therefore

00:03:51.460 --> 00:03:53.920
going to be a coordinate.

00:03:53.920 --> 00:03:57.220
We defined the class in the
sort of general way, OK?

00:03:57.220 --> 00:03:59.890
So we needed a way to
be able to access data

00:03:59.890 --> 00:04:02.950
attributes of any instance.

00:04:02.950 --> 00:04:06.565
So we use this
self variable, OK?

00:04:06.565 --> 00:04:09.910
And the self variable
we used to refer

00:04:09.910 --> 00:04:15.180
to any instance-- to the data
attributes of any instance

00:04:15.180 --> 00:04:16.769
in a general way
without actually

00:04:16.769 --> 00:04:20.070
having a particular
instance in mind, OK?

00:04:20.070 --> 00:04:21.959
So whenever we access
data attributes,

00:04:21.959 --> 00:04:26.230
we would say something like self
dot to access a data attribute.

00:04:26.230 --> 00:04:29.160
You'd access the attribute
directly with self.x.

00:04:29.160 --> 00:04:30.690
Or if you wanted
to access a method,

00:04:30.690 --> 00:04:33.149
you would say self, dot,
and then the method name--

00:04:33.149 --> 00:04:34.065
for example, distance.

00:04:37.350 --> 00:04:39.870
And really, the bottom line
of the class definition

00:04:39.870 --> 00:04:42.912
is that your class defines
all of the data-- so data

00:04:42.912 --> 00:04:44.370
attributes-- and
all of the methods

00:04:44.370 --> 00:04:47.190
that are going to be common
across all of the instances.

00:04:47.190 --> 00:04:52.410
So any instance that you create
of a particular object type,

00:04:52.410 --> 00:04:58.490
that instance is going to have
this exact same structure, OK?

00:04:58.490 --> 00:05:02.570
The difference is that
every instance's values

00:05:02.570 --> 00:05:05.270
are going to be different.

00:05:05.270 --> 00:05:08.200
So when you're creating
instances of classes,

00:05:08.200 --> 00:05:11.770
you can create more than one
instance of the same class.

00:05:11.770 --> 00:05:14.470
So we can create a
Coordinate object here

00:05:14.470 --> 00:05:17.650
using this syntax right here.

00:05:17.650 --> 00:05:22.030
So you say the type, and then,
whatever values it takes in.

00:05:22.030 --> 00:05:24.380
And you can create more
than one Coordinate object.

00:05:27.570 --> 00:05:30.150
Each Coordinate object is
going to have different data

00:05:30.150 --> 00:05:32.430
attributes.

00:05:32.430 --> 00:05:34.610
Sorry, it's going to have
different data attribute

00:05:34.610 --> 00:05:36.740
values, OK?

00:05:36.740 --> 00:05:39.650
Every Coordinate object is
going to have an x value and a y

00:05:39.650 --> 00:05:41.530
value.

00:05:41.530 --> 00:05:45.280
But the x and y values
among different instances

00:05:45.280 --> 00:05:47.680
are going to vary, OK?

00:05:47.680 --> 00:05:50.240
So that's the difference
between defining a class

00:05:50.240 --> 00:05:52.240
and looking at a particular
instance of a class.

00:05:52.240 --> 00:05:54.370
So instances have the
structure of the class.

00:05:54.370 --> 00:05:56.950
So for a coordinate,
all instances

00:05:56.950 --> 00:05:59.740
have an x value and a y value.

00:05:59.740 --> 00:06:01.840
But the actual values
are going to vary

00:06:01.840 --> 00:06:04.340
between the different instances.

00:06:04.340 --> 00:06:06.580
OK, so ultimately,
why do we want

00:06:06.580 --> 00:06:08.170
to use object-oriented
programming?

00:06:08.170 --> 00:06:12.440
So, so far, the
examples that we've seen

00:06:12.440 --> 00:06:16.520
were numerical, right--
a coordinate, a fraction.

00:06:16.520 --> 00:06:19.640
But using object-oriented
programming,

00:06:19.640 --> 00:06:24.730
you can create objects
that mimic real life.

00:06:24.730 --> 00:06:29.650
So if I wanted to create
objects of-- an object that

00:06:29.650 --> 00:06:32.230
defined a cat and an object
that defined a rabbit,

00:06:32.230 --> 00:06:34.930
I could do that with
object-oriented programming.

00:06:34.930 --> 00:06:38.140
I would just have to decide,
as a programmer, what

00:06:38.140 --> 00:06:40.350
data and what methods
I'd want to assign

00:06:40.350 --> 00:06:45.210
to these groups of objects, OK?

00:06:45.210 --> 00:06:48.840
So using object-oriented
programming, each one of these

00:06:48.840 --> 00:06:51.270
is considered a
different object.

00:06:51.270 --> 00:06:53.370
And as a different
object, I can decide

00:06:53.370 --> 00:06:56.280
that a cat is going to have
a name, an age, and maybe

00:06:56.280 --> 00:06:57.950
a color associated with it.

00:07:00.872 --> 00:07:02.330
And these three
here, on the right,

00:07:02.330 --> 00:07:04.587
each one of these rabbits
is also an object.

00:07:04.587 --> 00:07:06.170
And I'm going to
decide that I'm going

00:07:06.170 --> 00:07:11.710
to represent a rabbit by
just an age and a color, OK?

00:07:11.710 --> 00:07:15.990
And with object-oriented
programming,

00:07:15.990 --> 00:07:21.600
using these
attributes, I can group

00:07:21.600 --> 00:07:24.790
these three objects together and
these three objects together,

00:07:24.790 --> 00:07:25.290
OK?

00:07:29.500 --> 00:07:33.070
So I'm grouping
sets of objects that

00:07:33.070 --> 00:07:37.190
are going to have the
same attributes together.

00:07:37.190 --> 00:07:40.230
And attributes-- this is also
a recap of last time-- come

00:07:40.230 --> 00:07:42.090
in two forms, right,
data attributes

00:07:42.090 --> 00:07:44.900
and procedural attributes.

00:07:44.900 --> 00:07:49.280
So the data attributes
are basically

00:07:49.280 --> 00:07:52.760
things that define
what the object is.

00:07:52.760 --> 00:07:56.307
So how do you represent
a cat as an object?

00:07:56.307 --> 00:07:57.890
And it's up to you,
as the programmer,

00:07:57.890 --> 00:07:59.750
to decide how you
want to do that.

00:07:59.750 --> 00:08:03.950
For a coordinate, it was
pretty straightforward.

00:08:03.950 --> 00:08:06.529
You had an x and a y value.

00:08:06.529 --> 00:08:09.070
If we're representing something
more abstract like an animal,

00:08:09.070 --> 00:08:10.445
then maybe I would
say, well, I'm

00:08:10.445 --> 00:08:12.844
going to represent an animal
by an age and a name, OK?

00:08:12.844 --> 00:08:14.260
So it's really up
to you to decide

00:08:14.260 --> 00:08:20.980
how you want to represent--
what data attributes you want

00:08:20.980 --> 00:08:24.550
to represent your object with.

00:08:24.550 --> 00:08:28.690
Procedural attributes were
also known as methods.

00:08:28.690 --> 00:08:33.020
And the methods are
essentially asking,

00:08:33.020 --> 00:08:35.210
what can your object do, OK?

00:08:35.210 --> 00:08:37.789
So how can someone who wants
to use your object-- how

00:08:37.789 --> 00:08:40.010
can someone interact with it?

00:08:40.010 --> 00:08:41.659
So for a coordinate,
we saw that you

00:08:41.659 --> 00:08:43.820
could find the distance
between two coordinates.

00:08:43.820 --> 00:08:47.180
Maybe for our abstract
Animal object,

00:08:47.180 --> 00:08:49.700
you might have it
make a sound, OK,

00:08:49.700 --> 00:08:53.920
by maybe printing to the
screen or something like that.

00:08:53.920 --> 00:08:58.800
OK, this slide's also a recap
of how to create a class just

00:08:58.800 --> 00:09:01.710
to make sure everyone's on
the same page before we go on.

00:09:01.710 --> 00:09:04.770
So we defined a class
using this class keyword.

00:09:04.770 --> 00:09:06.590
And we said, class,
the name of the class.

00:09:06.590 --> 00:09:09.677
So now we're going to create
a more abstract Animal class.

00:09:09.677 --> 00:09:11.260
We're going to see,
in the second half

00:09:11.260 --> 00:09:13.390
of the lecture, what it
means to put something

00:09:13.390 --> 00:09:14.390
else in the parentheses.

00:09:14.390 --> 00:09:23.620
But for now, we say that an
animal is an object in Python.

00:09:23.620 --> 00:09:26.650
So that means it's going to
have all of the properties

00:09:26.650 --> 00:09:29.740
that any other
object in Python has.

00:09:29.740 --> 00:09:31.210
And as we're
creating this animal,

00:09:31.210 --> 00:09:34.390
we're going to
define how to create

00:09:34.390 --> 00:09:36.310
an instance of this class.

00:09:36.310 --> 00:09:38.050
So we say def.

00:09:38.050 --> 00:09:40.540
And this __init__ was the
special method that told Python

00:09:40.540 --> 00:09:43.496
how to create an object.

00:09:43.496 --> 00:09:44.870
Inside the
parentheses, remember,

00:09:44.870 --> 00:09:47.000
we have the self,
which is a variable

00:09:47.000 --> 00:09:49.967
that we use to refer to any
instance of the class, OK?

00:09:49.967 --> 00:09:51.800
We don't have a particular
instance in mind,

00:09:51.800 --> 00:09:55.190
we just want to be able to
refer to any instance, OK?

00:09:55.190 --> 00:09:58.442
So we use this self variable.

00:09:58.442 --> 00:09:59.900
And then, the second
parameter here

00:09:59.900 --> 00:10:03.650
is going to represent
what other data we use

00:10:03.650 --> 00:10:05.070
to initialize our object with.

00:10:05.070 --> 00:10:06.486
So in this case,
I'm going to say,

00:10:06.486 --> 00:10:09.110
I'm going to initialize an
Animal object with an age, OK?

00:10:09.110 --> 00:10:13.149
So when I create an animal,
I need to give it an age.

00:10:13.149 --> 00:10:15.440
Inside the __init__ are any
initializations that I want

00:10:15.440 --> 00:10:16.910
to make.

00:10:16.910 --> 00:10:19.580
So the first thing is, I'm
going to assign an instance

00:10:19.580 --> 00:10:22.700
variable, age-- so this is
going to be the data attribute

00:10:22.700 --> 00:10:24.990
age-- to be whatever
is passed in.

00:10:24.990 --> 00:10:27.110
And then, I'm also making
another assignment here,

00:10:27.110 --> 00:10:29.180
where I'm assigning
the data attribute

00:10:29.180 --> 00:10:33.400
name to be None originally.

00:10:33.400 --> 00:10:35.740
Later on in the code, when
I want to create an Animal

00:10:35.740 --> 00:10:38.320
object, I say the class name.

00:10:38.320 --> 00:10:41.080
And then I pass it in
whatever parameters

00:10:41.080 --> 00:10:43.150
it takes-- in this
case, the age.

00:10:43.150 --> 00:10:50.760
And I'm assigning it to
this instance here, OK?

00:10:50.760 --> 00:10:54.990
All right, so now we
have this class, Animal.

00:10:54.990 --> 00:10:57.810
We've done the first
part here, which is

00:10:57.810 --> 00:11:01.260
to initialize the class, right?

00:11:01.260 --> 00:11:06.570
So we've told Python how to
create an object of this type.

00:11:06.570 --> 00:11:11.340
There's a few other methods
here that I've implemented.

00:11:11.340 --> 00:11:14.400
Next two we call
getters, and the two

00:11:14.400 --> 00:11:17.480
after that we call setters, OK?

00:11:17.480 --> 00:11:19.920
And getters and setters
are very commonly

00:11:19.920 --> 00:11:28.060
used when implementing a class.

00:11:28.060 --> 00:11:31.580
So getters essentially
return the values of any

00:11:31.580 --> 00:11:35.600
of the data attributes, OK?

00:11:35.600 --> 00:11:40.290
So if you look carefully,
get_age() is just returning

00:11:40.290 --> 00:11:44.640
self.age, and get_name()
just returns self.name.

00:11:44.640 --> 00:11:47.880
So they're very simple methods.

00:11:47.880 --> 00:11:49.600
Similarly, set_age()
and set_name()--

00:11:49.600 --> 00:11:53.370
we're going to see what this
funny equal sign is doing here

00:11:53.370 --> 00:11:55.020
in the next couple of slides.

00:11:55.020 --> 00:11:56.520
But setters do a
very similar thing

00:11:56.520 --> 00:11:58.436
where they're going to
set the data attributes

00:11:58.436 --> 00:12:00.927
to whatever is passed in, OK?

00:12:04.600 --> 00:12:06.670
So those are
getters and setters.

00:12:06.670 --> 00:12:09.420
And then, the last thing down
here is this __str__ method.

00:12:09.420 --> 00:12:13.530
And this __str__ method is used
to tell Python how to print

00:12:13.530 --> 00:12:15.951
an object of this type Animal.

00:12:15.951 --> 00:12:17.700
So if you didn't have
this __str__ method,

00:12:17.700 --> 00:12:19.230
if you remember
from last lecture,

00:12:19.230 --> 00:12:23.880
what ends up happening is you're
going to get some message when

00:12:23.880 --> 00:12:27.420
you print your object that
says, this is an object of type

00:12:27.420 --> 00:12:30.810
Animal at this memory location,
which is very uninformative,

00:12:30.810 --> 00:12:31.710
right?

00:12:31.710 --> 00:12:33.300
So you implement
this method here,

00:12:33.300 --> 00:12:40.319
which tells Python how to print
an object of this type, OK?

00:12:40.319 --> 00:12:43.407
So the big point
of this slide is

00:12:43.407 --> 00:12:45.490
that you should be using
getters and setters-- you

00:12:45.490 --> 00:12:49.340
should be implementing getters
and setters for your classes.

00:12:49.340 --> 00:12:52.440
And we're going to see, in
the next couple of slides,

00:12:52.440 --> 00:12:54.390
why exactly.

00:12:54.390 --> 00:12:57.660
But basically, they're
going to prevent bugs

00:12:57.660 --> 00:13:01.410
from coming into play
later on if someone decides

00:13:01.410 --> 00:13:04.150
to change implementation.

00:13:04.150 --> 00:13:06.179
So we saw how to-- so
the previous slide,

00:13:06.179 --> 00:13:08.470
this slide here, shows the
implementation of the Animal

00:13:08.470 --> 00:13:09.790
class.

00:13:09.790 --> 00:13:12.550
And here we can see
how we can create

00:13:12.550 --> 00:13:14.830
an instance of this object.

00:13:14.830 --> 00:13:16.740
So we can say a = Animal(3).

00:13:16.740 --> 00:13:20.800
So this is going to create a new
Animal object with an age of 3.

00:13:20.800 --> 00:13:25.390
And we can access the object
through the variable a.

00:13:25.390 --> 00:13:27.250
Dot notation, recall,
is a way for you

00:13:27.250 --> 00:13:31.120
to access data attributes
and methods of a class, OK?

00:13:31.120 --> 00:13:34.660
So you can say a.age
later on in your program,

00:13:34.660 --> 00:13:36.390
and that is allowed.

00:13:36.390 --> 00:13:38.530
It'll try to access
the age data attribute

00:13:38.530 --> 00:13:41.440
of this particular
instance of the class, a.

00:13:41.440 --> 00:13:44.460
So this is going to give you 3.

00:13:44.460 --> 00:13:47.580
However, it's actually
not recommended to access

00:13:47.580 --> 00:13:48.760
data attributes directly.

00:13:48.760 --> 00:13:50.280
So this is the
reason-- so you're

00:13:50.280 --> 00:13:52.780
going to see in the next slide,
the reason-- why we're going

00:13:52.780 --> 00:13:53.940
to use getters and setters.

00:13:53.940 --> 00:13:59.710
Instead, you should use the
get_age() getter method to get

00:13:59.710 --> 00:14:02.840
the age of the animal.

00:14:02.840 --> 00:14:04.404
So this is going
to return, also, 3.

00:14:04.404 --> 00:14:06.070
So these are going
to do the same thing.

00:14:09.900 --> 00:14:12.720
And the reason why you'd want
to use getters and setters

00:14:12.720 --> 00:14:15.579
is this idea of
information hiding, OK?

00:14:15.579 --> 00:14:17.370
So the whole reason
why we're using classes

00:14:17.370 --> 00:14:19.590
in object-oriented
programming is

00:14:19.590 --> 00:14:24.690
so that you can abstract
certain data from the user, OK?

00:14:24.690 --> 00:14:26.880
One of the things you
should be abstracting

00:14:26.880 --> 00:14:29.340
is these data attributes.

00:14:29.340 --> 00:14:31.170
So users shouldn't
really need to know

00:14:31.170 --> 00:14:33.360
how a class is implemented.

00:14:33.360 --> 00:14:36.450
They should just know
how to use the class, OK?

00:14:36.450 --> 00:14:37.890
So consider the following case.

00:14:37.890 --> 00:14:41.280
Let's say whoever wrote
the Animal class wants

00:14:41.280 --> 00:14:43.662
to change the implementation.

00:14:43.662 --> 00:14:45.120
And they've decided
they don't want

00:14:45.120 --> 00:14:47.070
to call the data attribute
"age" anymore, they

00:14:47.070 --> 00:14:48.900
want to call it "years," OK?

00:14:48.900 --> 00:14:50.430
So when they
initialize an animal

00:14:50.430 --> 00:14:53.970
they say self.years = age.

00:14:53.970 --> 00:14:58.140
So an animal still gets
initialized by its age.

00:14:58.140 --> 00:15:03.070
And the age gets passed into a
data attribute named "years,"

00:15:03.070 --> 00:15:04.580
OK?

00:15:04.580 --> 00:15:06.200
Since I'm implementing
this class,

00:15:06.200 --> 00:15:08.930
I want to have a getter, which
is going to return self.years.

00:15:08.930 --> 00:15:11.180
So I'm not returning
self.age anymore,

00:15:11.180 --> 00:15:15.990
because age is no longer the
data attribute I'm using.

00:15:15.990 --> 00:15:20.310
So with this new
implementation, if someone

00:15:20.310 --> 00:15:23.820
was using this implementation
and was accessing age directly

00:15:23.820 --> 00:15:27.940
as-- was accessing the data
attribute age directly--

00:15:27.940 --> 00:15:29.440
with this new
implementation, they'd

00:15:29.440 --> 00:15:31.030
actually get an error, right?

00:15:31.030 --> 00:15:33.190
Because this animal
that they created

00:15:33.190 --> 00:15:35.560
using my old
implementation no longer

00:15:35.560 --> 00:15:37.870
has an attribute named "age."

00:15:37.870 --> 00:15:39.850
And so Python's
going to spit out

00:15:39.850 --> 00:15:42.385
an error saying no attribute
found or something like that,

00:15:42.385 --> 00:15:43.280
OK?

00:15:43.280 --> 00:15:46.270
If they were using the
getter a.get_age()--

00:15:46.270 --> 00:15:49.060
the person who implemented the
class re-implemented get_age()

00:15:49.060 --> 00:15:55.480
to work correctly, right,
with their new data attribute,

00:15:55.480 --> 00:15:59.940
years, as opposed to age--
so if I was using the getter

00:15:59.940 --> 00:16:03.670
get_age(), I wouldn't
have run into the bug, OK?

00:16:03.670 --> 00:16:09.060
So things to remember--
write getters and setters

00:16:09.060 --> 00:16:10.410
for your classes.

00:16:10.410 --> 00:16:12.990
And later on in your code,
use getters and setters

00:16:12.990 --> 00:16:19.750
to prevent bugs and to
promote easy to maintain code.

00:16:19.750 --> 00:16:25.009
OK, so information
hiding is great.

00:16:25.009 --> 00:16:26.800
But having said that,
Python's actually not

00:16:26.800 --> 00:16:29.290
very great at
information hiding, OK?

00:16:29.290 --> 00:16:31.060
Python allows you
to do certain things

00:16:31.060 --> 00:16:33.280
that you should never be doing.

00:16:33.280 --> 00:16:34.120
OK.

00:16:34.120 --> 00:16:37.180
So the first, we've just seen.

00:16:37.180 --> 00:16:39.790
The first is to
access data attributes

00:16:39.790 --> 00:16:42.490
from outside of the class, OK?

00:16:42.490 --> 00:16:45.010
So if I were to
say a.age, Python

00:16:45.010 --> 00:16:49.700
allows me to do that without
using a getter and setter.

00:16:49.700 --> 00:16:53.900
Python also allows you to
write to data attributes

00:16:53.900 --> 00:16:56.250
from outside the class.

00:16:56.250 --> 00:16:58.620
So if I implemented
the class Animal

00:16:58.620 --> 00:17:02.110
assuming that age was
a number, an integer,

00:17:02.110 --> 00:17:05.770
and all of my methods work
as long as age is an integer,

00:17:05.770 --> 00:17:09.260
but someone decided to be smart
and, outside of the class,

00:17:09.260 --> 00:17:14.230
set age to be infinite
as a string, that might

00:17:14.230 --> 00:17:17.020
cause the code to crash, OK?

00:17:17.020 --> 00:17:18.790
Python allows you to do that.

00:17:18.790 --> 00:17:22.750
But now you're breaking
the fact that age

00:17:22.750 --> 00:17:24.364
has to be an integer, right?

00:17:24.364 --> 00:17:25.780
So now the methods
should probably

00:17:25.780 --> 00:17:28.154
be checking the fact that age
is an integer all the time.

00:17:30.702 --> 00:17:32.410
The other thing that
you're allowed to do

00:17:32.410 --> 00:17:35.890
is to create data attributes
outside of the class

00:17:35.890 --> 00:17:37.540
definition, OK?

00:17:37.540 --> 00:17:40.480
So if I wanted to create a new
data attribute called "size"

00:17:40.480 --> 00:17:42.370
for this particular
instance, Python also

00:17:42.370 --> 00:17:43.790
allows me to do that.

00:17:43.790 --> 00:17:46.547
And I can set it to
whatever I want, OK?

00:17:46.547 --> 00:17:48.380
So Python allows you
to do all these things,

00:17:48.380 --> 00:17:52.590
but it's actually not good
style to do any of them.

00:17:52.590 --> 00:17:56.220
So just don't do it.

00:17:56.220 --> 00:17:58.394
All right.

00:17:58.394 --> 00:18:00.560
So the last thing I want
to mention-- the last thing

00:18:00.560 --> 00:18:02.840
about classes before we
go on to inheritance--

00:18:02.840 --> 00:18:05.660
is this idea called
default arguments.

00:18:05.660 --> 00:18:09.011
And default arguments
are passed into methods.

00:18:09.011 --> 00:18:10.510
And since methods
are functions, you

00:18:10.510 --> 00:18:14.590
can also pass in different
arguments to functions.

00:18:14.590 --> 00:18:17.650
So for example, this
set_name() method had self.

00:18:17.650 --> 00:18:22.720
And then, this new name is equal
to this empty string here, OK?

00:18:22.720 --> 00:18:26.152
We haven't seen this before.

00:18:26.152 --> 00:18:27.735
But this is called
a default argument.

00:18:30.390 --> 00:18:33.615
And you can use the
function in one of two ways.

00:18:37.240 --> 00:18:38.940
The first way is
so we can create

00:18:38.940 --> 00:18:42.900
a new instance of an Animal
type object with this line

00:18:42.900 --> 00:18:46.360
here, a = Animal(3).

00:18:46.360 --> 00:18:48.230
And then we can
say a.set_name().

00:18:48.230 --> 00:18:52.360
So this calls the setter
method to set the name.

00:18:52.360 --> 00:18:55.470
And notice, we've
always said that you

00:18:55.470 --> 00:19:00.270
have to put in parameters for
everything other than self, OK?

00:19:00.270 --> 00:19:04.240
But here we have no
parameters passed in.

00:19:04.240 --> 00:19:07.890
But that's OK, because
newname actually

00:19:07.890 --> 00:19:11.440
has a default argument, OK?

00:19:11.440 --> 00:19:15.490
So that tells Python, if no
parameter is passed in for this

00:19:15.490 --> 00:19:19.570
particular formal parameter,
then use whatever is up here

00:19:19.570 --> 00:19:23.160
by default. So if I haven't
passed in the parameter

00:19:23.160 --> 00:19:27.610
a.get_na-- a.set_name(), sorry--
a.sett_name() is going to be

00:19:27.610 --> 00:19:29.290
setting the name to
the empty string,

00:19:29.290 --> 00:19:32.030
because that's what the
default parameter is.

00:19:32.030 --> 00:19:34.120
So in the next line, when
I print a.get_name(),

00:19:34.120 --> 00:19:36.380
this is just going to
print the empty string, OK?

00:19:39.340 --> 00:19:41.170
If you do want to
pass in a parameter,

00:19:41.170 --> 00:19:43.720
you can do so as normal.

00:19:43.720 --> 00:19:46.090
So you can say a =
Animal(3), a.set_name(),

00:19:46.090 --> 00:19:48.610
and then pass in
a parameter here.

00:19:48.610 --> 00:19:52.195
And then, newname is going to be
assigned to whatever parameter

00:19:52.195 --> 00:19:55.890
is passed in like that.

00:19:55.890 --> 00:19:59.010
Whatever you pass in overrides
the default argument,

00:19:59.010 --> 00:20:00.997
and everything is good.

00:20:00.997 --> 00:20:03.330
So when I print a.get_name(),
this is going to print out

00:20:03.330 --> 00:20:05.010
the name that you've passed in.

00:20:08.350 --> 00:20:10.177
Questions about default?

00:20:10.177 --> 00:20:11.151
Yeah.

00:20:11.151 --> 00:20:12.125
AUDIENCE: [INAUDIBLE]

00:20:16.055 --> 00:20:18.430
ANA BELL: What if you don't
provide a default value for--

00:20:18.430 --> 00:20:19.750
AUDIENCE: For newname?

00:20:19.750 --> 00:20:20.666
ANA BELL: For newname?

00:20:22.560 --> 00:20:24.960
If you don't provide a
default argument for newname

00:20:24.960 --> 00:20:27.600
and you do this case
here, then that's

00:20:27.600 --> 00:20:28.750
going to give you an error.

00:20:28.750 --> 00:20:30.416
So Python's going to
say something like,

00:20:30.416 --> 00:20:33.325
expected one argument, got
zero, or something like that.

00:20:33.325 --> 00:20:33.950
Great question.

00:20:37.537 --> 00:20:39.900
OK.

00:20:39.900 --> 00:20:42.834
All right, so let's move on to
this idea of hierarchies, OK?

00:20:42.834 --> 00:20:45.000
So the great thing about
object-oriented programming

00:20:45.000 --> 00:20:47.520
is that it allows us to
add layers of abstraction

00:20:47.520 --> 00:20:48.490
to our code, all right?

00:20:48.490 --> 00:20:52.140
So we don't need to know how
very, very low-level things are

00:20:52.140 --> 00:20:53.820
implemented in
order to use them.

00:20:53.820 --> 00:20:59.490
And we can build up our code
to be more and more complex

00:20:59.490 --> 00:21:04.140
as we use up these
different abstractions.

00:21:04.140 --> 00:21:08.700
So consider every one of these
things on this slide as being

00:21:08.700 --> 00:21:10.950
a separate object, all right?

00:21:10.950 --> 00:21:15.993
Every one of these things can be
considered to be an animal, OK?

00:21:19.690 --> 00:21:21.760
According to our
implementation of an animal,

00:21:21.760 --> 00:21:24.400
the one thing that an
animal has is an age, OK?

00:21:24.400 --> 00:21:26.170
And that's probably true, right?

00:21:26.170 --> 00:21:28.860
Every one of these
things has an age.

00:21:28.860 --> 00:21:31.630
But now I want to
build up on this

00:21:31.630 --> 00:21:33.580
and create separate
groups, right?

00:21:33.580 --> 00:21:35.350
And each one of
these separate groups

00:21:35.350 --> 00:21:37.990
that I create on
top of Animal is

00:21:37.990 --> 00:21:40.099
going to have its own
functionality, right?

00:21:40.099 --> 00:21:42.640
They're going to be a little
bit more specific, a little more

00:21:42.640 --> 00:21:44.560
specialized.

00:21:44.560 --> 00:21:47.410
So I can create these three
groups now, a cat, a rabbit,

00:21:47.410 --> 00:21:50.360
and a person group.

00:21:50.360 --> 00:21:53.510
And for example-- so
they're all animals, right?

00:21:53.510 --> 00:21:55.750
They all have an age.

00:21:55.750 --> 00:21:57.310
But for example,
maybe a person's

00:21:57.310 --> 00:22:00.730
going to have a list of friends
whereas a cat and a rabbit

00:22:00.730 --> 00:22:02.660
do not.

00:22:02.660 --> 00:22:07.280
Maybe a cat has a data attribute
for the number of lives

00:22:07.280 --> 00:22:11.690
they have left, right, whereas a
person and a rabbit do not, OK?

00:22:11.690 --> 00:22:16.580
So you can think of adding
these more specialized-- adding

00:22:16.580 --> 00:22:19.339
functionality to each one
of these subgroups, OK?

00:22:19.339 --> 00:22:21.380
So they're going to be
more and more specialized,

00:22:21.380 --> 00:22:24.055
but all of them retaining the
fact that they are animals.

00:22:24.055 --> 00:22:28.160
So they all have an
age, for example.

00:22:28.160 --> 00:22:31.930
So on top of these, we
can add another layer

00:22:31.930 --> 00:22:36.590
and say that a student is a
person and is an animal, OK?

00:22:36.590 --> 00:22:39.910
But in addition to having an
age and maybe also having a list

00:22:39.910 --> 00:22:42.430
of friends, a student
might also have a major

00:22:42.430 --> 00:22:46.720
or-- they're pretty, so
maybe-- their favorite subject

00:22:46.720 --> 00:22:47.220
in school.

00:22:52.290 --> 00:22:55.310
So that's the general
idea of hierarchies, OK?

00:22:55.310 --> 00:23:02.000
So we can sort of abstract the
previous slide into this one

00:23:02.000 --> 00:23:05.570
and say that we have parent
classes and child classes, OK?

00:23:05.570 --> 00:23:07.890
The Animal class is
like our parent class.

00:23:07.890 --> 00:23:09.220
It's the highest-level class.

00:23:13.480 --> 00:23:15.580
Inheriting from
the Animal class,

00:23:15.580 --> 00:23:19.380
we have these child
classes or subclasses, OK?

00:23:19.380 --> 00:23:21.684
Whatever an animal can
do, a person can do.

00:23:21.684 --> 00:23:23.350
Whatever an animal
can do, a cat can do.

00:23:23.350 --> 00:23:26.430
And whatever an animal can do,
a rabbit can do, OK-- that is,

00:23:26.430 --> 00:23:31.530
have an age and maybe some
really basic functionality, OK?

00:23:31.530 --> 00:23:33.300
But between person,
cat, and rabbit,

00:23:33.300 --> 00:23:34.890
they're going to
be varying wildly

00:23:34.890 --> 00:23:36.973
as to the kinds of things
that they can do, right?

00:23:36.973 --> 00:23:40.070
But they can all do
whatever Animal can do.

00:23:40.070 --> 00:23:43.010
So child classes inherit
all of the data attributes

00:23:43.010 --> 00:23:44.750
and all of the
methods, or behaviors,

00:23:44.750 --> 00:23:48.610
that their parent's
classes have, OK?

00:23:48.610 --> 00:23:50.800
But child classes can
add more information.

00:23:50.800 --> 00:23:53.290
Like for example, a person
can have a list of friends

00:23:53.290 --> 00:23:55.030
whereas a general
animal will not.

00:23:58.790 --> 00:24:00.140
It can add more behavior.

00:24:00.140 --> 00:24:03.170
Like, maybe a cat can
climb trees whereas people

00:24:03.170 --> 00:24:05.820
and rabbits cannot.

00:24:05.820 --> 00:24:07.990
Or you can also
override behavior.

00:24:07.990 --> 00:24:11.430
So in the previous one, we
had animal, person, student.

00:24:11.430 --> 00:24:13.770
So maybe we have, an animal
doesn't speak at all,

00:24:13.770 --> 00:24:15.420
but a person can speak.

00:24:15.420 --> 00:24:18.420
So that's added
functionality to the person.

00:24:18.420 --> 00:24:20.340
And maybe a person
can only say hello.

00:24:20.340 --> 00:24:21.840
But then, when we
talk to a student,

00:24:21.840 --> 00:24:25.590
we can override the fact--
override the speak() method

00:24:25.590 --> 00:24:28.800
of a person and say that a
student can say, you know,

00:24:28.800 --> 00:24:31.550
I have homework, or I need
sleep, or something like that,

00:24:31.550 --> 00:24:32.550
OK?

00:24:32.550 --> 00:24:35.162
So we have the same speak()
method for both person

00:24:35.162 --> 00:24:36.870
and student, because
they can both speak.

00:24:36.870 --> 00:24:38.559
But student will
override the fact

00:24:38.559 --> 00:24:40.225
that they say hello
with something else.

00:24:42.790 --> 00:24:47.360
OK, so let's look at some code
to put this into perspective.

00:24:47.360 --> 00:24:52.290
So we have this Animal class,
which we've seen before.

00:24:52.290 --> 00:24:54.500
This is the parent class, OK?

00:24:54.500 --> 00:24:58.130
It inherits from object,
which means that everything

00:24:58.130 --> 00:25:01.670
that a basic object can do
in Python, an animal can do,

00:25:01.670 --> 00:25:04.000
which is things like
binding variables,

00:25:04.000 --> 00:25:06.831
you know, very
low-level things, OK?

00:25:06.831 --> 00:25:07.830
We've seen the __init__.

00:25:07.830 --> 00:25:11.820
We've seen the two getters, the
setters, and the string method

00:25:11.820 --> 00:25:15.750
to print an object
of type Animal.

00:25:15.750 --> 00:25:18.330
All right, now, let's
create a subclass of Animal.

00:25:18.330 --> 00:25:22.440
We'll call it Cat, OK?

00:25:22.440 --> 00:25:25.697
We create a class named Cat.

00:25:25.697 --> 00:25:27.530
In parentheses, instead
of putting "object,"

00:25:27.530 --> 00:25:29.200
we now put "Animal."

00:25:29.200 --> 00:25:34.870
And this tells Python that
Cat's parent class is Animal.

00:25:34.870 --> 00:25:37.900
So everything that an
animal can do, a cat can do.

00:25:37.900 --> 00:25:40.120
So that includes all of
the attributes, which

00:25:40.120 --> 00:25:42.650
was age and name, and
all of the methods.

00:25:42.650 --> 00:25:45.490
So all the getters, the setters,
the __str__, the __init__,

00:25:45.490 --> 00:25:48.160
everything that the animal
had, now the cat has--

00:25:48.160 --> 00:25:51.700
the Cat class has.

00:25:51.700 --> 00:25:55.730
In the Cat class, we're going
to add two more methods though.

00:25:55.730 --> 00:25:59.190
The first is speak().

00:25:59.190 --> 00:26:04.980
So speak() is going to be a
method that's going to just

00:26:04.980 --> 00:26:08.920
take in the self, OK--
no other parameters.

00:26:08.920 --> 00:26:11.710
And all it's doing is printing
"meow" to the screen-- very

00:26:11.710 --> 00:26:13.150
simple, OK?

00:26:13.150 --> 00:26:15.760
So through this speak(),
we've added new functionality

00:26:15.760 --> 00:26:17.080
to the class.

00:26:17.080 --> 00:26:22.690
So an animal couldn't speak,
whereas a cat says "meow."

00:26:22.690 --> 00:26:25.000
Additionally, through
this __str__ method here,

00:26:25.000 --> 00:26:28.240
we're overriding the
animal __str__, OK?

00:26:28.240 --> 00:26:30.320
So if we go back to
the previous slide,

00:26:30.320 --> 00:26:34.210
we can see that the
animal's __str__ had animal,

00:26:34.210 --> 00:26:40.900
plus the name, plus the age here
whereas the cat's __str__ now

00:26:40.900 --> 00:26:44.510
says "cat," name,
and the age, OK?

00:26:44.510 --> 00:26:48.270
So this is just how I chose
to implement this, OK?

00:26:48.270 --> 00:26:51.330
So here I've overridden the
__str__ method of the Animal

00:26:51.330 --> 00:26:53.690
class.

00:26:53.690 --> 00:26:56.450
Notice that this class
doesn't have an __init__,

00:26:56.450 --> 00:26:57.690
and that's OK.

00:26:57.690 --> 00:27:01.070
Because Python's actually
going to say, well,

00:27:01.070 --> 00:27:04.430
if there's no __init__ in this
particular method-- sorry,

00:27:04.430 --> 00:27:09.770
in this particular class-- then
look to my parents and say,

00:27:09.770 --> 00:27:11.780
do my parents have
an __init__, OK?

00:27:11.780 --> 00:27:15.540
And if so, use that __init__.

00:27:15.540 --> 00:27:17.617
So that's actually true
for any other methods.

00:27:20.770 --> 00:27:23.840
So the idea here is, when
you have hierarchies,

00:27:23.840 --> 00:27:26.799
you have a parent class,
you have a child class,

00:27:26.799 --> 00:27:28.840
you could have a child
class to that child class,

00:27:28.840 --> 00:27:29.910
and so on and so on.

00:27:29.910 --> 00:27:33.930
So you can have multiple
levels of inheritance.

00:27:33.930 --> 00:27:35.900
What happens when you
create an object that

00:27:35.900 --> 00:27:40.000
is of type something that's
been-- of a type that's

00:27:40.000 --> 00:27:43.700
the child class of a child
class of a child class, right?

00:27:43.700 --> 00:27:47.900
What happens when you call
a method on that object?

00:27:47.900 --> 00:27:50.510
Well, Python's are going
to say, does a method with

00:27:50.510 --> 00:27:53.120
that name exist in my
current class definition?

00:27:53.120 --> 00:27:54.750
And if so, use that.

00:27:54.750 --> 00:27:56.390
But if not, then,
look to my parents.

00:27:56.390 --> 00:27:59.060
Do my parents know
how to do that, right?

00:27:59.060 --> 00:28:04.010
Do my parents have a method
for whatever I want to do?

00:28:04.010 --> 00:28:04.820
If so, use that.

00:28:04.820 --> 00:28:07.100
If not, look to their
parents, and so on and so on.

00:28:07.100 --> 00:28:09.950
So you're sort of tracing
back up your ancestry

00:28:09.950 --> 00:28:14.630
to figure out if you can
do this method or not.

00:28:14.630 --> 00:28:18.270
So let's look at a slightly
more complicated example.

00:28:18.270 --> 00:28:19.880
We have a class named Person.

00:28:19.880 --> 00:28:21.296
It's going to
inherit from Animal.

00:28:24.680 --> 00:28:27.500
Inside this person, I'm
going to create my own--

00:28:27.500 --> 00:28:29.510
I'm going to create
an __init__ method.

00:28:29.510 --> 00:28:32.134
And the __init__ method is going
to do something different than

00:28:32.134 --> 00:28:34.370
what the animal's
__init__ method is doing.

00:28:34.370 --> 00:28:36.170
It's going to take
in self, as usual.

00:28:36.170 --> 00:28:38.294
And it's going to take in
two parameters as opposed

00:28:38.294 --> 00:28:41.309
to one, a name and an age.

00:28:41.309 --> 00:28:43.600
First thing the __init__
method's doing is it's calling

00:28:43.600 --> 00:28:46.090
the animal's __init__ method.

00:28:46.090 --> 00:28:48.040
Why am I doing that?

00:28:48.040 --> 00:28:53.040
Well, I could theoretically
initialize the name

00:28:53.040 --> 00:28:55.350
and the age data
attributes that Animal

00:28:55.350 --> 00:28:58.570
initializes in this method.

00:28:58.570 --> 00:29:00.630
But I'm using the
fact that I've already

00:29:00.630 --> 00:29:03.420
written code that initializes
those two data attributes.

00:29:03.420 --> 00:29:05.790
So why not just use it, OK?

00:29:05.790 --> 00:29:08.910
So here, this says, I'm going
to call the class Animal.

00:29:08.910 --> 00:29:10.800
I'm going to call
its __init__ method.

00:29:10.800 --> 00:29:14.550
And I'm going to leave it up to
you to-- not you as the class,

00:29:14.550 --> 00:29:18.645
but I'm talking as the
programs is running--

00:29:18.645 --> 00:29:20.520
I'm going to leave it
up to you to figure out

00:29:20.520 --> 00:29:24.840
how to initialize an animal
with this particular age

00:29:24.840 --> 00:29:26.051
and what to name it.

00:29:28.640 --> 00:29:31.110
So Python says, yep,
I know how to do this,

00:29:31.110 --> 00:29:33.470
so I'm going to go ahead
and do that for you.

00:29:33.470 --> 00:29:35.900
So now it says
person is an animal.

00:29:35.900 --> 00:29:38.760
And I've initialized the
age and the name for you.

00:29:38.760 --> 00:29:41.260
The next thing I'm doing in the
__init__ is I'm going to set

00:29:41.260 --> 00:29:44.752
the name to whatever
name was passed in, OK?

00:29:44.752 --> 00:29:46.960
So in the __init__, notice,
I can do whatever I want,

00:29:46.960 --> 00:29:50.805
including calling methods.

00:29:50.805 --> 00:29:52.430
And then, the last
thing I'm doing here

00:29:52.430 --> 00:29:55.250
is I'm going to create a new
data attribute for Person,

00:29:55.250 --> 00:29:57.899
which is a list of friends, OK?

00:29:57.899 --> 00:29:59.690
So an animal didn't
have a list of friends,

00:29:59.690 --> 00:30:01.721
but a person is going to.

00:30:04.550 --> 00:30:07.727
The next four methods here
are-- this one's a getter,

00:30:07.727 --> 00:30:09.560
so it's going to return
the list of friends.

00:30:09.560 --> 00:30:14.750
This is going to append a
friend to the end of my list.

00:30:14.750 --> 00:30:17.420
I want to make a note that I
actually didn't write a method

00:30:17.420 --> 00:30:18.300
to remove friends.

00:30:18.300 --> 00:30:21.290
So once you get a friend,
they're friends for life.

00:30:21.290 --> 00:30:24.200
But that's OK.

00:30:24.200 --> 00:30:27.590
The next method here is speak(),
which is going to print "hello"

00:30:27.590 --> 00:30:29.690
to the screen.

00:30:29.690 --> 00:30:31.306
And the last method
here is going

00:30:31.306 --> 00:30:33.180
to get the age difference
between two people.

00:30:33.180 --> 00:30:34.930
So that just basically
subtracts their age

00:30:34.930 --> 00:30:38.730
and says it's a five-year age
difference, or whatever it is.

00:30:38.730 --> 00:30:40.750
And down here, I have
an __str__ method,

00:30:40.750 --> 00:30:42.700
which I've overridden
from the Animal, which,

00:30:42.700 --> 00:30:46.630
instead of "animal: name," it's
going to say "person: name :

00:30:46.630 --> 00:30:47.466
age," OK?

00:30:52.820 --> 00:30:54.760
So we can run this code.

00:30:54.760 --> 00:30:56.300
So that's down here.

00:30:56.300 --> 00:31:03.670
I have an animal person here.

00:31:07.690 --> 00:31:10.850
So I'm going to run this code.

00:31:10.850 --> 00:31:13.180
And what did I do?

00:31:13.180 --> 00:31:14.290
I created a new person.

00:31:14.290 --> 00:31:16.210
I gave it a name and an age.

00:31:16.210 --> 00:31:18.220
I created another person,
a name and an age.

00:31:18.220 --> 00:31:19.990
And here I've just run
some methods on it,

00:31:19.990 --> 00:31:22.540
which was get_name(),
get_age(), get_name(),

00:31:22.540 --> 00:31:25.090
and get_age() for each
of the two people.

00:31:25.090 --> 00:31:29.490
So that printed, Jack
is 30, Jill is 25.

00:31:29.490 --> 00:31:32.190
If I print p1, this is going
to use the __str__ method

00:31:32.190 --> 00:31:32.789
of Person.

00:31:32.789 --> 00:31:34.830
So it's to print "person:",
their name, and then,

00:31:34.830 --> 00:31:36.540
their age.

00:31:36.540 --> 00:31:38.310
p1.speak() just says "hello."

00:31:38.310 --> 00:31:41.940
And then, the age difference
between p1 and p2 is just 5.

00:31:41.940 --> 00:31:44.897
So that's just subtracting
and then printing that out

00:31:44.897 --> 00:31:45.480
to the screen.

00:31:49.408 --> 00:31:50.680
OK, so that's my person.

00:31:53.560 --> 00:31:57.040
Let's add another class.

00:31:57.040 --> 00:31:58.950
This class is going
to be a student,

00:31:58.950 --> 00:32:02.850
and it's going to be
a subclass of Person.

00:32:02.850 --> 00:32:04.770
Since it's a subclass
of Person, it's

00:32:04.770 --> 00:32:07.670
going to-- a student
is going inherit

00:32:07.670 --> 00:32:11.360
all the attributes of a
person, and therefore, all the

00:32:11.360 --> 00:32:14.780
attributes of an animal.

00:32:14.780 --> 00:32:17.270
The __init__ method of a
student is going to be a little

00:32:17.270 --> 00:32:19.550
different from
the one of Person.

00:32:19.550 --> 00:32:22.540
We're going to give it a
name, an age, and a major.

00:32:22.540 --> 00:32:25.610
Notice we're using
default arguments here.

00:32:25.610 --> 00:32:27.965
So if I create a student
without giving it a major,

00:32:27.965 --> 00:32:32.990
the major is going to be
set to None originally.

00:32:32.990 --> 00:32:37.730
Once again, this line here,
Person. init (self, name, age),

00:32:37.730 --> 00:32:40.430
tells Python, hey,
you already know

00:32:40.430 --> 00:32:44.120
how to initialize a person for
me with this name and this age.

00:32:44.120 --> 00:32:46.370
So can you just do that?

00:32:46.370 --> 00:32:48.740
And Python says, yes,
I can do that for you.

00:32:48.740 --> 00:32:51.844
And so that saves you, maybe,
like five lines of code just

00:32:51.844 --> 00:32:54.260
by calling the __init__ method
that you've already written

00:32:54.260 --> 00:32:56.870
through Person, OK?

00:32:56.870 --> 00:33:00.680
So Student has been
initialized to be a person.

00:33:00.680 --> 00:33:03.140
And additionally, we're going
to set another data attribute

00:33:03.140 --> 00:33:04.640
for the student to be the major.

00:33:04.640 --> 00:33:08.860
And we're going to set
the major to be None.

00:33:08.860 --> 00:33:12.640
The student is going to get
this setter here, this setter

00:33:12.640 --> 00:33:15.460
method, which is going to
change the major to whatever

00:33:15.460 --> 00:33:19.850
else they want if they
want to change it.

00:33:19.850 --> 00:33:24.910
And then, I'm going to
override the speak() method.

00:33:24.910 --> 00:33:26.500
So the speak method
for the person,

00:33:26.500 --> 00:33:28.210
recall, just said "hello."

00:33:28.210 --> 00:33:31.180
A student is going to be
a little bit more complex.

00:33:31.180 --> 00:33:33.760
I'm going to use the
fact that someone

00:33:33.760 --> 00:33:36.300
created this random class, OK?

00:33:36.300 --> 00:33:38.920
So this is where we can
write more interesting code

00:33:38.920 --> 00:33:41.580
by reusing code that
other people have written.

00:33:41.580 --> 00:33:43.480
So someone wrote a
random class that

00:33:43.480 --> 00:33:47.430
can do cool things
with random numbers.

00:33:47.430 --> 00:33:49.871
So if I want to use
random numbers in my code,

00:33:49.871 --> 00:33:52.370
I'm going to put this "import
random" at the top of my code,

00:33:52.370 --> 00:33:54.953
which essentially brings in all
of the methods from the Random

00:33:54.953 --> 00:33:58.910
class, one of the methods
being this random() method.

00:33:58.910 --> 00:34:04.840
So random() is a random()
method from the Random class.

00:34:04.840 --> 00:34:07.800
And this essentially gives
me a number between 0 and 1,

00:34:07.800 --> 00:34:11.440
including 0 but not
including 1, OK?

00:34:11.440 --> 00:34:14.770
So this random number I get
here is going to help me write

00:34:14.770 --> 00:34:18.844
my method for speak(), where
it's going to-- with 25%

00:34:18.844 --> 00:34:21.219
probability, it's either going
to say, "I have homework,"

00:34:21.219 --> 00:34:24.400
"I need sleep," "I should
eat," or "I'm watching TV," OK?

00:34:24.400 --> 00:34:27.052
So a student is going to say
one of those four things.

00:34:27.052 --> 00:34:29.260
And the last thing I'm doing
down here is overwriting

00:34:29.260 --> 00:34:31.449
the __str__ method.

00:34:31.449 --> 00:34:33.880
So let's look at the code.

00:34:33.880 --> 00:34:41.620
I'm going to comment this part
out, and uncomment the student,

00:34:41.620 --> 00:34:42.992
and see what we get.

00:34:45.760 --> 00:34:49.270
OK, so here, I am
creating the student.

00:34:49.270 --> 00:34:52.530
I'm creating one student whose
major is CS, name is Alice,

00:34:52.530 --> 00:34:54.210
and age is 20.

00:34:54.210 --> 00:34:56.770
s2 is going to be
another student-- name--

00:34:56.770 --> 00:34:57.997
Beth, age-- 18.

00:34:57.997 --> 00:34:59.830
And the major is going
to be None, because I

00:34:59.830 --> 00:35:02.090
didn't pass in any major here.

00:35:02.090 --> 00:35:04.150
So by default, using
the default argument,

00:35:04.150 --> 00:35:05.290
it's going to be None.

00:35:05.290 --> 00:35:08.590
If I print s1, s2, that's going
to print out these two things

00:35:08.590 --> 00:35:12.270
over here just by whatever
__str__ method does.

00:35:14.780 --> 00:35:19.020
And then I'm going to get
the students to speak.

00:35:19.020 --> 00:35:20.780
And if I run it
multiple times, you

00:35:20.780 --> 00:35:23.670
can see that it's going to print
different things each time.

00:35:26.670 --> 00:35:29.160
So "I need sleep," "I have
homework," "I need sleep,"

00:35:29.160 --> 00:35:31.570
"I have homework," yeah.

00:35:31.570 --> 00:35:33.904
So every time, it's going to
print something different.

00:35:37.800 --> 00:35:43.180
OK, questions about
inheritance in this example?

00:35:46.512 --> 00:35:47.837
OK.

00:35:47.837 --> 00:35:49.920
Last thing we're going to
talk about in this class

00:35:49.920 --> 00:35:52.200
is an idea of-- or
in this lecture,

00:35:52.200 --> 00:35:55.320
is the idea of-- a
class variable, OK?

00:35:55.320 --> 00:35:58.320
So to illustrate this, I'm going
to create yet another subclass

00:35:58.320 --> 00:36:00.350
of my animal called a rabbit.

00:36:03.970 --> 00:36:08.530
So class variables-- so so
far, we've seen-- sorry,

00:36:08.530 --> 00:36:09.250
let me back up.

00:36:09.250 --> 00:36:12.640
So so far, we've seen
instance variables, right?

00:36:12.640 --> 00:36:15.776
So things like self.name,
self.age, those

00:36:15.776 --> 00:36:16.900
are all instance variables.

00:36:16.900 --> 00:36:18.880
So they're variables
that are specif--

00:36:18.880 --> 00:36:22.190
they are common across all of
the instances of the class,

00:36:22.190 --> 00:36:22.690
right?

00:36:22.690 --> 00:36:26.290
Every instance of the class
has this particular variable.

00:36:26.290 --> 00:36:27.890
But the value of the
variable is going

00:36:27.890 --> 00:36:31.800
to be different between all
of the different instances.

00:36:31.800 --> 00:36:36.560
So class variables are
going to be variables

00:36:36.560 --> 00:36:38.330
whose values are
shared between all

00:36:38.330 --> 00:36:40.130
of the instances in the class.

00:36:40.130 --> 00:36:46.435
So if one instance of the class
modifies this class variable,

00:36:46.435 --> 00:36:48.610
then, any other
instance of the class

00:36:48.610 --> 00:36:50.270
is going to see
the modified value.

00:36:50.270 --> 00:36:55.020
So it's sort of shared among
all of the different instances.

00:36:55.020 --> 00:36:56.970
So we're going to
use class variables

00:36:56.970 --> 00:36:58.325
to keep track of rabbits.

00:37:00.940 --> 00:37:04.160
OK, so we're creating
this class, Rabbit.

00:37:04.160 --> 00:37:05.040
tag = 1.

00:37:05.040 --> 00:37:08.970
We haven't seen something
like this before.

00:37:08.970 --> 00:37:11.990
So tag is our class variable.

00:37:11.990 --> 00:37:15.380
Class variables are typically
defined inside the class

00:37:15.380 --> 00:37:21.000
definition but outside
of the __init__.

00:37:21.000 --> 00:37:22.710
So tag is going to
be a class variable,

00:37:22.710 --> 00:37:26.420
and I'm initializing it to 1.

00:37:26.420 --> 00:37:29.450
Inside the __init__, this
tells us how to create a Rabbit

00:37:29.450 --> 00:37:30.170
object.

00:37:30.170 --> 00:37:32.990
So I'm going to give it self
as usual, an age, and then

00:37:32.990 --> 00:37:34.550
two parents.

00:37:34.550 --> 00:37:38.000
Don't worry about the
two parents for now.

00:37:38.000 --> 00:37:41.000
Inside the __init__--
sorry, inside the __init__--

00:37:41.000 --> 00:37:45.440
I'm going to call the __init__
of the animal just to do less

00:37:45.440 --> 00:37:47.320
work.

00:37:47.320 --> 00:37:49.930
Python already knows how to
initialize an animal for me,

00:37:49.930 --> 00:37:50.860
so let's do that.

00:37:50.860 --> 00:37:54.920
So that's going to set the two
data attributes, name and age.

00:37:54.920 --> 00:37:56.420
I'm going to set
the data attributes

00:37:56.420 --> 00:37:58.160
for parent1,
parent2 for a rabbit

00:37:58.160 --> 00:38:00.145
to be whatever's passed in.

00:38:00.145 --> 00:38:01.520
And then, this is
where I'm going

00:38:01.520 --> 00:38:04.220
to use this class variable.

00:38:04.220 --> 00:38:09.090
So I'm creating
this data attribute

00:38:09.090 --> 00:38:13.380
instance variable particular
to a specific instance called

00:38:13.380 --> 00:38:15.730
rid, OK?

00:38:15.730 --> 00:38:20.890
And I'm assigning this instance
variable to the class variable.

00:38:20.890 --> 00:38:23.590
And I access class
variables using not self,

00:38:23.590 --> 00:38:26.755
but the class name-- so
in this case, rabbit.tag.

00:38:30.010 --> 00:38:32.290
So initially, tag
is going to be 1.

00:38:32.290 --> 00:38:37.116
And then, the __init__ is going
to increment the tag by 1 here,

00:38:37.116 --> 00:38:37.960
OK?

00:38:37.960 --> 00:38:39.910
So that means that,
from now on, if I

00:38:39.910 --> 00:38:42.130
create any other instances,
the other instances

00:38:42.130 --> 00:38:45.070
are going to be accessing the
updated value of tag instead

00:38:45.070 --> 00:38:46.450
of being 1.

00:38:46.450 --> 00:38:50.060
So let's do a quick drawing
to show you what I mean.

00:38:50.060 --> 00:38:57.224
So let's say I have
Rabbit.tag here, OK?

00:39:00.360 --> 00:39:05.620
So initially, tag is
going to be 1, OK?

00:39:05.620 --> 00:39:08.090
And then I'm going to
create a new Rabbit object.

00:39:08.090 --> 00:39:10.550
So this is as I'm
calling the code, OK?

00:39:10.550 --> 00:39:15.290
So let's say this is a rabbit
object-- oh boy, OK-- r1.

00:39:17.777 --> 00:39:19.860
You know, I actually googled
how to draw a rabbit,

00:39:19.860 --> 00:39:21.090
but that didn't help at all.

00:39:21.090 --> 00:39:28.620
OK, so r1 is going to be a
new rabbit that we create.

00:39:28.620 --> 00:39:33.270
Initially, what happens is, when
I first create this new rabbit,

00:39:33.270 --> 00:39:38.770
it's going to access the
class variable, which,

00:39:38.770 --> 00:39:41.210
it's current value is 1.

00:39:41.210 --> 00:39:45.340
So when I create the
rabbit ID-- the rabbit ID,

00:39:45.340 --> 00:39:50.345
r1.rid-- this is going
to get the value 1.

00:39:53.200 --> 00:39:55.390
And according to
the code, after I

00:39:55.390 --> 00:39:57.160
set the rabbit ID
to whatever tag is,

00:39:57.160 --> 00:39:59.660
I'm going to increment the tag.

00:39:59.660 --> 00:40:02.230
So this is going to say,
OK, now that I've said it,

00:40:02.230 --> 00:40:07.000
I'm going to go back up here
and increment the tag to be 2.

00:40:07.000 --> 00:40:07.970
OK.

00:40:07.970 --> 00:40:11.612
So let's say I create
another Rabbit object, OK?

00:40:11.612 --> 00:40:18.180
All right, there--
that's a sad rabbit, r2.

00:40:18.180 --> 00:40:24.430
The ID of r2 is
going to be what?

00:40:24.430 --> 00:40:28.150
Well, according to the way
we create a new Rabbit object

00:40:28.150 --> 00:40:30.910
is it's going to access
whatever the value of tag

00:40:30.910 --> 00:40:33.430
is, which is a class variable.

00:40:33.430 --> 00:40:36.400
It was changed by the previous
creation of my rabbit,

00:40:36.400 --> 00:40:39.010
so now I'm going to
access that, right?

00:40:39.010 --> 00:40:40.792
So the value is going to be 2.

00:40:43.322 --> 00:40:45.030
And according to the
code, the next thing

00:40:45.030 --> 00:40:48.630
I do after I create
the instance rid

00:40:48.630 --> 00:40:50.850
is I'm going to increment tag.

00:40:50.850 --> 00:40:56.560
So I'm incrementing the
class variable to be 3, OK?

00:40:56.560 --> 00:40:57.990
So notice that all
of my instances

00:40:57.990 --> 00:41:02.790
are accessing this
shared resource,

00:41:02.790 --> 00:41:05.580
this shared variable called tag.

00:41:05.580 --> 00:41:07.530
So as I'm creating
more and more rabbits,

00:41:07.530 --> 00:41:09.870
they're all going to be
incrementing the value of tag,

00:41:09.870 --> 00:41:13.480
because it's shared among
all of the instances.

00:41:13.480 --> 00:41:17.470
And so this value, this
tag class variable,

00:41:17.470 --> 00:41:20.830
keeps track of how many
different instances of a rab--

00:41:20.830 --> 00:41:23.110
of how many different
instances of rabbits

00:41:23.110 --> 00:41:27.390
I've created throughout
my entire program, OK?

00:41:27.390 --> 00:41:30.260
So the big idea here is that
class variables are shared

00:41:30.260 --> 00:41:31.790
across all the instances.

00:41:31.790 --> 00:41:34.590
So they can all modify them.

00:41:34.590 --> 00:41:40.850
But these rids, right,
these instance variables,

00:41:40.850 --> 00:41:43.950
are only for that
particular instance.

00:41:43.950 --> 00:41:49.850
So r2 can't have access to r1's
ID value, nor could change it.

00:41:49.850 --> 00:41:54.702
But it won't change it across
all of the different instances,

00:41:54.702 --> 00:41:55.202
OK?

00:42:00.840 --> 00:42:07.060
So that's how the __init__
method works of Rabbit, OK?

00:42:07.060 --> 00:42:09.690
So we have these tags that
keep track of how many rabbits

00:42:09.690 --> 00:42:11.490
we've created.

00:42:11.490 --> 00:42:14.597
We have a couple of getter--
we have some getters here

00:42:14.597 --> 00:42:15.555
to get all the parents.

00:42:15.555 --> 00:42:20.020
So now let's add a somewhat
more interesting function.

00:42:20.020 --> 00:42:23.230
Oh, I just want to mention,
when I'm getting the rid,

00:42:23.230 --> 00:42:26.520
I'm actually using this
cool zfill() function here,

00:42:26.520 --> 00:42:30.850
or method, which actually pads
the beginning of any number

00:42:30.850 --> 00:42:34.180
with however many zeros in order
to get to that number here.

00:42:34.180 --> 00:42:36.530
So the number 1
becomes 001 and so on.

00:42:36.530 --> 00:42:40.510
So it ensures that I have
this nice-looking ID type

00:42:40.510 --> 00:42:42.160
thing that's always
three digits long.

00:42:44.880 --> 00:42:47.120
So let's try to work
with this Rabbit object.

00:42:47.120 --> 00:42:50.830
Let's define what happens when
you add two rabbits together,

00:42:50.830 --> 00:42:53.730
OK-- in this class,
not in the real world.

00:42:53.730 --> 00:42:54.850
OK.

00:42:54.850 --> 00:42:59.760
So if I want to use the plus
operator between two rabbit

00:42:59.760 --> 00:43:07.750
instances, I have to implement
this __add__ method, OK?

00:43:07.750 --> 00:43:11.574
So all I'm doing here is I'm
returning a new Rabbit object,

00:43:11.574 --> 00:43:12.400
OK?

00:43:12.400 --> 00:43:14.290
Whoops, sorry about that.

00:43:14.290 --> 00:43:18.310
And let's recall the __init__
method of the rabbit, OK?

00:43:18.310 --> 00:43:20.394
So when I'm returning
a new Rabbit object,

00:43:20.394 --> 00:43:22.060
I'm returning a new
Rabbit object that's

00:43:22.060 --> 00:43:25.090
going to have an age of 0.

00:43:25.090 --> 00:43:28.180
Self-- so the Rabbit object
I'm calling this method on

00:43:28.180 --> 00:43:31.540
is going to be the
parent of the new rabbit.

00:43:31.540 --> 00:43:36.066
And other is going to be the
other parent of the new rabbit,

00:43:36.066 --> 00:43:38.330
OK?

00:43:38.330 --> 00:43:58.950
So if we look at the
code, and I run it,

00:43:58.950 --> 00:44:04.710
this part here, I'm creating
three rabbits, r1, r2, and r3.

00:44:04.710 --> 00:44:08.220
Notice this class
variable is working

00:44:08.220 --> 00:44:10.680
as expected, because the
IDs of each of my rabbits

00:44:10.680 --> 00:44:12.240
increments as I
create more rabbits.

00:44:12.240 --> 00:44:17.710
So we have 001, 002, 003.

00:44:17.710 --> 00:44:20.500
If I print r1, and
r2, and r3-- that

00:44:20.500 --> 00:44:23.800
was these three lines over
here-- the parents of r1 and r2

00:44:23.800 --> 00:44:29.260
are None, because that's just
the default-- yes, the default

00:44:29.260 --> 00:44:31.950
arguments for creating a rabbit.

00:44:31.950 --> 00:44:34.300
To add two rabbits together,
I use the plus operator

00:44:34.300 --> 00:44:37.460
between two Rabbit objects.

00:44:37.460 --> 00:44:40.640
And on the right here, I'm
testing rabbit addition.

00:44:40.640 --> 00:44:44.815
And I can print out the
IDs of all my rabbits.

00:44:44.815 --> 00:44:46.940
And notice that, when I've
created this new rabbit,

00:44:46.940 --> 00:44:50.360
r4, the ID of it still
kept incrementing.

00:44:50.360 --> 00:44:55.670
So now, the ID of the
fourth rabbit is 004.

00:44:55.670 --> 00:44:57.590
And then, when I
get r4's parents,

00:44:57.590 --> 00:45:00.295
they are as we want them
to be, so r1 and r2.

00:45:05.530 --> 00:45:09.251
The other thing I want to do
is to compare two rabbits.

00:45:09.251 --> 00:45:10.750
So if I want to
compare two rabbits,

00:45:10.750 --> 00:45:13.370
I want to make sure that
their parents are the same.

00:45:13.370 --> 00:45:17.421
So I can compare the first
parent of the first rabbit

00:45:17.421 --> 00:45:19.170
with the first parent
of the second rabbit

00:45:19.170 --> 00:45:21.430
and the second parent
of the first rabbit

00:45:21.430 --> 00:45:23.830
to the second parent
of second rabbit

00:45:23.830 --> 00:45:26.650
or getting the
combinations of those two.

00:45:26.650 --> 00:45:29.470
So that's what these
two Booleans are doing.

00:45:29.470 --> 00:45:31.600
So these are going
to tell me-- these

00:45:31.600 --> 00:45:34.480
are going to be Boolean
values, either True or False.

00:45:34.480 --> 00:45:36.910
And I'm going to
return either they

00:45:36.910 --> 00:45:38.860
have the same
parents of that type

00:45:38.860 --> 00:45:41.956
or the same parents
criss-crossed, OK?

00:45:41.956 --> 00:45:43.330
So here, notice
that I'm actually

00:45:43.330 --> 00:45:46.840
comparing the IDs of the
rabbits as opposed to the Rabbit

00:45:46.840 --> 00:45:49.220
objects directly, OK?

00:45:49.220 --> 00:45:53.530
So if, instead of
comparing the IDs in here,

00:45:53.530 --> 00:45:56.490
I was comparing the parents
themselves, directly,

00:45:56.490 --> 00:45:59.800
what would end up happening
is this function, this method,

00:45:59.800 --> 00:46:02.770
eq(), would get called
over and over again.

00:46:02.770 --> 00:46:05.830
Because here, we have
parents that are rabbits.

00:46:05.830 --> 00:46:08.890
And at some point, the parents
of the very, very first rabbits

00:46:08.890 --> 00:46:11.690
ever created by this
program are None.

00:46:11.690 --> 00:46:14.410
And so when I try
to call-- when I

00:46:14.410 --> 00:46:19.330
try to call the parent one of
None, that's going to give me

00:46:19.330 --> 00:46:21.730
an error, OK, something
like an attribute error

00:46:21.730 --> 00:46:25.780
where None doesn't have
this parent attribute, OK?

00:46:25.780 --> 00:46:28.240
So that's why I'm
comparing IDs here, OK?

00:46:28.240 --> 00:46:32.140
And the code in the
lecture here shows

00:46:32.140 --> 00:46:36.910
you some tests about whether
rabbits have the same parents.

00:46:36.910 --> 00:46:38.470
And I've created
new rabbits here,

00:46:38.470 --> 00:46:43.490
r3 and r4, the
addition of those two.

00:46:43.490 --> 00:46:47.180
And r5 and r6 are going to have
the same parents down here--

00:46:47.180 --> 00:46:51.710
True-- but r4 and r6 don't, OK?

00:46:51.710 --> 00:46:54.290
So just to wrap it up,
object-oriented programming

00:46:54.290 --> 00:46:57.000
is the idea of creating
your own collections of data

00:46:57.000 --> 00:46:59.000
where you can organize
the information in a very

00:46:59.000 --> 00:46:59.810
consistent manner.

00:46:59.810 --> 00:47:01.910
So every single type
of object that you

00:47:01.910 --> 00:47:05.270
create of this particular
type that you create-- sorry,

00:47:05.270 --> 00:47:08.030
every object instance
of a particular type

00:47:08.030 --> 00:47:10.250
is going to have the
exact same data attributes

00:47:10.250 --> 00:47:11.870
and the exact same methods, OK?

00:47:11.870 --> 00:47:15.050
So this really comes back
to the idea of decomposition

00:47:15.050 --> 00:47:16.880
and abstraction in programming.

00:47:16.880 --> 00:47:19.450
All right, thanks, everyone.