WEBVTT

1
00:00:00.370 --> 00:00:02.700
Classes are all about organization.

2
00:00:02.700 --> 00:00:06.280
They're a solid reliable way to break
up your monolithic scripts into smaller

3
00:00:06.280 --> 00:00:10.410
chunks so they're easier to understand,
easier to reuse, and easier to customize.

4
00:00:10.410 --> 00:00:14.700
You've already seen how we can use super
to reuse the code written in a superclass,

5
00:00:14.700 --> 00:00:17.380
but that only gets us
part of the way there.

6
00:00:17.380 --> 00:00:18.990
If we continue with our current example,

7
00:00:18.990 --> 00:00:22.860
our Thief class, think of all the things
we eventually have to have in there.

8
00:00:22.860 --> 00:00:25.870
Thieves can pick pocket, hide in
the shadows, throw a few nasty daggers,

9
00:00:25.870 --> 00:00:29.050
and dodge attacks better than
some strong but slow warrior.

10
00:00:29.050 --> 00:00:31.790
We don't want to implement all
of that on our Thief class.

11
00:00:31.790 --> 00:00:34.080
Especially when other classes
like maybe an assassin or

12
00:00:34.080 --> 00:00:37.000
a ninja would meet some
of those abilities too.

13
00:00:37.000 --> 00:00:38.420
Let's get back to workspaces and

14
00:00:38.420 --> 00:00:41.150
look at extending several
classes to make one awesome one.

15
00:00:42.870 --> 00:00:46.010
Alright, things have changed a little
bit so you'll want to be sure and

16
00:00:46.010 --> 00:00:49.600
relaunch the work space if you're
following along in workspaces.

17
00:00:49.600 --> 00:00:53.670
Now, I've prepared a couple of classes and
modules already.

18
00:00:53.670 --> 00:00:56.430
Here, there's this
attributes.py module and

19
00:00:56.430 --> 00:00:58.460
you can see we're inside
the RPG directory.

20
00:00:59.620 --> 00:01:02.800
And inside here,
we have an Agile attribute and

21
00:01:02.800 --> 00:01:04.510
we have a Sneaky one as well.

22
00:01:04.510 --> 00:01:08.370
We'll use both of these, but say we're
making a gymnast, they might be agile but

23
00:01:08.370 --> 00:01:10.970
they wouldn't necessarily be sneaky.

24
00:01:10.970 --> 00:01:14.495
I've also refactored the Thief class
out into its own module called

25
00:01:14.495 --> 00:01:16.950
thieves.py, wait a minute.

26
00:01:16.950 --> 00:01:19.290
Refactored, what kind of word is that?

27
00:01:19.290 --> 00:01:23.820
Well, refactoring is a common programming
concept that sometimes is as easy as

28
00:01:23.820 --> 00:01:27.260
cutting and pasting some code
from one place to another.

29
00:01:27.260 --> 00:01:29.485
Sometimes it's more complicated than that.

30
00:01:29.485 --> 00:01:33.365
Ultimately though, it's just the tech
jargon word for rearranging code into

31
00:01:33.365 --> 00:01:37.885
a more logical state through renaming
things, moving code between files, or

32
00:01:37.885 --> 00:01:42.265
deleting or combining code where
too much optimization was done.

33
00:01:42.265 --> 00:01:45.955
So our thief character is still
pretty similar to how it was before.

34
00:01:45.955 --> 00:01:50.720
Thief inherits from Character, but you
notice we're not setting sneaky or agile.

35
00:01:50.720 --> 00:01:53.380
We're gonna handle those
inside these attributes.

36
00:01:54.850 --> 00:01:56.310
So let's go ahead and do that.

37
00:01:56.310 --> 00:02:00.330
I want this to be a sneaky,
agile character, and

38
00:02:00.330 --> 00:02:01.960
of course it should still be a character.

39
00:02:01.960 --> 00:02:08.870
So let's say,
from attributes import Agile and Sneaky.

40
00:02:08.870 --> 00:02:12.220
And now I need to use them in
the definition of my class,

41
00:02:12.220 --> 00:02:15.500
now the order that you add items into
the inheritance chain of your class,

42
00:02:15.500 --> 00:02:20.940
this list of classes here that your
class extends, that order matters.

43
00:02:20.940 --> 00:02:24.450
It matters because of things like super
and how Python figures out which super

44
00:02:24.450 --> 00:02:28.800
classes methods to call when two of
them have methods with the same name.

45
00:02:28.800 --> 00:02:32.410
This the called the method
resolution order or MRO.

46
00:02:32.410 --> 00:02:33.950
And it can cause a lot of headaches.

