Skip to content
Snippets Groups Projects
Commit 86028b67 authored by Ulrich Kerzel's avatar Ulrich Kerzel
Browse files

change the name of the variable of the name of the dog in the class so it's...

change the name of the variable of the name of the dog in the class so it's not the same as the name of the getter method.
parent 94286d34
No related branches found
No related tags found
No related merge requests found
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
# Classes # Classes
So far, we have approached Python in a procedural way: Starting from a long list of statements, we have introduced functions to make our code more efficient, more reusable and also cleaner. So far, we have approached Python in a procedural way: Starting from a long list of statements, we have introduced functions to make our code more efficient, more reusable and also cleaner.
The functions took common datatypes, from single variables to dicionaries, as input and we could also return such entities. The functions took common datatypes, from single variables to dicionaries, as input and we could also return such entities.
Classes take this concept further and allow us to define our own entities (or objects). We distinguish between the "class" itself, this describes how this object looks like, which information it stores, or which functionality (defined by functions) it has. We can, informally, understand the class as a construction plan. If we want to make use of classes, we create "instances" of the class that then represent the actual objects. Classes take this concept further and allow us to define our own entities (or objects). We distinguish between the "class" itself, this describes how this object looks like, which information it stores, or which functionality (defined by functions) it has. We can, informally, understand the class as a construction plan. If we want to make use of classes, we create "instances" of the class that then represent the actual objects.
For example, we may define a class "dog": This class allows us to describe the dog (e.g. fur colour, it's name or race), and what it can do (e.g. bark). The instances of this class then refer to specific dogs. For example, we may define a class "dog": This class allows us to describe the dog (e.g. fur colour, it's name or race), and what it can do (e.g. bark). The instances of this class then refer to specific dogs.
The general setup of a class is: The general setup of a class is:
``` ```
class class_name(): class class_name():
def __init__(self, <optional parameters>): def __init__(self, <optional parameters>):
#... any initialisations we need to do.... #... any initialisations we need to do....
def my_function_1(self, <optional parameters>): def my_function_1(self, <optional parameters>):
# ... define the function .... # ... define the function ....
# with or without return value # with or without return value
#... define more functions if needed ... #... define more functions if needed ...
``` ```
**We notice:** **We notice:**
* The class is defined by the keyword ```class```, followed by the name of the class * The class is defined by the keyword ```class```, followed by the name of the class
* Each class needs to be initialised, this is done in the function defined with ```___init___()```. The function with a double underscore before and after the keyword is called a "dunder" or "magic" method in python. These methods are not intended to be called directly (by us), but they are typically called internally by some other method or action. In our case, this "magic" method is automatically called when we create an instance of this class. This initialisation function can take one or more optional arguments. Typically, we define variables here that define the properties of our class. * Each class needs to be initialised, this is done in the function defined with ```___init___()```. The function with a double underscore before and after the keyword is called a "dunder" or "magic" method in python. These methods are not intended to be called directly (by us), but they are typically called internally by some other method or action. In our case, this "magic" method is automatically called when we create an instance of this class. This initialisation function can take one or more optional arguments. Typically, we define variables here that define the properties of our class.
* The indicator ```self``` indicates the instance of this class. It is not a keyword as such (we could use some other word) - but pretty much everyone uses *self* to avoid confusion. We use this to refer to, e.g., the variables or functions that are part of this class. * The indicator ```self``` indicates the instance of this class. It is not a keyword as such (we could use some other word) - but pretty much everyone uses *self* to avoid confusion. We use this to refer to, e.g., the variables or functions that are part of this class.
* A class can have one or more functions that define the functionality. They can (or not) have return values, they may operate on internal variables of the class, etc. * A class can have one or more functions that define the functionality. They can (or not) have return values, they may operate on internal variables of the class, etc.
A simple class for dogs could be: A simple class for dogs could be:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
class Dog(): class Dog():
def __init__(self): def __init__(self):
pass pass
def bark(self): def bark(self):
print('Woof') print('Woof')
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Note that here we do not have anything to initialise - but we still need the magic ```__init___()``` function. We can use the keyword ```pass``` to tell python to "pass over" this function and move on. Note that here we do not have anything to initialise - but we still need the magic ```__init___()``` function. We can use the keyword ```pass``` to tell python to "pass over" this function and move on.
We also note that the function "bark" needs to take ```self``` as an argument - we need to tell Python, even though the code is indented below the class, that this method is part of the class. We also note that the function "bark" needs to take ```self``` as an argument - we need to tell Python, even though the code is indented below the class, that this method is part of the class.
> We also used upper case letters to define the class. This is a common convention. > We also used upper case letters to define the class. This is a common convention.
We can then create an instance of the class to refer to a specific dog and let it bark: We can then create an instance of the class to refer to a specific dog and let it bark:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
# create an instance # create an instance
dog = Dog() dog = Dog()
# bark # bark
dog.bark() dog.bark()
``` ```
%% Output %% Output
Woof Woof
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
This is not the most exciting dog... Let's add some variables This is not the most exciting dog... Let's add some variables
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
class Dog(): class Dog():
def __init__(self, name, fur_color, clean = True): def __init__(self, name, fur_color, clean = True):
self.name = name self.dog_name = name
self.fur_color = fur_color self.fur_color = fur_color
self.clean = clean self.clean = clean
def bark(self): def bark(self):
print('Woof') print('Woof')
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Here, we added a few variables, making the last one optional with a default argument. Here, we added a few variables, making the last one optional with a default argument.
The variables we created are associated with the class again by using ```self``` beforehand. The variables we created are associated with the class again by using ```self``` beforehand.
**Private and public variables** **Private and public variables**
> All variables in python are public. > All variables in python are public.
All variables that are part of the class are public, i.e. we cannot prevent someone to access them directly and change them. For example: All variables that are part of the class are public, i.e. we cannot prevent someone to access them directly and change them. For example:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
# create an instance of the class # create an instance of the class
dog = Dog('Rocky', 'brown') dog = Dog('Rocky', 'brown')
dog.bark() dog.bark()
print('The name of the dog is {}'.format(dog.name)) print('The name of the dog is {}'.format(dog.dog_name))
``` ```
%% Output %% Output
Woof Woof
The name of the dog is Rocky The name of the dog is Rocky
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
However, this is not really considered good practice - we should avoid to temper with the internals of the class directly but instead write "setter" and "getter" methods. However, this is not really considered good practice - we should avoid to temper with the internals of the class directly but instead write "setter" and "getter" methods.
The "setter" methods take an argument and set the respective variables to the new value, for example "set_name(new_name)" should update ```self.name``` to ```new_name```. The "setter" methods take an argument and set the respective variables to the new value, for example "set_name(new_name)" should update ```self.name``` to ```new_name```.
The "getter" methods should return the value of the variable, e.g. ```get_name()``` or just ```name()```. The "getter" methods should return the value of the variable, e.g. ```get_name()``` or just ```name()```.
### Exercise ### Exercise
Extend the class definition by a "getter" method ```name()``` that returns the name of the dog, so that we can write: Extend the class definition by a "getter" method ```name()``` that returns the name of the dog, so that we can write:
```print('The name of the dog is {}'.format(dog.name()))``` ```print('The name of the dog is {}'.format(dog.name()))```
> Trying to avoid accesing class variables avoids potential bugs a little as we interact with the variables in a controlled way. > Trying to avoid accesing class variables avoids potential bugs a little as we interact with the variables in a controlled way.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
class Dog(): class Dog():
def __init__(self, name, fur_color, clean = True): def __init__(self, name, fur_color, clean = True):
self.name = name self.dog_name = name
self.fur_color = fur_color self.fur_color = fur_color
self.clean = clean self.clean = clean
def bark(self): def bark(self):
print('Woof') print('Woof')
# ... your code here ... # ... your code here ...
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
dog = Dog('Rocky', 'brown') dog = Dog('Rocky', 'brown')
#print('The name of the dog is {}'.format(dog.name())) #print('The name of the dog is {}'.format(dog.name()))
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
**Getting help** **Getting help**
Python has an inbuilt function that provides more information about objects and classes. You can access it anytime using Python has an inbuilt function that provides more information about objects and classes. You can access it anytime using
> help(object) > help(object)
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
help(dog) help(dog)
``` ```
%% Output %% Output
Help on Dog in module __main__ object: Help on Dog in module __main__ object:
class Dog(builtins.object) class Dog(builtins.object)
| Dog(name, fur_color) | Dog(name, fur_color)
| |
| Methods defined here: | Methods defined here:
| |
| __init__(self, name, fur_color) | __init__(self, name, fur_color)
| Initialize self. See help(type(self)) for accurate signature. | Initialize self. See help(type(self)) for accurate signature.
| |
| bark(self) | bark(self)
| |
| is_clean(self) | is_clean(self)
| |
| play(self) | play(self)
| |
| wash(self) | wash(self)
| |
| ---------------------------------------------------------------------- | ----------------------------------------------------------------------
| Data descriptors defined here: | Data descriptors defined here:
| |
| __dict__ | __dict__
| dictionary for instance variables (if defined) | dictionary for instance variables (if defined)
| |
| __weakref__ | __weakref__
| list of weak references to the object (if defined) | list of weak references to the object (if defined)
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### Exercise ### Exercise
Imagine we are a bank and need to establish a way for us to track the transactions with customers. Imagine we are a bank and need to establish a way for us to track the transactions with customers.
In a very simple scenario, we need an account for each customer, that has the following properties: In a very simple scenario, we need an account for each customer, that has the following properties:
* It is associated with a customer with their name. * It is associated with a customer with their name.
* For simplicity, we only consider euros (i.e. no cents, just integer values). * For simplicity, we only consider euros (i.e. no cents, just integer values).
* Customers can put money into the account, withdraw money and ask for the balance. * Customers can put money into the account, withdraw money and ask for the balance.
* The bank does not offer credit or overdraft. * The bank does not offer credit or overdraft.
Implement this as a class. Implement this as a class.
First, think about why the above requirements are ambiguous and not sufficient (and then come up with a way to mitigate this.) First, think about why the above requirements are ambiguous and not sufficient (and then come up with a way to mitigate this.)
Then, think about which cases you need to consider in your implementation. Then, think about which cases you need to consider in your implementation.
*Hints* *Hints*
* The function ```int(number)``` converts number to an integer. * The function ```int(number)``` converts number to an integer.
* If we need to return "nothing", we can use the keyword ```None```. * If we need to return "nothing", we can use the keyword ```None```.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
# ... your code here ... # ... your code here ...
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
# Test your account # Test your account
account = Account('Smith') account = Account('Smith')
print('Opened account for {}'.format(account.name())) print('Opened account for {}'.format(account.name()))
print('Current balance: {}'.format(account.balance())) print('Current balance: {}'.format(account.balance()))
# make a deposit # make a deposit
account.deposit(10) account.deposit(10)
print('Current balance: {}'.format(account.balance())) print('Current balance: {}'.format(account.balance()))
# try to make an invalid deposit -- should produce an error message # try to make an invalid deposit -- should produce an error message
account.deposit(-10) account.deposit(-10)
# try to make an invalid deposit -- should produce an error message # try to make an invalid deposit -- should produce an error message
account.deposit(10.5) account.deposit(10.5)
# try to make an invalid deposit -- should produce an error message # try to make an invalid deposit -- should produce an error message
account.deposit('ten') account.deposit('ten')
# withdraw some money # withdraw some money
my_money = account.withdrawal(5) my_money = account.withdrawal(5)
print('I have now {} euros in my hand'.format(my_money)) print('I have now {} euros in my hand'.format(my_money))
print('Current balance: {}'.format(account.balance())) print('Current balance: {}'.format(account.balance()))
# try to make an invalid withdrawal -- should produce an error message and we have "nothing" in our hand # try to make an invalid withdrawal -- should produce an error message and we have "nothing" in our hand
my_money = account.withdrawal(-10) my_money = account.withdrawal(-10)
print('I have now {} euros in my hand'.format(my_money)) print('I have now {} euros in my hand'.format(my_money))
# try to make an invalid withdrawal -- should produce an error message and we have "nothing" in our hand # try to make an invalid withdrawal -- should produce an error message and we have "nothing" in our hand
my_money = account.withdrawal(10.5) my_money = account.withdrawal(10.5)
print('I have now {} euros in my hand'.format(my_money)) print('I have now {} euros in my hand'.format(my_money))
# try to make an invalid withdrawal -- should produce an error message and we have "nothing" in our hand # try to make an invalid withdrawal -- should produce an error message and we have "nothing" in our hand
my_money = account.withdrawal('ten') my_money = account.withdrawal('ten')
print('I have now {} euros in my hand'.format(my_money)) print('I have now {} euros in my hand'.format(my_money))
# try to make an invalid withdrawal -- should produce an error message and we have "nothing" in our hand # try to make an invalid withdrawal -- should produce an error message and we have "nothing" in our hand
my_money = account.withdrawal(500) my_money = account.withdrawal(500)
print('I have now {} euros in my hand'.format(my_money)) print('I have now {} euros in my hand'.format(my_money))
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
***Note*** ***Note***
In our implementation so far, we have resorted to a rather crude error handling by printing out an error statement. In our implementation so far, we have resorted to a rather crude error handling by printing out an error statement.
This is already quite good as we test for unexpected behaviour - but it would be better to deal with the erronous cases in a different way. This is called "exception handling" which we will focus on later. This is already quite good as we test for unexpected behaviour - but it would be better to deal with the erronous cases in a different way. This is called "exception handling" which we will focus on later.
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment