Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Hint: You can adjust the default video playback speed in your account settings.
Hint: You can set your subtitle preferences in your account settings.
Sorry! Looks like there’s an issue with video playback 🙁 This might be due to a temporary outage or because of a configuration issue with your browser. Please refer to our video player troubleshooting guide for assistance.

Access Control Continued

00:00 Welcome to lesson six in Object-Oriented Programming in Python versus Java. In your last lesson, you learned about access control through properties to control access to certain attributes of your class. In this lesson, we will take a look at another example.

00:21 The example I have in mind is a clock. So, a Clock class might have a field for hours with a value that’s going to be between 1 and 12 and a field for minutes with a value that’s going to be between 0 and 59.

00:37 This is the behavior we’re going to be taking a look at. We’re going to look at a partial implementation of a Python class that just controls that the minutes should be between 0 and 59.

00:49 We would do something similar for hours, we would also have methods to set the time and increment each minute—not the scope of this lesson, so we’re just going to focus on that portion of the class that wants to restrict the value of minute between 0 and 59.

01:11 And we will look at two different versions of this class. So, here’s our first version. We define non-public attributes of ._hours and ._minutes.

01:24 We know they’re non-public by the presence of the underscore (_). The initializer method provides values through the parameters. And I’ve written a property for minutes that if I refer to .minutes directly to inquire its value as a property, it will return the value of the non-public attribute ._minutes.

01:45 And, more importantly, I have a setter method that if I’ve tried to provide a new value for min, and if it’s at least 0 but less than 60, it’s valid.

02:01 I will reassign the non-public attribute to that value. And if I’m given an illegal value, it will leave the value of ._minutes the same. Let’s take a look at that.

02:17 So, let’s import. I called this clock1, not to confuse it with clock2. And I’m going to create a Clock, call it alarm. It equals clock1.Clock(), and we’ll say that my alarm is set for 6:30.

02:43 And I’ll just create a very boring and bland print statement, f"The value of minutes is {alarm.minutes}". And the value of minutes for our alarm clock is 30.

03:00 And I can change that. Say I need to get up earlier one morning, set it to 6:15. The setter was called, checked the value of 15, saw that it was between 0 and 59 inclusive, and it allowed that value to change.

03:20 Now, if I try changing it to an illegal value,

03:26 say 75, my setter did not produce an error message—that was my choice. Depending on where this clock is being used, an error message may or may not be visible to someone to actually see it, but it did prevent the value of ._minutes from being changed because 6:75 is not a valid time to set your clock.

03:53 My second version makes a slight change that makes it impossible to set an illegal value upon creation. I come back over here, create a new Clock.

04:07 I’ll call it bad_clock.

04:11 If I try to create a Clock with the time set for 7:65,

04:23 and then see what the value of bad clock’s minutes is,

04:30 Python was just fine to assign it that value because I didn’t have anything in the initializer to prevent it. The initializer doesn’t use our property to get to it.

04:46 This new version changes that. I redefine the attribute in the initializer to refer to the property—not a non-public attribute. I then define the property .minutes. When we use .minutes directly it’s going to return the value of the non-public attribute, which is actually created the first time a setter is used.

05:14 And so my setter will be used for the very first time during the constructor if the parameter minutes, which is going to be the right-hand operand in an assignment statement, is legal. Again, I set the value of the non-public attribute to that parameter, and if this happens in the constructor call, this is actually created.

05:38 So, the non-public attribute doesn’t get created until the constructor call refers to it through the property using its setter. Now, there are two times where this setter might be used, where this test for a legal value could fail. One, the Clock object has already been created, and we want to ignore the new value since it’s illegal.

06:03 But the other time it could happen is during the initializer call, and so we’ll check to see if that attribute actually exists and if that attribute hasn’t been created and we had tried to assign a value that’s illegal for ._minutes, we’ll just set ._minutes to 0.

06:20 So, let’s take a look and see what this would look like. So, let me import clock2, create a new alarm clock using clock2. And again, I’m going to set an alarm to 6:30.

06:40 f"The value of alarm2's minutes is {alarm2.minutes}".

06:54 And that’s still 30, because during the initializer call we set the property value using the setter to minutes, 30 was a legal value used here, and so it created this non-public attribute and assigned it to the value 30, which was passed by the parameter to the initializer.

07:18 And then when I wanted to retrieve it from the property, it used that same value.

07:28 And again, if I want to try to change it, alarm2.minutes = 15, and repeat that statement—that works.

07:45 And if I try to set it to an illegal value—

07:51 again, no error message reported, by my choice. In your context, if you want an error message displayed, you can do that. But I can see that wasn’t changed. Now, if I want to try to create a new clock… We’ll call this bad2, clock2, and again, try to set it for 7:65 in the morning, whatever that would possibly mean.

08:19 Again, the statement executed because everything inside my class works. But now if I want to see, f"The value of bad2's minutes is {}"

08:37 Because I tried to create it with an illegal value, it used my default value of 0, so my alarm is set for 7:00 AM.

08:51 So, there are two versions of a Clock class, which prevent illegal values to be given to the ._minutes. In a fully implemented class, I would do the same thing with ._hours to keep them between 1 and 12, or between 0 and 23 if you would prefer a 24-hour clock. In our next lesson, we’ll take a look at Python’s use of the word self.

Danilo on Nov. 23, 2021

Is it okay to have both _minutes and minutes attributes?

I tested the code and if the value is legal, both attributes have the same value. When the value is illegal, _minutes is 0 and minutes is the illegal value.

Bartosz Zaczyński RP Team on Nov. 24, 2021

@Danilo As long as your attributes have distinct names, then having both is perfectly okay. However, it’s more idiomatic to define public attribute minutes as a @property backed by the “private” field _minutes as explained in the video.

Personally, I tend to use properties for dynamically calculated attributes derived from other fields. For example, a Person class could define the age property based on the current date and a particular person’s birth date fixed at the object’s creation time.

Become a Member to join the conversation.