4.4. Dataclass Define Nested¶
4.4.1. Relation to Objects¶
>>> from dataclasses import dataclass
>>>
>>>
>>> @dataclass
... class Mission:
... year: int
... name: str
>>>
>>>
>>> @dataclass
... class Astronaut:
... firstname: str
... lastname: str
... missions: list[Mission]
>>>
>>>
>>> astro = Astronaut('Mark', 'Watney', missions=[
... Mission(1973, 'Apollo 18'),
... Mission(2012, 'STS-136'),
... Mission(2035, 'Ares 3')])
>>>
>>> astro
Astronaut(firstname='Mark', lastname='Watney',
missions=[Mission(year=1973, name='Apollo 18'),
Mission(year=2012, name='STS-136'),
Mission(year=2035, name='Ares 3')])
4.4.2. Relation to Self¶
Note, that there are
None
default friendsUsing an empty list
[]
as a default value will not workWe will cover this topic later
>>> from dataclasses import dataclass
>>>
>>>
>>> @dataclass
... class Astronaut:
... firstname: str
... lastname: str
... friends: list['Astronaut'] = None
>>>
>>>
>>> astro = Astronaut('Mark', 'Watney', friends=[
... Astronaut('Melissa', 'Lewis'),
... Astronaut('Rick', 'Martinez'),
... Astronaut('Beth', 'Johanssen'),
... Astronaut('Chris', 'Beck'),
... Astronaut('Alex', 'Vogel')])
>>>
>>> astro
Astronaut(firstname='Mark', lastname='Watney',
friends=[Astronaut(firstname='Melissa', lastname='Lewis', friends=None),
Astronaut(firstname='Rick', lastname='Martinez', friends=None),
Astronaut(firstname='Beth', lastname='Johanssen', friends=None),
Astronaut(firstname='Chris', lastname='Beck', friends=None),
Astronaut(firstname='Alex', lastname='Vogel', friends=None)])
4.4.3. Assignments¶
"""
* Assignment: Dataclass DefineRelations Syntax
* Complexity: easy
* Lines of code: 16 lines
* Time: 5 min
English:
1. You received input data in JSON format from the API
2. Using `dataclass` model `DATA`:
a. Create class `Pet`
b. Create class `Category`
c. Create class `Tags`
3. Model relations between classes
4. Run doctests - all must succeed
Polish:
1. Otrzymałeś z API dane wejściowe w formacie JSON
2. Wykorzystując `dataclass` zamodeluj `DATA`:
a. Stwórz klasę `Pet`
b. Stwórz klasę `Category`
c. Stwórz klasę `Tags`
3. Zamodeluj relacje między klasami
4. Uruchom doctesty - wszystkie muszą się powieść
References:
[1]: https://petstore.swagger.io/#/pet/getPetById
Tests:
>>> import sys; sys.tracebacklimit = 0
>>> from inspect import isclass
>>> from dataclasses import is_dataclass
>>> import json
>>> assert isclass(Pet)
>>> assert isclass(Category)
>>> assert isclass(Tag)
>>> assert is_dataclass(Pet)
>>> assert is_dataclass(Category)
>>> assert is_dataclass(Tag)
>>> fields = {'id', 'category', 'name', 'photoUrls', 'tags', 'status'}
>>> assert set(Pet.__dataclass_fields__.keys()) == fields, \
f'Invalid fields, your fields should be: {fields}'
>>> data = json.loads(DATA)
>>> result = Pet(**data)
>>> result.category = Category(**result.category)
>>> result.tags = [Tag(**tag) for tag in result.tags]
>>> result # doctest: +NORMALIZE_WHITESPACE
Pet(id=0, category=Category(id=0, name='dogs'), name='doggie',
photoUrls=['img/dogs/0.png'], tags=[Tag(id=0, name='dog'),
Tag(id=1, name='hot-dog')],
status='available')
"""
from dataclasses import dataclass
DATA = """
{
"id": 0,
"category": {
"id": 0,
"name": "dogs"
},
"name": "doggie",
"photoUrls": [
"img/dogs/0.png"
],
"tags": [
{
"id": 0,
"name": "dog"
},
{
"id": 1,
"name": "hot-dog"
}
],
"status": "available"
}
"""
# Using `dataclass` model `DATA`, create class `Category`
# type: Type
class Category:
...
# Using `dataclass` model `DATA`, create class `Tag`
# type: Type
class Tag:
...
# Using `dataclass` model `DATA`, create class `Pet`
# type: Type
class Pet:
...
"""
* Assignment: Dataclass DefineBasic DatabaseDump
* Complexity: medium
* Lines of code: 13 lines
* Time: 13 min
English:
1. You received input data in JSON format from the API
a. `str` fields: firstname, lastname, role, username, password, email,
b. `date` field: born,
c. `datetime` field: last_login (field is optional),
d. `bool` fields: is_active, is_staff, is_superuser,
e. `list[dict]` field: user_permissions
2. Using `dataclass` model data as class `User`
3. Do not create additional classes to represent `permission` filed,
leave it as `list[dict]`
4. Note, that fields order is important for tests to pass
5. Run doctests - all must succeed
Polish:
1. Otrzymałeś z API dane wejściowe w formacie JSON
a. pola `str`: firstname, lastname, role, username, password, email,
b. pole `date`: born,
c. pole `datetime`: last_login (pole jest opcjonalne),
d. pola `bool`: is_active, is_staff, is_superuser,
e. pola `list[dict]`: user_permissions
2. Wykorzystując `dataclass` zamodeluj dane za pomocą klasy `User`
3. Nie twórz dodatkowych klas do reprezentacji pola `permission`,
niech zostanie jako `list[dict]`
4. Zwróć uwagę, że kolejność pól ma znaczenie aby testy przechodziły
5. Uruchom doctesty - wszystkie muszą się powieść
Tests:
>>> import sys; sys.tracebacklimit = 0
>>> from inspect import isclass
>>> from dataclasses import is_dataclass
>>> from pprint import pprint
>>> assert isclass(User)
>>> assert is_dataclass(User)
>>> attributes = User.__dataclass_fields__.keys()
>>> list(attributes) # doctest: +NORMALIZE_WHITESPACE
['firstname', 'lastname', 'role', 'username', 'password', 'email', 'born',
'last_login', 'is_active', 'is_staff', 'is_superuser', 'user_permissions']
>>> pprint(User.__annotations__, sort_dicts=False)
{'firstname': <class 'str'>,
'lastname': <class 'str'>,
'role': <class 'str'>,
'username': <class 'str'>,
'password': <class 'str'>,
'email': <class 'str'>,
'born': <class 'datetime.date'>,
'last_login': datetime.datetime | None,
'is_active': <class 'bool'>,
'is_staff': <class 'bool'>,
'is_superuser': <class 'bool'>,
'user_permissions': list[dict]}
>>> result = [User(**user['fields']) for user in json.loads(DATA)]
>>> result[0] # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
User(firstname='Melissa',
lastname='Lewis',
role='commander',
username='mlewis',
password='pbkdf2_sha256$120000$gvEBNiCeTrYa0$5C+NiCeTrYsha1PHog...=',
email='melissa.lewis@nasa.gov',
born='1995-07-15',
last_login='1970-01-01T00:00:00.000+00:00',
is_active=True,
is_staff=True,
is_superuser=False,
user_permissions=[{'eclss': ['add', 'modify', 'view']},
{'communication': ['add', 'modify', 'view']},
{'medical': ['add', 'modify', 'view']},
{'science': ['add', 'modify', 'view']}])
"""
import json
from dataclasses import dataclass
from datetime import date, datetime
DATA = ('[{"model":"authorization.user","pk":1,"fields":{"firstname":"Melissa"'
',"lastname":"Lewis","role":"commander","username":"mlewis","password"'
':"pbkdf2_sha256$120000$gvEBNiCeTrYa0$5C+NiCeTrYsha1PHogqvXNiCeTrY0CRS'
'LYYAA90=","email":"melissa.lewis@nasa.gov","born":"1995-07-15","last_'
'login":"1970-01-01T00:00:00.000+00:00","is_active":true,"is_staff":tr'
'ue,"is_superuser":false,"user_permissions":[{"eclss":["add","modify",'
'"view"]},{"communication":["add","modify","view"]},{"medical":["add",'
'"modify","view"]},{"science":["add","modify","view"]}]}},{"model":"au'
'thorization.user","pk":2,"fields":{"firstname":"Rick","lastname":"Mar'
'tinez","role":"pilot","username":"rmartinez","password":"pbkdf2_sha25'
'6$120000$aXNiCeTrY$UfCJrBh/qhXohNiCeTrYH8nsdANiCeTrYnShs9M/c=","born"'
':"1996-01-21","last_login":null,"email":"rick.martinez@ansa.gov","is_'
'active":true,"is_staff":true,"is_superuser":false,"user_permissions":'
'[{"communication":["add","view"]},{"eclss":["add","modify","view"]},{'
'"science":["add","modify","view"]}]}},{"model":"authorization.user","'
'pk":3,"fields":{"firstname":"Alex","lastname":"Vogel","role":"chemist'
'","username":"avogel","password":"pbkdf2_sha256$120000$eUNiCeTrYHoh$X'
'32NiCeTrYZOWFdBcVT1l3NiCeTrY4WJVhr+cKg=","email":"alex.vogel@esa.int"'
',"born":"1994-11-15","last_login":null,"is_active":true,"is_staff":tr'
'ue,"is_superuser":false,"user_permissions":[{"eclss":["add","modify",'
'"view"]},{"communication":["add","modify","view"]},{"medical":["add",'
'"modify","view"]},{"science":["add","modify","view"]}]}},{"model":"au'
'thorization.user","pk":4,"fields":{"firstname":"Chris","lastname":"Be'
'ck","role":"crew-medical-officer","username":"cbeck","password":"pbkd'
'f2_sha256$120000$3G0RNiCeTrYlaV1$mVb62WNiCeTrYQ9aYzTsSh74NiCeTrY2+c9/'
'M=","email":"chris.beck@nasa.gov","born":"1999-08-02","last_login":"1'
'970-01-01T00:00:00.000+00:00","is_active":true,"is_staff":true,"is_su'
'peruser":false,"user_permissions":[{"communication":["add","view"]},{'
'"medical":["add","modify","view"]},{"science":["add","modify","view"]'
'}]}},{"model":"authorization.user","pk":5,"fields":{"firstname":"Beth'
'","lastname":"Johanssen","role":"sysop","username":"bjohanssen","pass'
'word":"pbkdf2_sha256$120000$QmSNiCeTrYBv$Nt1jhVyacNiCeTrYSuKzJ//Wdyjl'
'NiCeTrYYZ3sB1r0g=","email":"","born":"2006-05-09","last_login":null,"'
'is_active":true,"is_staff":true,"is_superuser":false,"user_permission'
's":[{"communication":["add","view"]},{"science":["add","modify","view'
'"]}]}},{"model":"authorization.user","pk":6,"fields":{"firstname":"Ma'
'rk","lastname":"Watney","role":"botanist","username":"mwatney","passw'
'ord":"pbkdf2_sha256$120000$bxS4dNiCeTrY1n$Y8NiCeTrYRMa5bNJhTFjNiCeTrY'
'p5swZni2RQbs=","email":"","born":"1994-10-12","last_login":null,"is_a'
'ctive":true,"is_staff":true,"is_superuser":false,"user_permissions":['
'{"communication":["add","modify","view"]},{"science":["add","modify",'
'"view"]}]}}]')
# Using `dataclass` model data as class `User`
# a. `str` fields: firstname, lastname, role, username, password, email,
# b. `date` field: born,
# c. `datetime` field: last_login (optional),
# c. `bool` fields: is_active, is_staff, is_superuser,
# d. `list[dict]` field: user_permissions
# Leave `permission` attribute as `list[dict]`
# Note, that fields order is important for tests to pass
# type: Type
class User:
...