6.9. Relations¶
6.9.1. Relations¶
class Astronaut:
def __init__(self, firstname, lastname, missions=()):
self.firstname = firstname
self.lastname = lastname
self.missions = list(missions)
class Mission:
def __init__(self, year, name):
self.year = year
self.name = name
DATA = [
Astronaut('Jan', 'Twardowski', missions=[
Mission('1967', 'Apollo 1'),
Mission('1970', 'Apollo 13'),
Mission('1973', 'Apollo 18')]),
Astronaut('Ivan', 'Ivanovic', missions=[
Mission('2023', 'Artemis 2'),
Mission('2024', 'Artemis 3')]),
Astronaut('Mark', 'Watney', missions=[
Mission('2035', 'Ares 3')]),
Astronaut('Melissa', 'Lewis'),
]
6.9.2. Serialization¶
pickle
- has relationsjson
- has relationscsv
- non-relational format

Figure 6.1. Relational files or database dump¶

Figure 6.2. Ffill - Forward fill¶

Figure 6.3. Fill in specified columns¶

Figure 6.4. Data duplication with unique ID¶

Figure 6.5. Each relations attribute adds one column¶

Figure 6.6. Each relations instance adds one column¶

Figure 6.7. Each relations class adds one column¶

Figure 6.8. Relations attributes split into columns¶

