1
1

00:00:01,170  -->  00:00:02,470
<v Instructor>Hey guys, welcome back.</v>
2

2

00:00:02,470  -->  00:00:06,540
And in this video, we're going to talk about dataclasses,
3

3

00:00:06,540  -->  00:00:09,153
Dataclasses are a new feature in python 3.7.
4

4

00:00:10,480  -->  00:00:14,300
That make defining our classes a little bit easier,
5

5

00:00:14,300  -->  00:00:16,460
and also a little bit better.
6

6

00:00:16,460  -->  00:00:19,810
The dataclasses are essentially a code generator.
7

7

00:00:19,810  -->  00:00:21,480
So we're going to use them.
8

8

00:00:21,480  -->  00:00:23,790
And it's going to generate some code for us
9

9

00:00:23,790  -->  00:00:26,560
so that we don't have to write it ourselves.
10

10

00:00:26,560  -->  00:00:29,423
Let's begin by defining a user class.
11

11

00:00:30,780  -->  00:00:33,540
And in this class, I'm going to have an init method.
12

12

00:00:33,540  -->  00:00:36,990
And this method is going to take three parameters,
13

13

00:00:36,990  -->  00:00:40,780
the id, the username and the password of the user.
14

14

00:00:40,780  -->  00:00:44,020
And it's going to do the usual self Id is Id,
15

15

00:00:44,020  -->  00:00:47,373
self username is username, and so on.
16

16

00:00:49,730  -->  00:00:51,880
Then we're also going to have a json method
17

17

00:00:53,310  -->  00:00:55,960
that is going to return the Id
18

18

00:00:58,890  -->  00:00:59,853
and the username.
19

19

00:01:02,320  -->  00:01:04,960
Very simple, very standard until now,
20

20

00:01:04,960  -->  00:01:06,550
nothing different than the models
21

21

00:01:06,550  -->  00:01:11,360
that we've been using so far in our application.
22

22

00:01:11,360  -->  00:01:12,850
But of course, this is a sample app
23

23

00:01:12,850  -->  00:01:14,660
it doesn't have anything to do with our application
24

24

00:01:14,660  -->  00:01:16,753
just to show you what dataclasses are.
25

25

00:01:18,850  -->  00:01:22,610
This is a very common pattern when we're defining classes,
26

26

00:01:22,610  -->  00:01:24,980
which is to define an init method.
27

27

00:01:24,980  -->  00:01:28,530
And to make it take some parameters.
28

28

00:01:28,530  -->  00:01:30,610
And to make those parameters affect
29

29

00:01:30,610  -->  00:01:35,000
some property inside the class or the object.
30

30

00:01:35,000  -->  00:01:37,430
So self Id is the Id parameter,
31

31

00:01:37,430  -->  00:01:39,224
username is the username parameter,
32

32

00:01:39,224  -->  00:01:41,160
password is password parameter.
33

33

00:01:41,160  -->  00:01:43,890
This is common, we do it all the time.
34

34

00:01:43,890  -->  00:01:45,580
And it's a little bit of duplication
35

35

00:01:45,580  -->  00:01:46,413
when you think about it,
36

36

00:01:46,413  -->  00:01:50,260
because all our models in our app have something like this,
37

37

00:01:50,260  -->  00:01:51,670
this class has something like this
38

38

00:01:51,670  -->  00:01:54,320
most classes we write, will have something like this.
39

39

00:01:55,570  -->  00:02:00,313
So this is one of the things that dataclasses helps us do.
40

40

00:02:01,410  -->  00:02:02,840
For now, I'm going to keep it as this,
41

41

00:02:02,840  -->  00:02:06,003
I'm gonna go over to app.py and I'm gonna import it.
42

42

00:02:07,781  -->  00:02:09,381
Then I'm going to define a user.
43

43

00:02:13,610  -->  00:02:15,540
And I'm gonna print it out.
44

44

00:02:15,540  -->  00:02:17,220
Just to show you a few of the things
45

45

