self
? Why use it?__init__
? Why use it?Often, we can't trust users to do the right thing. Thus, with classes, we often engage in data hiding. Data hiding is when we hide fields from the user and force them to use methods to access and modify fields. These methods can then validate the values coming in from the user. They also help to ensure data abstraction and encapsulation. By default, all members are publicly-accessible, that is they are accessible both in the class and outside of the class. Private members are those that are only directly-accessible inside the class (i.e. the members are hidden).
To hide a field (or method) from the user, start the member's name with two underscores. This causes Python to secretly mangle the name so that it isn't easily accessible outside of the class. Inside the class, you can still access the member using its name (including the two underscores).
For example, we want an age
field in our Person
class, but it only makes sense for age to be a positive number (maybe even just a positive int). So, we don't want the age field to be publicly accessible (why not?). To make age private, we name it __age
. See the example below:
The user of the Person
class can still access name directly but they can't access age:
If you want to allow the user to access your private fields, we need to write set and get methods (also called setters and getters or mutators and accessors). A get method gets the value of a field. A set method sets, modifies, or mutates the value of a field. Traditionally, accessors follow the naming convention "get_field_name
" and mutators follow the naming convention "set_field_name
". For example:
Now, if the user wants to access age, they can use the set_age
and get_age
methods. Notice that the set_age
method also validates the age, ensuring that it is a positive int. Notice also that the __init__
method now calls the set_age
method instead of directly setting __age
. Why do you think this change was made?
Your setter should do validation of the value before doing the assignment. In many cases, your setter and getter methods should save and return copies of the fields and not the originals so that the field's value can only be modified through the setter. We'll talk more about this when we cover data aggregatioin.
In many other programming languages, it is strongly recommended to make all fields private. Python is more lax about that, but it is still a good idea to make your fields private.
With some fields public (e.g. name
in the Person
class) and other fields private (e.g. age
in the Person
class), accessing fields becomes inconsistent. The programmer will need to memorize which fields they can access directly and which fields they must use methods for.
One solution to this problem is to provide set/get methods for all fields. A lot of programming languages take this approach (and also say that almost all fields should be private). This is an option for Python as well. In our Person
class, we could do:
In the code above, notice that name
was changed to __name
, set/get methods were written, and set_name
is used in the __init__
method.
While this solves our problem of inconsistent access to fields, it has the unfortunately effect of causing the user to type more to access a field. For example, now the user has to type:
instead of the shorter option of:
Python offers an alternative to this first solution: create properties. Properties are basically pseudo-fields. The user can treat them like a field, but secretly the user is interacting with methods. To create a property, first write set and get methods. Then, use the property function to create the property by passing in the get and set methods (do not call these methods when passing them in). The property function returns the pseudo-field, so store it into a field with the name you want to pseudo-field to have. For example:
To now use these properties, treat them just like a field:
There are other things you can do with properties, such as provide a del
method and provide documentation. We won't cover these features. However, the last thing to cover with properties is that you can use them to make constant fields. Constant fields are fields that do not change their values. To create a constant field, just create a get method, then just pass in the get method to the property
function. In the example below, two constant fields are created: birthday
and home_planet
and some other changes are made to support those fields (all new code is bolded and red):
Notice that:
birthday
property only has a getter, meaning it is not possible for the user to change the birthdayhome_planet
is a constant field whose value is just a string literal ('Earth'
)Python provides a long list of special methods for classes. These special methods make working with classes a lot easier. One example is the __init__
method we saw above. There are many others. Some allow us to use common operators (e.g. ==
, >
, [index]
), others perform helpful actions (e.g. when the object is created or destroyed), others help other actions work (such as using an object in a dictionary or set), or just letting us use standard Python syntax to accomplish what we want.
For now, we will look at just one other: __eq__
. Lab 8 will give you practice with another special method (the __str__
method). The __eq__
is used to compare two objects for equality. It is called when the equality operator (==
) is used.
How two objects are "equal" depends on the problems you're trying to solve and the situation you're working on. How do you determine whether two people are the same? Are two people the same if they have the same name? Are they the same if they have the same name and same birthdate? For our example, we will assume two people are the same if they have the same name and birthdate.
In the code below, the additions are in bold red.
The __eq__
method must return a True or False value.
Python will automatically call this method when a programmer tries to compare a Person
object with something:
<< Previous Notes | Daily Schedule | Next Notes >> |