Figure 6.9. Hybrid compact and separate columns¶
6.9.3. Assignments¶
"""
* Assignment: OOP Relations Syntax
* Filename: oop_relations_syntax.py
* Complexity: easy
* Lines of code: 7 lines
* Time: 5 min
English:
1. Use Dataclass to define class `Point` with attributes:
a. `x: int` with default value `0`
b. `y: int` with default value `0`
2. Use Dataclass to define class `Path` with attributes:
a. `points: list[Point]` with default empty list
3. Compare result with "Tests" section (see below)
Polish:
1. Użyj Dataclass do zdefiniowania klasy `Point` z atrybutami:
a. `x: int` z domyślną wartością `0`
b. `y: int` z domyślną wartością `0`
2. Użyj Dataclass do zdefiniowania klasy `Path` z atrybutami:
a. `points: list[Point]` z domyślną pustą listą
3. Porównaj wyniki z sekcją "Tests" (patrz poniżej)
Tests:
>>> from inspect import isclass
>>> assert isclass(Point)
>>> assert isclass(Path)
>>> assert hasattr(Point, 'x')
>>> assert hasattr(Point, 'y')
>>> Point()
Point(x=0, y=0)
>>> Point(x=0, y=0)
Point(x=0, y=0)
>>> Point(x=1, y=2)
Point(x=1, y=2)
>>> Path([Point(x=0, y=0),
... Point(x=0, y=1),
... Point(x=1, y=0)])
Path(points=[Point(x=0, y=0), Point(x=0, y=1), Point(x=1, y=0)])
"""
# Given
from dataclasses import dataclass, field
"""
* Assignment: OOP Relations Model
* Filename: oop_relations_model.py
* Complexity: easy
* Lines of code: 10 lines
* Time: 13 min
English:
1. Use data from "Given" section (see below)
2. In `DATA` we have two classes
3. Model data using classes and relations
4. Create instances dynamically based on `DATA`
5. Compare result with "Tests" section (see below)
Polish:
1. Użyj danych z sekcji "Given" (patrz poniżej)
2. W `DATA` mamy dwie klasy
3. Zamodeluj problem wykorzystując klasy i relacje między nimi
4. Twórz instancje dynamicznie na podstawie `DATA`
5. Porównaj wyniki z sekcją "Tests" (patrz poniżej)
Tests:
>>> assert type(result) is list
>>> assert all(type(astro) is Astronaut
... for astro in result)
>>> assert all(type(addr) is Address
... for astro in result
... for addr in astro.addresses)
>>> result # doctest: +NORMALIZE_WHITESPACE
[Astronaut(firstname='Jan',
lastname='Twardowski',
addresses=[Address(street='Kamienica Pod św. Janem Kapistranem', city='Kraków', postcode='31-008', region='Małopolskie', country='Poland')]),
Astronaut(firstname='José',
lastname='Jiménez',
addresses=[Address(street='2101 E NASA Pkwy', city='Houston', postcode=77058, region='Texas', country='USA'),
Address(street='', city='Kennedy Space Center', postcode=32899, region='Florida', country='USA')]),
Astronaut(firstname='Mark',
lastname='Watney',
addresses=[Address(street='4800 Oak Grove Dr', city='Pasadena', postcode=91109, region='California', country='USA'),
Address(street='2825 E Ave P', city='Palmdale', postcode=93550, region='California', country='USA')]),
Astronaut(firstname='Иван',
lastname='Иванович',
addresses=[Address(street='', city='Космодро́м Байкону́р', postcode='', region='Кызылординская область', country='Қазақстан'),
Address(street='', city='Звёздный городо́к', postcode=141160, region='Московская область', country='Россия')]),
Astronaut(firstname='Melissa',
lastname='Lewis',
addresses=[]),
Astronaut(firstname='Alex',
lastname='Vogel',
addresses=[Address(street='Linder Hoehe', city='Köln', postcode=51147, region='North Rhine-Westphalia', country='Germany')])]
"""
# Given
from dataclasses import dataclass
from typing import Optional, Union
DATA = [
{"firstname": "Jan", "lastname": "Twardowski", "addresses": [
{"street": "Kamienica Pod św. Janem Kapistranem", "city": "Kraków", "postcode": "31-008", "region": "Małopolskie", "country": "Poland"}]},
{"firstname": "José", "lastname": "Jiménez", "addresses": [
{"street": "2101 E NASA Pkwy", "city": "Houston", "postcode": 77058, "region": "Texas", "country": "USA"},
{"street": "", "city": "Kennedy Space Center", "postcode": 32899, "region": "Florida", "country": "USA"}]},
{"firstname": "Mark", "lastname": "Watney", "addresses": [
{"street": "4800 Oak Grove Dr", "city": "Pasadena", "postcode": 91109, "region": "California", "country": "USA"},
{"street": "2825 E Ave P", "city": "Palmdale", "postcode": 93550, "region": "California", "country": "USA"}]},
{"firstname": "Иван", "lastname": "Иванович", "addresses": [
{"street": "", "city": "Космодро́м Байкону́р", "postcode": "", "region": "Кызылординская область", "country": "Қазақстан"},
{"street": "", "city": "Звёздный городо́к", "postcode": 141160, "region": "Московская область", "country": "Россия"}]},
{"firstname": "Melissa", "lastname": "Lewis", "addresses": []},
{"firstname": "Alex", "lastname": "Vogel", "addresses": [
{"street": "Linder Hoehe", "city": "Köln", "postcode": 51147, "region": "North Rhine-Westphalia", "country": "Germany"}]}
]
"""
* Assignment: OOP Relations Movable
* Filename: oop_relations_movable.py
* Complexity: medium
* Lines of code: 18 lines
* Time: 21 min
English:
1. Define class `Point`
2. Class `Point` has attributes `x: int = 0` and `y: int = 0`
3. Define class `Movable`
4. In `Movable` define method `get_position(self) -> Point`
5. In `Movable` define method `set_position(self, x: int, y: int) -> None`
6. In `Movable` define method `change_position(self, left: int = 0, right: int = 0, up: int = 0, down: int = 0) -> None`
7. Assume left-top screen corner as a initial coordinates position:
a. going right add to `x`
b. going left subtract from `x`
c. going up subtract from `y`
d. going down add to `y`
8. Compare result with "Tests" section (see below)
Polish:
1. Zdefiniuj klasę `Point`
2. Klasa `Point` ma atrybuty `x: int = 0` oraz `y: int = 0`
3. Zdefiniuj klasę `Movable`
4. W `Movable` zdefiniuj metodę `get_position(self) -> Point`
5. W `Movable` zdefiniuj metodę `set_position(self, x: int, y: int) -> None`
6. W `Movable` zdefiniuj metodę `change_position(self, left: int = 0, right: int = 0, up: int = 0, down: int = 0) -> None`
7. Przyjmij górny lewy róg ekranu za punkt początkowy:
a. idąc w prawo dodajesz `x`
b. idąc w lewo odejmujesz `x`
c. idąc w górę odejmujesz `y`
d. idąc w dół dodajesz `y`
8. Porównaj wyniki z sekcją "Tests" (patrz poniżej)
Tests:
>>> from inspect import isclass, ismethod
>>> assert isclass(Point)
>>> assert isclass(Movable)
>>> assert hasattr(Point, 'x')
>>> assert hasattr(Point, 'y')
>>> assert hasattr(Movable, 'get_position')
>>> assert hasattr(Movable, 'set_position')
>>> assert hasattr(Movable, 'change_position')
>>> assert ismethod(Movable().get_position)
>>> assert ismethod(Movable().set_position)
>>> assert ismethod(Movable().change_position)
>>> class Astronaut(Movable):
... pass
>>> astro = Astronaut()
>>> astro.set_position(x=1, y=2)
>>> astro.get_position()
Point(x=1, y=2)
>>> astro.set_position(x=1, y=1)
>>> astro.change_position(right=1)
>>> astro.get_position()
Point(x=2, y=1)
>>> astro.set_position(x=1, y=1)
>>> astro.change_position(left=1)
>>> astro.get_position()
Point(x=0, y=1)
>>> astro.set_position(x=1, y=1)
>>> astro.change_position(down=1)
>>> astro.get_position()
Point(x=1, y=2)
>>> astro.set_position(x=1, y=1)
>>> astro.change_position(up=1)
>>> astro.get_position()
Point(x=1, y=0)
"""
from dataclasses import dataclass
"""
* Assignment: OOP Relations Flatten
* Filename: oop_relations_flatten.py
* Complexity: hard
* Lines of code: 5 lines
* Time: 21 min
English:
1. Use code from "Given" section (see below)
2. How to write relations to CSV file (contact has many addresses)?
3. Convert `DATA` to `resul: list[dict[str,str]]`
4. Non-functional requirements:
a. Use `,` to separate fields
b. Use `;` to separate columns
5. Compare result with "Tests" section (see below)
Polish:
1. Użyj kodu z sekcji "Given" (patrz poniżej)
2. Jak zapisać w CSV dane relacyjne (kontakt ma wiele adresów)?
3. Przekonwertuj `DATA` do `resul: list[dict[str,str]]`
4. Wymagania niefunkcjonalne:
b. Użyj `,` do oddzielenia pól
b. Użyj `;` do oddzielenia kolumn
5. Porównaj wyniki z sekcją "Tests" (patrz poniżej)
Tests:
>>> result # doctest: +NORMALIZE_WHITESPACE
[{'firstname': 'Jan', 'lastname': 'Twardowski', 'missions': '1967,Apollo 1;1970,Apollo 13;1973,Apollo 18'},
{'firstname': 'Ivan', 'lastname': 'Ivanovic', 'missions': '2023,Artemis 2;2024,Artemis 3'},
{'firstname': 'Mark', 'lastname': 'Watney', 'missions': '2035,Ares 3'},
{'firstname': 'Melissa', 'lastname': 'Lewis', 'missions': ''}]
"""
# Given
class Astronaut:
def __init__(self, firstname, lastname, missions=()):
self.firstname = firstname
self.lastname = lastname
self.missions = list(missions)
class Mission:
def __init__(self, year, name):
self.year = year
self.name = name
DATA = [
Astronaut('Jan', 'Twardowski', missions=[
Mission('1967', 'Apollo 1'),
Mission('1970', 'Apollo 13'),
Mission('1973', 'Apollo 18')]),
Astronaut('Ivan', 'Ivanovic', missions=[
Mission('2023', 'Artemis 2'),
Mission('2024', 'Artemis 3')]),
Astronaut('Mark', 'Watney', missions=[
Mission('2035', 'Ares 3')]),
Astronaut('Melissa', 'Lewis')]
result: list = []
"""
* Assignment: OOP Relations Nested
* Filename: oop_relations_nested.py
* Complexity: medium
* Lines of code: 7 lines
* Time: 21 min
English:
1. Use data from "Given" section (see below)
2. Convert `DATA` to format with one column per each attrbute
for example: `street1`, `street2`, `city1`, `city2`, etc.
3. Compare result with "Tests" section (see below)
Polish:
1. Użyj danych z sekcji "Given" (patrz poniżej)
2. Przekonweruj `DATA` do formatu z jedną kolumną dla każdego atrybutu,
np. `street1`, `street2`, `city1`, `city2`, itd.
3. Porównaj wyniki z sekcją "Tests" (patrz poniżej)
Tests:
>>> type(result)
<class 'list'>
>>> result # doctest: +NORMALIZE_WHITESPACE
[{'firstname': 'Jan', 'lastname': 'Twardowski', 'street1': 'Kamienica Pod św. Janem Kapistranem', 'city1': 'Kraków', 'post_code1': '31-008', 'region1': 'Małopolskie', 'country1': 'Poland'},
{'firstname': 'José', 'lastname': 'Jiménez', 'street1': '2101 E NASA Pkwy', 'city1': 'Houston', 'post_code1': 77058, 'region1': 'Texas', 'country1': 'USA', 'street2': '', 'city2': 'Kennedy Space Center', 'post_code2': 32899, 'region2': 'Florida', 'country2': 'USA'},
{'firstname': 'Mark', 'lastname': 'Watney', 'street1': '4800 Oak Grove Dr', 'city1': 'Pasadena', 'post_code1': 91109, 'region1': 'California', 'country1': 'USA', 'street2': '2825 E Ave P', 'city2': 'Palmdale', 'post_code2': 93550, 'region2': 'California', 'country2': 'USA'},
{'firstname': 'Иван', 'lastname': 'Иванович', 'street1': '', 'city1': 'Космодро́м Байкону́р', 'post_code1': '', 'region1': 'Кызылординская область', 'country1': 'Қазақстан', 'street2': '', 'city2': 'Звёздный городо́к', 'post_code2': 141160, 'region2': 'Московская область', 'country2': 'Россия'},
{'firstname': 'Melissa', 'lastname': 'Lewis'},
{'firstname': 'Alex', 'lastname': 'Vogel', 'street1': 'Linder Hoehe', 'city1': 'Köln', 'post_code1': 51147, 'region1': 'North Rhine-Westphalia', 'country1': 'Germany'}]
"""
# Given
import json
DATA = """[
{"firstname": "Jan", "lastname": "Twardowski", "addresses": [
{"street": "Kamienica Pod św. Janem Kapistranem", "city": "Kraków", "post_code": "31-008", "region": "Małopolskie", "country": "Poland"}]},
{"firstname": "José", "lastname": "Jiménez", "addresses": [
{"street": "2101 E NASA Pkwy", "city": "Houston", "post_code": 77058, "region": "Texas", "country": "USA"},
{"street": "", "city": "Kennedy Space Center", "post_code": 32899, "region": "Florida", "country": "USA"}]},
{"firstname": "Mark", "lastname": "Watney", "addresses": [
{"street": "4800 Oak Grove Dr", "city": "Pasadena", "post_code": 91109, "region": "California", "country": "USA"},
{"street": "2825 E Ave P", "city": "Palmdale", "post_code": 93550, "region": "California", "country": "USA"}]},
{"firstname": "Иван", "lastname": "Иванович", "addresses": [
{"street": "", "city": "Космодро́м Байкону́р", "post_code": "", "region": "Кызылординская область", "country": "Қазақстан"},
{"street": "", "city": "Звёздный городо́к", "post_code": 141160, "region": "Московская область", "country": "Россия"}]},
{"firstname": "Melissa", "lastname": "Lewis", "addresses": []},
{"firstname": "Alex", "lastname": "Vogel", "addresses": [
{"street": "Linder Hoehe", "city": "Köln", "post_code": 51147, "region": "North Rhine-Westphalia", "country": "Germany"}]}
]"""
result = []