00:02:17,220  -->  00:02:19,070
that dataclasses can do.
46

46

00:02:19,070  -->  00:02:22,110
Now I'm gonna run app.py and what's gonna happen here is
47

47

00:02:22,110  -->  00:02:25,490
you're gonna see the user object printed out.
48

48

00:02:25,490  -->  00:02:26,960
So what's happened?
49

49

00:02:26,960  -->  00:02:30,130
Well, we've passed in the Id, username
50

50

00:02:30,130  -->  00:02:32,820
and password to our user constructor.
51

51

00:02:32,820  -->  00:02:36,880
Those have been set as the properties inside self.
52

52

00:02:36,880  -->  00:02:39,880
And then we've printed out the user object.
53

53

00:02:39,880  -->  00:02:43,230
Note that we have not called u.json,
54

54

00:02:43,230  -->  00:02:45,220
we just printed the user object.
55

55

00:02:45,220  -->  00:02:49,090
And we've got this weird user.User object
56

56

00:02:49,090  -->  00:02:51,150
at this thing.
57

57

00:02:51,150  -->  00:02:53,380
This is the memory address of the object
58

58

00:02:53,380  -->  00:02:56,700
and this is the class and then it tells you is an object.
59

59

00:02:56,700  -->  00:02:58,190
So this is not terribly useful
60

60

00:02:58,190  -->  00:03:00,750
because when you print an object out
61

61

00:03:00,750  -->  00:03:02,670
It doesn't tell you anything interesting
62

62

00:03:02,670  -->  00:03:05,400
about it other than the class.
63

63

00:03:05,400  -->  00:03:09,690
So, dataclasses are also going to help us fix that,
64

64

00:03:09,690  -->  00:03:11,770
or at least improve it.
65

65

00:03:11,770  -->  00:03:15,410
Let's begin by importing dataclasses.
66

66

00:03:15,410  -->  00:03:18,083
So from dataclasses, import dataclass.
67

67

00:03:19,370  -->  00:03:23,793
And then we're gonna say @dataclass, up here.
68

68

00:03:24,740  -->  00:03:26,690
Now we're gonna print it out.
69

69

00:03:26,690  -->  00:03:30,530
And now you can see that we get user empty brackets.
70

70

00:03:30,530  -->  00:03:31,780
So this has changed a little bit
71

71

00:03:31,780  -->  00:03:33,950
when you print out a dataclass,
72

72

00:03:33,950  -->  00:03:36,570
you no longer get the memory address.
73

73

00:03:36,570  -->  00:03:39,910
And the user.User object, blah, blah, blah.
74

74

00:03:39,910  -->  00:03:44,450
Now you get the class and then you get a pair of brackets.
75

75

00:03:44,450  -->  00:03:47,660
Inside these brackets, the dataclass is going
76

76

00:03:47,660  -->  00:03:51,873
to tell you the object properties that it knows about.
77

77

00:03:52,930  -->  00:03:55,020
So what we're gonna do next is we're gonna say,
78

78

00:03:55,020  -->  00:03:59,980
_id is a string, username is a string
79

79

00:03:59,980  -->  00:04:01,993
and password is a string.
80

80

00:04:03,490  -->  00:04:06,970
When you do this inside a normal class,
81

81

00:04:06,970  -->  00:04:09,510
what this says is, there aren't gonna be three properties
82

82

00:04:09,510  -->  00:04:10,530
and they're gonna be strings.
83

83

00:04:10,530  -->  00:04:12,460
And we've used this in the past.
84

84

00:04:12,460  -->  00:04:15,040
When you do this inside a dataclass,
85

85

00:04:15,040  -->  00:04:17,910
it knows how to use this information
86

86

00:04:17,910  -->  00:04:20,770
to define the init method for you.
87

87

00:04:20,770  -->  00:04:24,890
So you no longer need an init method.
88

88

00:04:24,890  -->  00:04:29,010
Notice that when I run the app, now, it's all going to work.
89

89

00:04:29,010  -->  00:04:33,140
But the dataclass now knows about these three properties.
90

90

00:04:33,140  -->  00:04:37,350
And it tells you in this thing, when it's prints it out.
91

91

00:04:37,350  -->  00:04:38,980
It tells you that the Id is 123,
92

92

00:04:38,980  -->  00:04:41,393
the username is bob and the password is password.
93

93

00:04:43,260  -->  00:04:45,660
This is one of the things that dataclasses does.
94

94

00:04:46,900  -->  00:04:51,120
It allows us to simplify our init method
95

95

00:04:51,120  -->  00:04:52,990
by simply telling it the properties
96

96

00:04:52,990  -->  00:04:55,850
and what their values are or the type of values are
97

97

00:04:55,850  -->  00:04:58,430
and then it will define the init method for us,
98

98

00:04:58,430  -->  00:05:03,020
as well as allowing us to print it out a bit more simply.
99

99

00:05:03,020  -->  00:05:05,330
So how do we print something out?
100

100

00:05:05,330  -->  00:05:08,863
Well, that is the dunder repr method.
101

101

00:05:10,690  -->  00:05:13,620
If we define a dunder repr method inside our user class,
102

102

00:05:13,620  -->  00:05:16,190
and we make it return hi, when we run the app,
103

103

00:05:16,190  -->  00:05:18,550
you'll see that hi is printed out.
104

104

00:05:18,550  -->  00:05:19,600
What does that mean?
105

105

00:05:19,600  -->  00:05:23,150
It means that dataclass has defined a repr method for us,
106

106

00:05:23,150  -->  00:05:25,330
which returns all of this stuff.
107

107

00:05:25,330  -->  00:05:27,470
So it does that for us without us having
108

108

00:05:27,470  -->  00:05:29,253
to define our own repr method.
109

109

00:05:31,170  -->  00:05:32,050
Here's a couple more things
110

110

00:05:32,050  -->  00:05:33,773
that we can do using dataclasses.
111

111

00:05:34,950  -->  00:05:38,020
From the dataclasses package, we can import field,
112

112

00:05:38,020  -->  00:05:41,870
and then we can make one of these equal to field.
113

113

00:05:41,870  -->  00:05:45,010
And then we can pass in a bunch of configuration properties
114

114

00:05:45,010  -->  00:05:46,890
that allow us to further customise
115

115

00:05:46,890  -->  00:05:49,867
how this field is going to behave in this dataclass.
116

116

00:05:50,720  -->  00:05:52,690
I don't want my password to be printed out
117

117

00:05:52,690  -->  00:05:54,110
when I print the user object.
118

118

00:05:54,110  -->  00:05:57,593
So what I'm going to do is say, repr equal False.
119

119

00:05:58,530  -->  00:06:02,130
And now when I run the app, password is not printed there.
120

120

00:06:02,130  -->  00:06:04,680
But it's still accepted as a parameter,
121

121

00:06:04,680  -->  00:06:09,193
because it is not changed when you define repr equal False.
122

122

00:06:10,180  -->  00:06:12,330
If you say init equal False,
123

123

00:06:12,330  -->  00:06:14,990
then it won't be accepted as a parameter.
124

124

00:06:14,990  -->  00:06:17,650
And now you get an error, because it doesn't expect you
125

125

00:06:17,650  -->  00:06:20,610
to pass in a password in here, because we've said
126

126

00:06:20,610  -->  00:06:23,543
that it should not be included in the init method.
127

127

00:06:26,810  -->  00:06:29,930
Now, you can also compare user objects,
128

128

00:06:29,930  -->  00:06:33,253
you can say, is this user object greater than that one?
129

129

00:06:34,140  -->  00:06:36,490
And then in the context of user objects,
130

130

00:06:36,490  -->  00:06:39,030
it doesn't really make sense to compare them.
131

131

00:06:39,030  -->  00:06:41,930
But sometimes it makes sense to say,
132

132

00:06:41,930  -->  00:06:46,060
is this user exactly equal to this other user?
133

133

00:06:46,060  -->  00:06:50,610
And dataclasses will allow that to work with these fields.
134

134

00:06:50,610  -->  00:06:54,251
So if we define u and u2,
135

135

00:06:54,251  -->  00:06:59,251
and we say u equal u2, you'll see that it comes out as True.
136

136

00:06:59,990  -->  00:07:01,440
Why is that?
137

137

00:07:01,440  -->  00:07:04,630
Because the assumption is that all
138

138

00:07:04,630  -->  00:07:06,330
of these fields are the same
139

139

00:07:06,330  -->  00:07:09,260
these fields that the dataclass knows about.
140

140

00:07:09,260  -->  00:07:11,740
So because they are the same across both users,
141

141

00:07:11,740  -->  00:07:13,770
when you compare two dataclasses,
142

142

00:07:13,770  -->  00:07:15,070
what happens in the background is
143

143

00:07:15,070  -->  00:07:17,083
the fields themselves are compared.
144

144

00:07:18,860  -->  00:07:20,290
If you want one of these fields
145

145

00:07:20,290  -->  00:07:23,120
to not be included in the comparison,
146

146

00:07:23,120  -->  00:07:26,160
you can say compare equal False.
147

147

00:07:26,160  -->  00:07:29,100
What happens then, if they have different passwords,
148

148

00:07:29,100  -->  00:07:32,683
such as password and password2 it will still return True.
149

149

00:07:35,040  -->  00:07:39,049
Note that if one of them is bob, and the other one is bob2,
150

150

00:07:39,049  -->  00:07:40,810
this will return False because the fields
151

151

00:07:40,810  -->  00:07:43,010
that we're comparing are no longer the same.
152

152

00:07:46,320  -->  00:07:49,540
In our MongoDB, we've been looking at
153

153

00:07:49,540  -->  00:07:52,610
how to make this _id property,
154

154

00:07:52,610  -->  00:07:55,870
something that isn't passed necessarily by the user.
155

155

00:07:55,870  -->  00:08:00,810
i.e, given it a uuid value by default.
156

156

00:08:00,810  -->  00:08:03,030
Because we no longer have an init method,
157

157

00:08:03,030  -->  00:08:07,240
you may think that you cannot set this value dynamically.
158

158

00:08:07,240  -->  00:08:08,590
But that's not true.
159

159

00:08:08,590  -->  00:08:11,150
You can always define a function
160

160

00:08:13,110  -->  00:08:14,160
such as generate_hex,
161

161

00:08:15,260  -->  00:08:19,110
make it return uuid.uuid4().hex,
162

162

00:08:19,110  -->  00:08:20,423
make sure to import that.
163

163

00:08:22,260  -->  00:08:24,150
And then in this id property,
164

164

00:08:24,150  -->  00:08:26,440
you can say, this is equal to a field.
165

165

00:08:26,440  -->  00:08:30,910
And the default factory is going to be this function here,
166

166

00:08:30,910  -->  00:08:35,070
make sure to pass in the function and not the function call.
167

167

00:08:35,070  -->  00:08:37,540
Pass in the function name, because this function is going
168

168

00:08:37,540  -->  00:08:41,400
to be evaluated when the object is created,
169

169

00:08:41,400  -->  00:08:44,320
if the id property is not given,
170

170

00:08:44,320  -->  00:08:46,480
and the return value of this function is going
171

171

00:08:46,480  -->  00:08:50,410
to be used as a value for that property at runtime,
172

172

00:08:50,410  -->  00:08:53,400
so it will calculate the new uuid value
173

173

00:08:53,400  -->  00:08:55,450
and it will assign it to the id property.
174

174

00:08:56,680  -->  00:08:59,510
What this does in the background is essentially
175

175

00:09:00,420  -->  00:09:03,473
this self.username = username.
176

176

00:09:05,170  -->  00:09:07,940
self.password = password,
177

177

00:09:07,940  -->  00:09:12,450
self._id = _id
178

178

