13.4. OOP Methods

13.4.1. Rationale

  • Methods are functions in the class

  • Prevents copy-paste code

  • Improves readability

  • Improves refactoring

  • Decomposes bigger problem into smaller chunks

  • At this moment:

    • At definition - self should always be a first parameter

    • At call - self is not passed as an argument (Python will do that)

    • Later you will learn more advanced things like static methods etc.

method

Functions in the class which takes instance as first argument (self)

Syntax:

>>> class MyClass:
...     def mymethod(self):
...         pass
>>>
>>>
>>> my = MyClass()
>>> my.mymethod()

13.4.2. Method Parameters

  • At definition - self should always be a first parameter

  • Later you will learn more advanced things like static methods etc.

  • Parameter - Receiving variable used within the function

  • Required parameter:

    • Necessary to call that function

    • Specified at leftmost side

  • Default parameter:

    • Optional to call that function

    • Has default value

    • Default value will be overridden if specified at a call time

    • Specified at rightmost side

Without parameters:

>>> class Astronaut:
...     def say_hello(self):
...         print('My name... José Jiménez')

Methods with required parameter:

>>> class Astronaut:
...     def say_hello(self, firstname):
...         print(f'My name... {firstname}')

Method with optional parameter:

>>> class Astronaut:
...     def say_hello(self, firstname='unknown'):
...         print(f'My name... {firstname}')

Method with required and optional parameter:

>>> class Astronaut:
...     def say_hello(self, firstname, lastname='unknown'):
...         print(f'My name... {firstname} {lastname}')

13.4.3. Method Arguments

  • At call - self is not passed as an argument (Python will do that)

>>> class Astronaut:
...     def say_hello(self):
...         print('My name... José Jiménez')
...
...
>>> jose = Astronaut()
>>> jose.say_hello()
My name... José Jiménez

Method with positional argument:

>>> class Astronaut:
...     def say_hello(self, name):
...         print(f'My name... {name}')
>>>
>>>
>>> jose = Astronaut()
>>>
>>> jose.say_hello('José Jiménez')
My name... José Jiménez
>>>
>>> jose.say_hello()
Traceback (most recent call last):
TypeError: say_hello() missing 1 required positional argument: 'name'

Method with keyword argument:

>>> class Astronaut:
...     def say_hello(self, firstname, lastname):
...         print(f'My name... {firstname} {lastname}')
>>>
>>>
>>> jose = Astronaut()
>>>
>>> jose.say_hello(firstname='José', lastname='Jiménez')
My name... José Jiménez
>>>
>>> jose.say_hello(lastname='Jiménez', firstname='José')
My name... José Jiménez
>>>
>>> jose.say_hello()
Traceback (most recent call last):
TypeError: say_hello() missing 1 required positional argument: 'name'

13.4.4. Use Cases

>>> class Counter:
...     current_value: int
...
...     def __init__(self):
...         self.current_value = 0
...
...     def increment(self):
...         self.current_value += 1
...
...     def decrement(self):
...         if self.current_value - 1 < 0:
...             raise ValueError('Cannot decrement below zero')
...         self.current_value -= 1
...
...     def show(self):
...         return self.current_value
>>>
>>>
>>> c = Counter()
>>> c.increment()
>>> c.increment()
>>> c.show()
2
>>> c.decrement()
>>> c.decrement()
>>> c.show()
0
>>> c.decrement()
Traceback (most recent call last):
ValueError: Cannot decrement below zero

13.4.5. Assignments

Code 13.6. Solution
"""
* Assignment: OOP Method Syntax
* Required: yes
* Complexity: easy
* Lines of code: 3 lines
* Time: 3 min

English:
    1. Define class `Calculator`
    2. Define method `add` in class `Calculator`
    3. Method `add` take `a` and `b` as arguments
    4. Method returns sum of `a` and `b`
    5. Run doctests - all must succeed

Polish:
    1. Zdefiniuj klasę `Calculator`
    2. Zdefiniuj metodę `add` w klasie `Calculator`
    3. Metoda `add` przyjmuje `a` i `b` jako argumenty
    4. Metoda zwraca sumę `a` i `b`
    5. Uruchom doctesty - wszystkie muszą się powieść

Tests:
    >>> import sys; sys.tracebacklimit = 0
    >>> from inspect import isclass, ismethod

    >>> assert isclass(Calculator)
    >>> calc = Calculator()
    >>> assert ismethod(calc.add)
    >>> calc.add(1, 2)
    3
"""


Code 13.7. Solution
"""
* Assignment: OOP Method Mean
* Required: yes
* Complexity: easy
* Lines of code: 4 lines
* Time: 5 min

English:
    1. Define class `Stats`
    2. Define method `mean()` in `Stats` class
    3. Method takes `data: list[float]` as an argument
    4. Method returns arithmetic mean of the `data`
    5. Returned value must me rounded to one decimal places
    6. Run doctests - all must succeed

Polish:
    1. Zdefiniuj klasę `Stats`
    2. Zdefiniuj metodę `mean()` w klasie `Stats`
    3. Metoda przyjmuje `data: list[float]` jako argument
    4. Metoda zwraca średnią arytmetyczną z `data`
    5. Zwracana wartość ma być zaokrąglona do jednego miejsca po przecinku
    6. Uruchom doctesty - wszystkie muszą się powieść

Hints:
    * `round()`

Tests:
    >>> import sys; sys.tracebacklimit = 0
    >>> from inspect import isclass, ismethod

    >>> assert isclass(Stats)
    >>> stats = Stats()
    >>> assert ismethod(stats.mean)

    >>> stats.mean([1, 2])
    1.5
    >>> stats.mean([5.8, 2.7, 5.1, 1.9])
    3.9
"""


Code 13.8. Solution
"""
* Assignment: OOP Method Call
* Required: yes
* Complexity: easy
* Lines of code: 2 lines
* Time: 2 min

English:
    1. Create instance of `Stats` class
    2. Iterate over `DATA` skipping header
    3. Separate features from label
    4. Call `mean()` method of `Stats` class passing list of features
    5. Define `result: list[float]` with list of means from each row
    6. Run doctests - all must succeed

Polish:
    1. Stwórz instancję klasy `Stats`
    2. Iteruj po `DATA` pomijając nagłówek
    3. Rozdziel cechy od etykiety
    4. Wywołuj metodę `mean()` klasy `Stats` przekazując listę features
    5. Zdefiniuj `result: list[float]` z listą średnich każdego z wierszy
    6. Uruchom doctesty - wszystkie muszą się powieść

Hints:
    * `round()`

Tests:
    >>> import sys; sys.tracebacklimit = 0

    >>> assert type(result) is list
    >>> assert all(type(x) is float for x in result)

    >>> result
    [3.9, 2.5, 3.5, 4.1, 3.9, 2.4]
"""

DATA = [
    ('Sepal length', 'Sepal width', 'Petal length', 'Petal width', 'Species'),
    (5.8, 2.7, 5.1, 1.9, 'virginica'),
    (5.1, 3.5, 1.4, 0.2, 'setosa'),
    (5.7, 2.8, 4.1, 1.3, 'versicolor'),
    (6.3, 2.9, 5.6, 1.8, 'virginica'),
    (6.4, 3.2, 4.5, 1.5, 'versicolor'),
    (4.7, 3.2, 1.3, 0.2, 'setosa'),
]


class Stats:
    def mean(self, data):
        avg = sum(data) / len(data)
        return round(avg, 1)