Write Pythonic Code Like a Seasoned Developer Transcripts
Chapter: Classes and Objects
Lecture: Encapsulation and data hiding
0:01 Encapsulation and data hiding is a key building block for object-oriented design. And Python, being a very object-oriented language,
0:07 or at least having great support for object-oriented programming, of course, has ways to do this.
0:13 But I would say it's less Pythonic to focus heavily on these ideas,
0:16 things like private variables, and whatnot with inside classes or protected variables that only derived classes can see, things like this.
0:25 But let's look at it anyway. So, over here we have a class called PetSnake, and you can give it a name and you can give it an age,
0:31 and you can supply this protected value but it builds it up as you create it and possibly these changes over time, right,
0:38 this is a great simplification of anything you might really use. And, like before, we have this string overwrite,
0:43 where we can print out some information about this. So here we can say "Here is my pet snake:", I want her called Slide, it's 6 years old,
0:50 and we can just print it out, that will call this method, we can also access its values directly here, so let's just run this.
0:57 Great, here is my pet snake, age 6, looks like it has an age and a name backwards but that's fine, and the protection level here, perfect.
1:04 There is nothing wrong with this class, it seems fine to me, but what if we wanted the age and the name to be read-only?
1:11 Once you have created a snake, you can't change its name, once you've created a snake, you can't change its age,
1:16 other than possibly having some way to like give it a birthday or something like that.
1:20 First of all, let me switch these, because this is kind of bugging me, this is backwards,
1:24 so in Python, there is a way to do this and let's work with this protected value,
1:29 let's suppose that we would like this to be accessible to derive classes but we want to indicate to the consumers of it,
1:35 "hey you probably shouldn't be messing with it", so let's just, before we change it, let's print it out over here,
1:40 so here we'll print it out and see everything is fine, if you look at the warnings form PyCharm, no warnings.
1:45 So the way that you indicate something should not be consumed outside of the class or more generally outside modules,
1:51 sort of externally is to say "_" as the prefix. So now if I say this, notice, this goes away,
1:57 obviously, because it doesn't exist, but we can put it back, that's fine, this goes away because it doesn't exist, we can put it back,
2:04 but now we have this warning and PyCharm is saying: "Access to a protected member such and such of class
2:10 you probably shouldn't be doing this unless you know what you are doing." However, this is just a warning, it still works.
2:17 Notice here we are reading the name and the age but we just as well could, and we are going to say "py.name = py.name.upper()", something like that,
2:27 so now we have Slide, so we are actually changing the type, OK and maybe I'll change this print statement order as well
2:33 here we go, SLIDE and SLIDE, capital. So what if we don't want this to be possible, we want read-only access to this and we'd have to provide a way
2:40 to get to it which, we'll get to later. So the way you do that in Python is you use double underscores, and of course down here,
2:48 those names changed, let me put this back for just a second, if we really want to make this change we can hit Ctrl+T
2:54 and do double underscore and change it everywhere Ctrl+T to save me some typing and be safer, of course.
3:01 You can see it changes everywhere but down here PyCharm is like "not so sure this is going to work well for you",
3:06 unresolved reference, well, maybe it's just hiding from us, maybe it's saying you know, you really shouldn't access this,
3:12 we are going to tell you that it's not there. If I say "py." and the only reason it thinks the name is there is because we are doing this line,
3:18 if I take this line away, there is no name, and it thought that line was creating the thing called __name which it would have,
3:24 if we set it, you can see those don't show up. OK, so now let's run it and see what happens. Boom, PetSnake has no thing called __name
3:34 and yet if I hide this, it does seem to have __name, so what is going on here? So you really can't access it by name here,
3:43 so let's look, so we'll look inside of type and say "what methods and fields does it have?" with this thing called dir, so I can "dir(py)"
3:51 and ask: "What basically features do you have?" It'll show us all the various things here,
3:57 so if we come over and we look for, here is our protected value, let's go and add just one normal value,
4:04 so I'll just say "self.normal = True", so here at the end is our normal value,
4:09 here is our protected one so we can't get to it, we are just told "you probably shouldn't".
4:13 So here we are saying "self.__age", "self.__name" and that seems to work,
4:18 but it's actually got this rewritten name, where it's rewritten based on the type.
4:24 So technically, you could come over here and copy this out and access this, and it certainly wouldn't look like what's written up here
4:31 and it would tell you "you know, you probably should stay away from that." This is how you do private fields within classes in Python,
4:37 here is how you do protected ones. And of course, doing neither of those, makes it just a normal type. OK, so here is our PetSnake in a graphic.
4:46 We saw if we wanted the age and the name to be completely private, we use double underscores,
4:52 if we want to have a protected variable that we want to strongly encourage people
4:55 to stay away from, we use single underscores and you saw that we get warnings on the linters and things like that,
5:01 so if we go and write some code that tries to access this type, here we can see we are creating a PetSnake called a py
5:07 we'll get to this property thing in a moment, if we want to say "py._protected", we can, but that does give us a warning, if we try to say "py.__age",
5:19 we saw that it crashes and basically that name doesn't exist, it's technically rewritten to be kind of hidden but normal access doesn't work for it.