00:09:12,450  -->  00:09:14,383
or generate_hex.
179

179

00:09:17,257  -->  00:09:20,563
And of course you do need username, password, _id.
180

180

00:09:23,503  -->  00:09:25,660
It essentially does something like this.
181

181

00:09:25,660  -->  00:09:28,760
But due to the way that these properties work,
182

182

00:09:28,760  -->  00:09:32,200
you cannot have this one at the top.
183

183

00:09:32,200  -->  00:09:34,510
Fields with a default value must come
184

184

00:09:34,510  -->  00:09:36,520
after fields without a default.
185

185

00:09:36,520  -->  00:09:41,123
So this one has to come down here underneath the password.
186

186

00:09:43,050  -->  00:09:45,610
And of course, when you're working with your app.py,
187

187

00:09:45,610  -->  00:09:49,633
the id property must be at the end if you wanna pass it in.
188

188

00:09:51,810  -->  00:09:53,443
Then when we run this, you see
189

189

00:09:53,443  -->  00:09:56,530
that you get username and bob printed out
190

190

00:09:56,530  -->  00:09:59,970
and username bob and the id printed out there.
191

191

00:09:59,970  -->  00:10:02,053
If you don't pass in the id,
192

192

00:10:03,440  -->  00:10:07,523
then a uuid will be generated for us.
193

193

00:10:09,780  -->  00:10:12,850
To make things simpler, you can remove this function here
194

194

00:10:12,850  -->  00:10:15,113
and instead, use a lambda function.
195

195

00:10:18,760  -->  00:10:20,250
This is exactly the same thing
196

196

00:10:20,250  -->  00:10:23,810
you are passing on to the default factory function.
197

197

00:10:23,810  -->  00:10:27,583
And when you run this function, you get a new uuid value.
198

198

00:10:28,470  -->  00:10:30,510
So this one is no longer needed.
199

199

00:10:30,510  -->  00:10:32,730
And I actually prefer this notation a little bit better
200

200

00:10:32,730  -->  00:10:35,400
because this lambda function is short enough
201

201

00:10:35,400  -->  00:10:40,170
that it is readable in a single sweep of your eyes
202

202

00:10:40,170  -->  00:10:43,020
to see what it's gonna do when it runs.
203

203

00:10:43,020  -->  00:10:44,530
What this default factory's gonna do is
204

204

00:10:44,530  -->  00:10:48,143
is gonna give you a new uuid value when it runs.
205

205

00:10:49,160  -->  00:10:52,070
Notice that you cannot do this.
206

206

00:10:52,070  -->  00:10:56,480
You cannot say, default_factory=uuid.uuid4.hex
207

207

00:10:56,480  -->  00:11:00,060
Because this is not a function.
208

208

00:11:00,060  -->  00:11:03,890
So if you run this, you will get an error saying
209

209

00:11:03,890  -->  00:11:08,470
that the string that this returns is not callable,
210

210

00:11:08,470  -->  00:11:10,360
because what it's trying to do in there
211

211

00:11:10,360  -->  00:11:14,113
again, is essentially this.
212

212

00:11:15,390  -->  00:11:18,940
Where this fun is this factory that we passed in.
213

213

00:11:18,940  -->  00:11:21,930
So the username and password will be passed in as strings,
214

214

00:11:21,930  -->  00:11:24,990
but this function will be called.
215

215

00:11:24,990  -->  00:11:27,290
And because this is not a function is a string,
216

216

00:11:27,290  -->  00:11:29,410
it cannot be called so you get an error saying
217

217

00:11:29,410  -->  00:11:31,743
you cannot call the string object.
218

218

00:11:35,550  -->  00:11:39,980
However, you can pass in defaults that are not functions.
219

219

00:11:39,980  -->  00:11:41,410
For example, let's say that we wanted
220

220

00:11:41,410  -->  00:11:44,560
to define a country for our users.
221

221

00:11:44,560  -->  00:11:46,190
And we're gonna say that this has
222

222

00:11:46,190  -->  00:11:49,800
a default value of United Kingdom.
223

