Heads up! To view this whole video, sign in with your Courses account or enroll in your free 7-day trial. Sign In Enroll
Preview
Start a free Courses trial
to watch this video
What if a built-in class has most of what you need? Here's how to customize them to fit exactly your needs.
Completely subclassing built-ins isn't the easiest skill in the world. You're doing an amazing job on this!
Just because immutable types expect you to override __new__
doesn't mean you can't use __init__
on them. They still call the method but you can't change the initialization of the object in them.
I think we're going to leave our
role playing game characters behind.
0:00
They were really handy for exploring brand
new classes but a lot of the power in
0:02
object oriented design comes from
taking advantage of the classes and
0:06
objects provided by your language.
0:09
Python gives us tons of objects
that we can use as the basis of our
0:11
own custom ones.
0:14
We've been really heavy into
magic methods in this stage.
0:15
That's not gonna change now.
0:18
When you're dealing with
custom versions of built-ins,
0:19
there are two magic methods that you'll
want to know about above all others.
0:21
Init and new.
0:25
You've already seen init,
0:27
we use it when we want to customize how
a new instance of a class is created.
0:28
New works very similarly but
0:32
has some special magic that we're
not quite ready for it just yet.
0:34
The major difference between the two
though is when you use them.
0:37
If you're customizing a mutable data
type like list, you override init.
0:39
If you're customizing an immutable type,
you use new.
0:44
Let's go to work spaces to
try these new methods out.
0:47
All right, so
let's start with an immutable class first.
0:52
How about we make a version of string,
of STR that is always reversed.
0:54
I know, I know, you just thought of
a million uses for such a thing.
1:00
How it's not already
built into the library,
1:03
I don't know,
we'll just have to submit I guess.
1:05
[LAUGH] First things first,
I have a file named reversedstr.py,
1:07
and we need to go ahead and
create our class.
1:12
So I'm gonna call it ReversedStr,
because that's what it is, right?
1:14
I don't wanna confuse anyone.
1:19
I don't want them thinking
this is a normal string.
1:21
And instead of going
straight to the colon,
1:23
I'm going to extend it with str or
I'm going to make it extend str.
1:26
So then I'm just going to say pass.
1:31
And if I use the class right now,
it's a string.
1:34
Now let's just go ahead and do it.
1:36
So Python from reversesdtr
import ReversedStr.
1:42
rs = ReversedStr('hello'),
1:48
and rs is hello, right?
1:52
And len(rs) I get 5.
1:56
So yep, that's a string.
1:59
Okay, so we want to change what
happens when the string is created.
2:02
Since strings are immutable,
2:06
the only place we should change
them is at creation time.
2:08
We do that in new for immutable objects.
2:13
So we're gonna say def __new__( And
2:16
this is going to take whatever args and
kwargs come in, just in case.
2:19
And then, we're gonna say, self =,
and self here is just a variable name,
2:25
we don't have to call it self,
but self = str._
2:28
_new_ _ (args, kwargs).
2:33
And then we're gonna say
self = self reversed, ::-1.
2:38
And then we're going to return self.
2:43
Unlike init, new does a return.
2:46
Now did you notice that
new doesn't take self?
2:49
That's because new is a special kind
of method known as a class method.
2:51
We'll talk more about them in
a later video, but for now,
2:55
just now that they're methods that operate
on a class, and not on an instance.
2:57
And inside of here,
why aren't we using super?
3:02
Well, with immutable types like this,
3:05
it can actually be unsafe
to use super inside of new.
3:06
It's safer and easier to explicitly
use the new method of the class that
3:10
you're extending directly,
which is why we did the str.__new__.
3:14
And then of course, we're gonna go ahead
and reverse our string, and return it.
3:19
So does this work?
3:23
Let's find out.
3:24
Python, let's just import reversedstr,
that's a little bit easier.
3:28
And then let's say rs =
reversedstr.ReversedStr( And
3:33
I'm gonna say hello again.
3:38
And now let's look at rs.
3:41
I got olleh, ole.
3:44
It does, all right.
3:46
Well, great.
3:48
That was easier than I expected.
3:50
What if we wanted to make
a sub-class of list, though?
3:52
Now, since list is mutable, we'll
have to override init instead of new.
3:56
We should come up with something
to make first, though.
4:01
Let's borrow something from the data
science world, at least partially, very,
4:04
very partially.
4:08
And build a list that can be prefilled
with a certain number of numbers.
4:09
So I'm gonna make a New File for
this, I'm gonna call it filledlist.py
4:14
And then inside of here I'm actually
going to import a library called copy,
4:20
and then I'll make my class, FilledList.
4:25
And this is going to extend list,
and I need to override init.
4:28
And I know init takes self.
4:33
All right, so I'm gonna take args and
4:35
kwargs because If anybody
passes me an iterable or
4:38
something like that that they want made
into a list, I want to accept that.
4:43
But basically I'm going to ignore it.
4:47
I want two other arguments.
4:49
I want a count argument, and
4:51
a value argument, so how many do you want,
and what do you want, right?
4:53
So I want five zeros.
4:57
So, first things first,
let's go ahead and super, and then init.
5:00
And we're going to ignore
everything that was passed in.
5:07
I don't care what they gave me.
5:10
All I want is an empty list when
I get to the end of this, okay?
5:12
So at this point,
5:16
right now, I have a brand new, empty
list instance, stored inside of self.
5:17
That I can just do whatever I want with.
5:22
So now, I use a for loop that'll go
through the count and we'll do something.
5:24
Now this underscore here ignores
5:32
the number that would come out of range,
right?
5:35
So, range would normally be zero,
one, two, three, four, five,
5:37
whatever I don't care about that,
so I'm going to ignore it.
5:41
And that's what underscore let's me do.
5:44
I could just use like x and
then never use x there.
5:46
But underscore is cleaner and implies
that you don't care about this value.
5:49
All right, so
then I'm going to append a copy
5:53
of whatever it is that they gave me,
whatever the value is that they gave me.
5:57
So I'm gonna use copy.copy.
6:01
And then value.
6:03
So I will append five copies or
ten copies or whatever.
6:05
Now why am I using copy.copy?
6:08
Well, if they send in something mutable,
like say, another list,
6:10
each member in that filled list or
each member in our filled list
6:14
that was a copy of that list would be the
same member if we weren't using copy.copy.
6:19
If you changed one of them it
would change all of the others too
6:24
because we're just putting
in references to that list.
6:26
Okay, let's try this one out.
6:30
With copy.copy though we're
not getting references.
6:33
We're just getting brand new
versions of those lists.
6:35
So let's say import filledlist and
we'll say fl = filledlist.FilledList.
6:40
And I want this to have 9
copies of the number 2.
6:48
So let's do len(fl), it's 9, cool.
6:51
Now let's do fl and
there's a whole bunch of 2s, nice.
6:56
Now let's make sure that
the copy part is working, right?
7:00
So let's do fl2 = filledlist.FilledList.
7:04
And I want two copies of the list 1, 2, 3.
7:09
Okay, so if I look at fl, sorry, if I look
at fl2, I get 1, 2, 3, 1, 2, 3, great.
7:14
Let's change, on fl2,
the first item's second item to be a 5.
7:21
So we should have 1, 5, 3, 1, 2, 3.
7:27
So if I look at fl2,
that's what I have, 1, 5, 3, 1, 2, 3.
7:30
So excellent, that's working too.
7:35
Now we could go another way, too, and just
change some of the abilities of a class.
7:37
We don't have to totally change the class,
and just destroy everything.
7:42
So let's make a new file.
7:48
And let's call this one
javascriptobject.py.
7:50
Maybe you can guess
what I'm gonna do here.
7:55
Let's make a dictionary that sorta
kind acts like a JavaScript object.
7:57
Or rather, it has one thing that we want,
8:03
which is the ability to
look up keys with a dot.
8:05
So there's a matching
method called getAttribute.
8:08
And that method is use whenever we ask for
an attribute using dot notation.
8:12
So let's try to use that to give us a key.
8:16
So well call us class JavaScriptObject.
8:19
This is going to extend dict.
8:23
And then we're gonna find
getattribute__(self, item).
8:24
And we're gonna try,
return self[item], except KeyError, so
8:32
if that key doesn't exist,
if item isn't a valid key,
8:38
then return
super().__getattribute__(item).
8:44
So if the key does exist,
then we'll get that off of the dict.
8:50
If the key doesn't exist though,
we fall back to the dict version
8:55
of getattribute, which actually looks
to see if there's an attribute,
9:00
an actual real attribute with that name,
okay.
9:04
So let's give that one a try now.
9:08
We're just making weirder and
weirder things?
9:10
Okay, so lets say import javascriptobject.
9:14
Okay, and lets call this jso =
javascriptobject.JavaScriptObject,
9:21
and we're gonna give this one key,
which is a (name).
9:28
And I always like to use my name,
feel free to use your own.
9:32
Let's set an actual attribute on this, so
9:36
jso.language and
will set that language to python, okay.
9:38
So, jso.name is Kenneth it went locked
up the key as an attribute, cool.
9:42
And if I do jso.language I get Python,
and if I do jso.fake, so
9:48
that means it doesn't exist at all,
I get an AttributeError.
9:53
And now an interesting thing here is,
if we scroll up just a little bit,
9:58
we see that our traceback, we got this
KeyError, which that's fine, right?
10:03
That's the KeyError that happened here.
10:08
And then while that KeyError happened,
it came in here into this attribute, and
10:10
it couldn't find that attribute.
10:15
So it threw a second error,
which was this AttributeError.
10:16
Hopefully this gives you some ideas, of
how you can customize the building classes
10:19
to do what you need,
with writing minimal code of your own.
10:23
You need to sign up for Treehouse in order to download course files.
Sign up