|
| 1 | +--- |
| 2 | +parent: Classes and Objects |
| 3 | +grand_parent: Programming for Modelling and Data Analysis |
| 4 | +nav_order: 2 |
| 5 | +--- |
| 6 | + |
| 7 | +# Classes and objects to organize your code |
| 8 | + |
| 9 | +The problem described in the previous chapter can be summarized as follows: how to combine together all the variables describing each player's state and how to operate on them. Python has neat solution for this using a concept of classes and objects. Classes are general concepts of an entity, its state and what it can do (in our case this will be **a** player), while objects are specific instances of these concepts (**the** players: *Alfred*, *Beatrice*, *Charlie*). |
| 10 | + |
| 11 | +It is best illustrated in example. In Python you define a class using **`class`** keyword: |
| 12 | + |
| 13 | +```python |
| 14 | +class Player: |
| 15 | + |
| 16 | + def __init__(self, name): |
| 17 | + self.name = name |
| 18 | + self.score = 0 |
| 19 | + self.in_game = True |
| 20 | + |
| 21 | + def roll(self): |
| 22 | + dice = random.randint(1, 6) |
| 23 | + self.score += dice |
| 24 | + score = self.score # `score` and `self.score` are DIFFERENT THINGS |
| 25 | + if score > 21: |
| 26 | + self.score = 0 |
| 27 | + in_game = False |
| 28 | + return dice, score |
| 29 | +``` |
| 30 | + |
| 31 | +What you see above is a declaration of a class named `Player` (usually class names should start with a CapitalLetter), are two functions defined **inside a class** (in an indented block). Such functions in a class are called *methods*: I will use this term from now on. Methods in a class take one additional argument called `self`, which must be always the first argument. This argument represents a particular instance (object) we are operating on. We can use it to set and read object *attributes*, using `self.attribute_name` notation. Contrary to the local variables, the attributes are shared between the calls of the methods and may also be accessed from outside. On the other hand, unlike global variables, they can be different for different objects (i.e. we track e.g. `score` separately for *Alfred* and separately for *Beatrice*). |
| 32 | + |
| 33 | +I guess, you understand the role of the `roll` method. `__init__` is the special method name, used for initialization of objects (it will be called automatically). Here, we set the player's name to the value provided as argument `name` (this a different thing than the attribute `self.name`) and initialize the score (`self.score`) and playing status (`self.in_game`). |
| 34 | + |
| 35 | +To create particular instances of a class, we call it like a function: |
| 36 | + |
| 37 | +```python |
| 38 | +player1 = Player("Alfred") |
| 39 | +player2 = Player("Beatrice") |
| 40 | +``` |
| 41 | + |
| 42 | +In the parenthesis we put the arguments that must be passed to the `__init__` method, **skipping the first argument `self`**. |
| 43 | + |
| 44 | +Other methods are called using notation, you have already seen: `object.method(arguments_without_self)`: |
| 45 | + |
| 46 | +```python |
| 47 | +player1.roll() |
| 48 | +player2.roll() |
| 49 | +``` |
| 50 | + |
| 51 | +For creating objects and calling methods, all the normal rules of passing arguments by name or using default arguments are the same as for normal functions. The only difference you must remember is to skip the first `self` argument. This argument is passed automatically and contains the object before the dot (or a newly created one in case of `__init__`). |
| 52 | + |
| 53 | +You use the dot-notation, you may access not only methods, but also object attributes: |
| 54 | + |
| 55 | +```python |
| 56 | +print(player1.score) |
| 57 | +``` |
| 58 | + |
| 59 | +## Two-player game |
| 60 | + |
| 61 | +Using classes and objects it is now very easy to write a two-player game: |
| 62 | + |
| 63 | +```python |
| 64 | +import random |
| 65 | + |
| 66 | + |
| 67 | +class Player: |
| 68 | + def __init__(self, name): |
| 69 | + self.name = name |
| 70 | + self.score = 0 |
| 71 | + self.in_game = True |
| 72 | + |
| 73 | + def roll(self): |
| 74 | + dice = random.randint(1, 6) |
| 75 | + self.score += dice |
| 76 | + score = self.score # `score` and `self.score` are DIFFERENT THINGS |
| 77 | + if score > 21: |
| 78 | + self.score = 0 |
| 79 | + self.in_game = False |
| 80 | + return dice, score |
| 81 | + |
| 82 | + |
| 83 | +def ask(player): |
| 84 | + # This function is part on the input-output, |
| 85 | + # so it should not be in the `Player` class |
| 86 | + while True: |
| 87 | + answer = input(f"{player.name}, do you want to keep rolling? [y/n]: ").lower() |
| 88 | + if answer in 'yn': |
| 89 | + break |
| 90 | + print("You must answer 'y' or 'n'!") |
| 91 | + return answer == 'y' |
| 92 | + |
| 93 | + |
| 94 | +player1 = Player(input("Enter first player's name: ")) |
| 95 | +player2 = Player(input("Enter second player's name: ")) |
| 96 | + |
| 97 | +while player1.in_game or player2.in_game: |
| 98 | + for player in (player1, player2): |
| 99 | + if player.in_game: |
| 100 | + dice, score = player.roll() |
| 101 | + print(f"{player.name} roll {dice} and has total score {score}.") |
| 102 | + # We check the condition again, as it might have changed in `player.roll()` |
| 103 | + if player.in_game: |
| 104 | + player.in_game = ask(player) |
| 105 | + |
| 106 | +print(f"{player1.name}'s final score is {player1.score}!") |
| 107 | +print(f"{player2.name}'s final score is {player2.score}!") |
| 108 | +``` |
| 109 | + |
| 110 | +You may download the game code [from here](game21.py). Can you write this game to allow any number of players? |
| 111 | + |
| 112 | +## Conclusions |
| 113 | + |
| 114 | +This tutorial went through creating classes, instantiating objects, initializing attributes with the constructor method, and working with more than one object of the same class. |
| 115 | + |
| 116 | +Object-oriented programming is an important concept to understand because it makes code recycling more straightforward, as objects created for one program can be used in another. Object-oriented programs also make for better program design since complex programs are difficult to write and require careful planning, and this in turn makes it less work to maintain the program over time. |
| 117 | + |
| 118 | + |
| 119 | +<hr/> |
| 120 | + |
| 121 | +Published under [Creative Commons Attribution-NonCommercial-ShareAlike](https://creativecommons.org/licenses/by-nc-sa/4.0/) license. |
0 commit comments