223

00:11:49,800  -->  00:11:52,030
That's totally fine, just say default
224

224

00:11:52,030  -->  00:11:53,590
and not default factory.
225

225

00:11:53,590  -->  00:11:56,060
And now if you don't pass in a country,
226

226

00:11:56,060  -->  00:11:58,750
then the United Kingdom will be used.
227

227

00:11:58,750  -->  00:11:59,670
By the way, we do have
228

228

00:11:59,670  -->  00:12:01,523
to bring back this lambda there.
229

229

00:12:03,000  -->  00:12:05,410
See, now we've got our user with username bob,
230

230

00:12:05,410  -->  00:12:07,100
the id that we've generated,
231

231

00:12:07,100  -->  00:12:09,170
and the country which is United Kingdom.
232

232

00:12:09,170  -->  00:12:12,230
Do note that because both id
233

233

00:12:12,230  -->  00:12:14,660
and country have default values,
234

234

00:12:14,660  -->  00:12:16,870
no one of them has to be at the end,
235

235

00:12:16,870  -->  00:12:20,260
they both have to be after the non default fields.
236

236

00:12:20,260  -->  00:12:24,230
But the order of the ones that have a default is irrelevant.
237

237

00:12:24,230  -->  00:12:26,633
I do like putting the id at the end, though.
238

238

00:12:31,090  -->  00:12:32,850
I would prefer it if the id was at the start,
239

239

00:12:32,850  -->  00:12:34,110
but you can't do that.
240

240

00:12:34,110  -->  00:12:37,063
So and that's just how it is.
241

241

00:12:39,780  -->  00:12:40,823
So this is a few of the things
242

242

00:12:40,823  -->  00:12:43,390
that dataclasses can do for us,
243

243

00:12:43,390  -->  00:12:47,560
it can help us define an init method more effectively.
244

244

00:12:47,560  -->  00:12:51,750
It helps with the repr method and makes it better.
245

245

00:12:51,750  -->  00:12:54,460
It also greatly helps with comparisons,
246

246

00:12:54,460  -->  00:12:58,860
because now you can compare different objects very easily
247

247

00:12:58,860  -->  00:13:02,860
because it automatically will compare the fields for you.
248

248

00:13:02,860  -->  00:13:06,370
It also does a bunch more things, including hashing,
249

249

00:13:06,370  -->  00:13:11,370
and equality operators, and a whole host of stuff.
250

250

00:13:11,800  -->  00:13:15,070
Think of dataclasses as a code generator.
251

251

00:13:15,070  -->  00:13:17,900
It is writing the code for you
252

252

00:13:17,900  -->  00:13:20,900
and putting it into your class, but you never get to see it.
253

253

00:13:21,830  -->  00:13:23,360
So that's good if you trust
254

254

00:13:23,360  -->  00:13:26,960
that the code it's generating is good for you.
255

255

00:13:26,960  -->  00:13:28,240
That's why you have to understand a bit
256

256

00:13:28,240  -->  00:13:30,990
about dataclasses before you start using them,
257

257

00:13:30,990  -->  00:13:33,483
so that you're comfortable with what it's doing.
258

258

00:13:35,540  -->  00:13:38,120
I'm going to link in the resources section of this lecture,
259

259

00:13:38,120  -->  00:13:41,550
a talk, which talks more about dataclasses,
260

260

00:13:41,550  -->  00:13:43,910
it goes a lot, much more in-depth.
261

261

00:13:43,910  -->  00:13:45,740
I think it's a really great talk
262

262

00:13:45,740  -->  00:13:47,740
and I would recommend that you watch it.
263

263

00:13:48,660  -->  00:13:51,000
But for now, we're going to be using dataclasses
264

264

00:13:51,000  -->  00:13:54,650
in our project, just to make things a little bit simpler.
265

265

00:13:54,650  -->  00:13:56,030
So join me there.
266

266

00:13:56,030  -->  00:13:57,580
I'll see you in the next video.
