13.13. OOP Methods and Attributes¶
Methods are functions in the class
First argument is always instance (
self
)While calling function you never pass
self
Prevents copy-paste code
Improves readability
Improves refactoring
Decomposes bigger problem into smaller chunks
Syntax:
>>> class MyClass:
... def __init__(self):
... self.myfield = 'some value'
...
... def mymethod(self):
... return self.myfield
>>>
>>>
>>> my = MyClass()
>>> my.mymethod()
'some value'
13.13.1. Methods Accessing Fields¶
Methods Accessing Fields:
>>> class Astronaut:
... def __init__(self, name):
... self.name = name
...
... def say_hello(self):
... return f'My name... {self.name}'
>>>
>>>
>>> jose = Astronaut('José Jiménez')
>>> jose.say_hello()
'My name... José Jiménez'
self.name
must be defined before accessing:
>>> class Astronaut:
... def say_hello(self):
... return f'My name... {self.name}'
>>>
>>>
>>> jose = Astronaut()
>>> jose.say_hello()
Traceback (most recent call last):
AttributeError: 'Astronaut' object has no attribute 'name'
13.13.2. Methods Calling Other Methods¶
Methods Calling Other Methods:
>>> class Astronaut:
... def get_name(self):
... return 'José Jiménez'
...
... def say_hello(self):
... name = self.get_name()
... return f'My name... {name}'
>>>
>>>
>>> jose = Astronaut()
>>> jose.say_hello()
'My name... José Jiménez'
Methods calling other methods:
>>> class Iris:
... def __init__(self):
... self.sepal_length = 5.1
... self.sepal_width = 3.5
... self.petal_length = 1.4
... self.petal_width = 0.2
...
... def sepal_area(self):
... return self.sepal_length * self.sepal_width
...
... def petal_area(self):
... return self.petal_length * self.petal_width
...
... def total_area(self):
... return self.sepal_area() + self.petal_area()
>>>
>>>
>>> flower = Iris()
>>> flower.total_area()
18.13
Since Python 3.7 there is a @dataclass
decorator, which automatically
generates __init__()
method with arguments and set-up fields for you.
More information in OOP Dataclass.
>>> from dataclasses import dataclass
>>>
>>>
>>> @dataclass
... class Iris:
... sepal_length = 5.1
... sepal_width = 3.5
... petal_length = 1.4
... petal_width = 0.2
... species: str = 'Iris'
...
... def sepal_area(self):
... return self.sepal_length * self.sepal_width
...
... def petal_area(self):
... return self.petal_length * self.petal_width
...
... def total_area(self):
... return self.sepal_area() + self.petal_area()
>>>
>>>
>>> flower = Iris()
>>> flower.total_area()
18.13
13.13.3. Examples¶
>>> class Point:
... def __init__(self, x, y, z):
... self.x = x
... self.y = y
... self.z = z
...
... def get_coordinates(self):
... return self.x, self.y, self.z
...
... def show(self):
... return f'Point(x={self.x}, y={self.y}, z={self.z})'
>>>
>>>
>>> point = Point(x=1, y=2, z=3)
>>>
>>> vars(point)
{'x': 1, 'y': 2, 'z': 3}
>>>
>>> point.get_coordinates()
(1, 2, 3)
>>>
>>> point.show()
'Point(x=1, y=2, z=3)'
13.13.4. Use Case - 0x01¶
>>> class Counter:
... current_value: int
...
... def __init__(self):
... self.current_value = 0
...
... def increment(self):
... self.current_value += 1
...
... def decrement(self):
... self.current_value -= 1
... if self.current_value < 0:
... raise ValueError('Cannot decrement below zero')
...
... 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.13.5. Use Case - 0x02¶
>>> from typing import Literal
>>>
>>>
>>> class Car:
... engine: Literal['on', 'off']
...
... def __init__(self):
... self.engine = 'off'
...
... def engine_start(self):
... self.engine = 'on'
...
... def engine_stop(self):
... self.engine = 'off'
...
... def drive_to(self, location: str):
... if self.engine != 'on':
... raise RuntimeError('Engine must be turned on to drive')
... else:
... return f'Driving to {location}'
>>>
>>>
>>> maluch = Car()
>>>
>>> maluch.drive_to('Cologne, Germany')
Traceback (most recent call last):
RuntimeError: Engine must be turned on to drive
>>>
>>> maluch.engine
'off'
>>>
>>> maluch.engine_start()
>>> maluch.engine
'on'
>>>
>>> maluch.drive_to('Cologne, Germany')
'Driving to Cologne, Germany'
>>>
>>> maluch.engine_stop()
>>> maluch.engine
'off'
13.13.6. Use Case - 0x03¶
Documentation: https://atlassian-python-api.readthedocs.io
Source Code: https://github.com/atlassian-api/atlassian-python-api
Examples: https://github.com/atlassian-api/atlassian-python-api/tree/master/examples
$ pip install atlassian-python-api
>>>
... from atlassian import Jira
...
... jira = Jira(
... url='https://example.com:8080',
... username='myusername',
... password='mypassword')
...
... JQL = 'project = DEMO AND status IN ("To Do", "In Progress") ORDER BY issuekey'
...
... result = jira.jql(JQL)
... print(result)
>>>
... from atlassian import Confluence
...
... confluence = Confluence(
... url='https://example.com:8090',
... username='myusername',
... password='mypassword')
...
... result = confluence.create_page(
... space='DEMO',
... title='This is the title',
... body='This is the body. You can use <strong>HTML tags</strong>!')
...
... print(result)
13.13.7. Assignments¶
"""
* Assignment: OOP Method Sequence
* Required: yes
* Complexity: easy
* Lines of code: 9 lines
* Time: 8 min
English:
1. Create class `Iris` with `features: list[float]` and `label: str` fields
2. For each row in `DATA` create `Iris` instance with row values
3. Set class attributes at the initialization from positional arguments
4. Create method which sums values of all `features`
5. In `result` gather species and sum of each row
6. Run doctests - all must succeed
Polish:
1. Stwórz klasę `Iris` z polami `features: list[float]` i `label: str`
2. Dla każdego wiersza w `DATA` twórz instancję `Iris` z danymi z wiersza
3. Ustaw atrybuty klasy przy inicjalizacji z argumentów pozycyjnych
4. Stwórz metodę sumującą wartości wszystkich `features`
5. W `result` zbieraj nazwę gatunku i sumę z każdego wiersza
6. Uruchom doctesty - wszystkie muszą się powieść
Tests:
>>> import sys; sys.tracebacklimit = 0
>>> result # doctest: +NORMALIZE_WHITESPACE
{'setosa': 9.4,
'versicolor': 16.299999999999997,
'virginica': 19.3}
"""
DATA = [
(4.7, 3.2, 1.3, 0.2, 'setosa'),
(7.0, 3.2, 4.7, 1.4, 'versicolor'),
(7.6, 3.0, 6.6, 2.1, 'virginica'),
]
result = {}
"""
* Assignment: OOP Method Nested
* Required: yes
* Complexity: medium
* Lines of code: 17 lines
* Time: 13 min
English:
1. Define class `Iris`
2. `Iris` has:
a. "Sepal length" type `float`
b. "Sepal width" type `float`
c. "Petal length" type `float`
d. "Petal width" type `float`
e. "Species" type `str`
3. `Iris` can:
a. Return number of `float` type attributes
b. Return list of all `float` type attributes
c. Return sum of values of all `float` type attributes
d. Return mean of all `float` type attributes
4. Use `vars(self)` iteration to return values of numeric fields
5. Create `setosa` object with attributes set at the initialization
6. Create `virginica` object with attributes set at the initialization
7. Method `.show()` returns sum, mean and species name, example:
a. 'total=10.20 mean=2.55 setosa'
b. 'total=15.50 mean=3.88 virginica'
8. Do not use `@dataclass`
9. Run doctests - all must succeed
Polish:
1. Zdefiniuj klasę `Iris`
2. `Iris` ma:
a. "Sepal length" typu `float`
b. "Sepal width" typu `float`
c. "Petal length" typu `float`
d. "Petal width" typu `float`
e. "Species" typu `str`
3. `Iris` może:
a. Zwrócić liczbę pól typu `float`
b. Zwrócić listę wartości wszystkich pól typu `float`
c. Zwrócić sumę wartości pól typu `float`
d. Zwrócić średnią arytmetyczną wartość pól typu `float`
4. Użyj iterowania po `vars(self)` do zwrócenia wartości pól numerycznych
7. Method `.show()` returns sumę, średnią oraz nazwę gatunku, przykład:
a. 'total=10.20 mean=2.55 setosa'
b. 'total=15.50 mean=3.88 virginica'
8. Nie używaj `@dataclass`
9. Uruchom doctesty - wszystkie muszą się powieść
Hints:
* `type(value) is float`
* `vars(self).values()`
* `{total=:.2f}`
* `{mean=:.2f}`
Tests:
>>> import sys; sys.tracebacklimit = 0
>>> setosa = Iris(5.1, 3.5, 1.4, 0.2, 'setosa')
>>> virginica = Iris(5.8, 2.7, 5.1, 1.9, 'virginica')
>>> setosa.show()
'total=10.20 mean=2.55 setosa'
>>> virginica.show()
'total=15.50 mean=3.88 virginica'
"""
class Iris:
def __init__(self, sepal_length, sepal_width,
petal_length, petal_width, species):
self.sepal_length = sepal_length
self.sepal_width = sepal_width
self.petal_length = petal_length
self.petal_width = petal_width
self.species = species
def features(self):
return [x for x in vars(self).values() if type(x) is float]