47
00:02:33.950 --> 00:02:38.800
But Python has gotten better and
better at making things just work for you.

48
00:02:39.930 --> 00:02:42.980
Well, let's look at our sneaky and
agile classes.

49
00:02:42.980 --> 00:02:46.680
Now, they both have
an init inside of them.

50
00:02:46.680 --> 00:02:52.080
And in both of them, the __init__ calls
super, and it passes on args and kwargs.

51
00:02:52.080 --> 00:02:53.290
So, that's good.

52
00:02:53.290 --> 00:02:56.640
And they don't have any other
methods where the names conflict.

53
00:02:56.640 --> 00:02:59.640
So, they should be fine
to use in either order.

54
00:02:59.640 --> 00:03:01.830
You know what?
I'm kinda tired to thinking about this.

55
00:03:01.830 --> 00:03:03.460
I'm just gonna use them
as if they're modifiers.

56
00:03:03.460 --> 00:03:04.670
I'm gonna make a sentence, right?

57
00:03:04.670 --> 00:03:08.490
So Character that is Agile and Sneaky.

58
00:03:08.490 --> 00:03:11.740
I'm also getting kind of tired of testing
this down in the shell constantly,

59
00:03:11.740 --> 00:03:13.499
like having to import and making things.

60
00:03:13.499 --> 00:03:17.175
So let's make a new file that
we will call play.py, and

61
00:03:17.175 --> 00:03:22.401
inside of here we'll import our Thief
class from thieves import Thief, and

62
00:03:22.401 --> 00:03:27.970
then we'll say that kenneth is equal to
a Thief(), and we give it the name Kenneth.

63
00:03:29.610 --> 00:03:35.065
And then let’s print kenneth.sneaky,

64
00:03:35.065 --> 00:03:40.150
kenneth.agile, let’s just print those for
right now.

65
00:03:40.150 --> 00:03:42.630
That covers two of our special attributes,
right?

66
00:03:42.630 --> 00:03:47.910
Maybe we will do a print kenneth.hide
when the light level is 8.

67
00:03:49.723 --> 00:03:53.147
All right, I think that's pretty good,
and you know what,

68
00:03:53.147 --> 00:03:55.870
let's do, here, let's say sneaky=False.

69
00:03:55.870 --> 00:03:59.610
All right, that covers a lot of our stuff,

70
00:03:59.610 --> 00:04:03.560
now feel free to change these attributes,
change the names,

71
00:04:03.560 --> 00:04:07.940
all that kind of stuff, make this your own
code, you don't have to copy me exactly.

72
00:04:07.940 --> 00:04:12.560
All right, so now let's try Python play.

73
00:04:12.560 --> 00:04:15.680
And we get False for sneaky, 
because we set sneaky to False.

74
00:04:15.680 --> 00:04:16.250
We get True for

75
00:04:16.250 --> 00:04:21.320
agile because we didn't change that, and
I failed to hide because I'm not sneaky.

76
00:04:21.320 --> 00:04:22.080
All right.

77
00:04:22.080 --> 00:04:25.350
But let's think about design for
a minute, right?

78
00:04:26.940 --> 00:04:30.248
Sometimes if you run this code, depending
on the version of Python that you're on,

79
00:04:30.248 --> 00:04:30.955
this might fail.

80
00:04:30.955 --> 00:04:35.980
You might get an error that there's no
attribute Agile or no attribute Sneaky.

81
00:04:35.980 --> 00:04:41.343
And even though we're using super on
all of these, sometimes the keys and

82
00:04:41.343 --> 00:04:46.101
values don't always get passed up
to where they need to be because

83
00:04:46.101 --> 00:04:51.230
the MRO falls down and Python fails
to put things where they need to go.

84
00:04:51.230 --> 00:04:53.978
So, okay, as part of that,

85
00:04:53.978 --> 00:04:59.490
we could add a super into
the init of our character class.

86
00:05:00.540 --> 00:05:05.570
And then everything should work in all
versions of python no matter where we are,

87
00:05:05.570 --> 00:05:08.510
right, and no matter how
the inheritance chain is set up.

88
00:05:08.510 --> 00:05:13.030
But, usually you want something
that's more concrete as a class,

89
00:05:13.030 --> 00:05:15.310
a class where things just stop, right?

90
00:05:15.310 --> 00:05:19.850
It's the concrete, and right now I
would think that our Character class is

91
00:05:19.850 --> 00:05:23.590
the class that we want that, and
we kind of have designed that, because our

92
00:05:23.590 --> 00:05:28.110
Character class doesn't have a super
in it, since it doesn't super anything,

93
00:05:28.110 --> 00:05:31.480
it's expecting to be the final
class that gets initialized.

94
00:05:31.480 --> 00:05:32.910
Well that's cool, we can do that.

95
00:05:32.910 --> 00:05:36.190
If it wants to be the final class,
let's make it the final class.

96
00:05:36.190 --> 00:05:41.880
So we'll put Character out here,
and then we'll,

97
00:05:43.640 --> 00:05:48.740
over this way and
delete Character off of there.

98
00:05:48.740 --> 00:05:53.190
And that reads better too, right,
our thief is an agile sneaky character.

99
00:05:53.190 --> 00:05:54.690
Okay, so
let's see if this is still working.

100
00:05:56.910 --> 00:06:01.770
And now we've got an error, we didn't get
this error before, but now we get one.

101
00:06:01.770 --> 00:06:08.421
And this time it says that init is missing
a required positional argument name.

102
00:06:08.421 --> 00:06:11.630
Okay, now, I know that we
provided a name value, right?

103
00:06:11.630 --> 00:06:13.860
We provided name right here.

104
00:06:13.860 --> 00:06:17.196
But, this is what I was talking
about with the super calls,

105
00:06:17.196 --> 00:06:21.933
from the time it goes to this super calls,
it's a lot of this positional argument and

106
00:06:21.933 --> 00:06:24.290
doesn't put name or this arg.

107
00:06:24.290 --> 00:06:28.530
It doesn't put this arg as
the first argument that provided.

108
00:06:28.530 --> 00:06:31.530
So, we have to make
another design decision.

109
00:06:31.530 --> 00:06:36.006
Now, we could go into our attributes and
we could change all of these and

110
00:06:36.006 --> 00:06:40.938
we could put a name right here, and then
we could put name and then continue on.

111
00:06:40.938 --> 00:06:46.776
But that means that all of our attributes
have to know how a character is created.

112
00:06:46.776 --> 00:06:50.767
Well, that's fine, but that creates what
you'll hear a lot of developers call

113
00:06:50.767 --> 00:06:53.940
tightly coupled code, or
a tightly coupled design.

114
00:06:53.940 --> 00:06:56.240
We want to create code
that's loosely coupled, so

115
00:06:56.240 --> 00:06:59.460
that it becomes easier to mix and
match things around.

116
00:06:59.460 --> 00:07:05.350
So instead, let's go change how the name
argument in Character is defined.

117
00:07:05.350 --> 00:07:10.230
And let's make it a keyword argument,
right?

118
00:07:10.230 --> 00:07:12.150
Because we provided a default value.

119
00:07:12.150 --> 00:07:15.490
All we have to do is add the equals and
the empty string.

120
00:07:15.490 --> 00:07:18.790
But we wanna make sure that
there's a name that's set.

121
00:07:18.790 --> 00:07:21.750
So let's add a check and an exception.

122
00:07:21.750 --> 00:07:27.585
So if not name, and
then we're going to raise ValueError,

123
00:07:27.585 --> 00:07:31.094
and we'll say, name is required.

124
00:07:33.170 --> 00:07:36.100
And then let's try to
create our Thief right now.

125
00:07:38.510 --> 00:07:40.430
And we get our ValueError,
name is required.

126
00:07:40.430 --> 00:07:43.500
So now when we create our thief,
let's go right here and

127
00:07:43.500 --> 00:07:44.900
let's say name is equal to Kenneth.

128
00:07:46.010 --> 00:07:50.640
So now, when we explicitly set this
parameter, and remember explicit is better

129
00:07:50.640 --> 00:07:55.960
than implicit, our whole character
works just like we expected it to.

130
00:07:55.960 --> 00:07:57.200
So great work everyone.

131
00:07:58.520 --> 00:08:01.540
You'll hear a lot of developers talk
about whether something is loosely or

132
00:08:01.540 --> 00:08:02.790
tightly coupled.

133
00:08:02.790 --> 00:08:06.650
Usually they'll complain about something
being tightly coupled and for good reason.

134
00:08:06.650 --> 00:08:09.260
Code that relies heavily on
other code can make it very

135
00:08:09.260 --> 00:08:12.390
difficult to add in
the functionality you want and need.

136
00:08:12.390 --> 00:08:16.050
For instance, imagine you had code to
send messages to users of your app.

137
00:08:16.050 --> 00:08:19.680
If your message sending code always used
a single mechanism for delivering those

138
00:08:19.680 --> 00:08:24.610
messages, like email, it would be really
hard to add in SMS or push notifications.

139
00:08:24.610 --> 00:08:27.750
If the code is more loosely coupled though
and just expected there to be a send

140
00:08:27.750 --> 00:08:31.060
message method, you could swap
out the backend much more easily.
