6.3. OOP Dataclass¶
6.3.1. Syntax¶
This are not static fields!
Dataclasses require Type Annotations
Introduced in Python 3.7
Backported to Python 3.6 via
python3 -m pip install dataclasses
6.3.2. Example 1¶
class
:
class Point:
def __init__(self, x, y, z=0):
self.x = x
self.y = y
self.z = z
p0 = Point()
# Traceback (most recent call last):
# TypeError: __init__() missing 2 required positional arguments: 'x' and 'y'
p1 = Point(10)
# Traceback (most recent call last):
# TypeError: __init__() missing 1 required positional argument: 'y'
p2 = Point(10, 20)
p3 = Point(10, 20, 30)
p4 = Point(10, 20, z=30)
p5 = Point(10, 20, z=30)
p6 = Point(x=10, y=20, z=30)
dataclass
:
from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
z: int = 0
p0 = Point()
# Traceback (most recent call last):
# TypeError: __init__() missing 2 required positional arguments: 'x' and 'y'
p1 = Point(10)
# Traceback (most recent call last):
# TypeError: __init__() missing 1 required positional argument: 'y'
p2 = Point(10, 20)
p3 = Point(10, 20, 30)
p4 = Point(10, 20, z=30)
p5 = Point(10, 20, z=30)
p6 = Point(x=10, y=20, z=30)
6.3.3. Example 2¶
class
:
class Astronaut:
firstname: str
lastname: str
def __init__(self, firstname: str, lastname: str, agency: str = 'POLSA'):
self.firstname = firstname
self.lastname = lastname
self.agency = agency
twardowski = Astronaut('Jan', 'Twardowski')
print(twardowski.firstname) # Jan
print(twardowski.lastname) # Twardowski
print(twardowski.agency) # POLSA
dataclass
:
from dataclasses import dataclass
@dataclass
class Astronaut:
firstname: str
lastname: str
agency: str = 'POLSA'
twardowski = Astronaut('Jan', 'Twardowski')
print(twardowski.firstname) # Jan
print(twardowski.lastname) # Twardowski
print(twardowski.agency) # POLSA
6.3.4. Example 3¶
class
:
from datetime import datetime
class StarWarsMovie:
title: str
episode_id: int
opening_crawl: str
director: str
producer: str
release_date: datetime
characters: tuple[str]
planets: tuple[str]
starships: tuple[str]
vehicles: tuple[str]
species: tuple[str]
created: datetime
edited: datetime
url: str
def __init__(self, title: str, episode_id: int, opening_crawl: str,
director: str, producer: str, release_date: datetime,
characters: tuple[str], planets: tuple[str], starships: tuple[str],
vehicles: tuple[str], species: tuple[str], created: datetime,
edited: datetime, url: str):
self.title = title
self.episode_id = episode_id
self.opening_crawl= opening_crawl
self.director = director
self.producer = producer
self.release_date = release_date
self.characters = characters
self.planets = planets
self.starships = starships
self.vehicles = vehicles
self.species = species
self.created = created
self.edited = edited
self.url = url
dataclass
:
from dataclasses import dataclass
from datetime import datetime
@dataclass
class StarWarsMovie:
title: str
episode_id: int
opening_crawl: str
director: str
producer: str
release_date: datetime
characters: tuple[str]
planets: tuple[str]
starships: tuple[str]
vehicles: tuple[str]
species: tuple[str]
created: datetime
edited: datetime
url: str
6.3.5. __init__
vs. __post_init__
¶
class
:
class Kelvin:
def __init__(self, value):
if value < 0.0:
raise ValueError('Temperature must be greater than 0')
else:
self.value = value
t1 = Kelvin(273.15)
print(t1.value)
# 273.15
t2 = Kelvin(-10)
# Traceback (most recent call last):
# ValueError: Temperature must be greater than 0
dataclass
:
from dataclasses import dataclass
@dataclass
class Kelvin:
value: float = 0.0
def __post_init__(self):
if self.value < 0.0:
raise ValueError('Temperature must be greater than 0')
t1 = Kelvin(273.15)
print(t1.value)
# 273.15
t2 = Kelvin(-10)
# Traceback (most recent call last):
# ValueError: Temperature must be greater than 0
6.3.6. Field Factory¶
from dataclasses import dataclass, field
@dataclass
class Point:
x: int
y: int = field(repr=False)
z: int = field(repr=False, default=10)
t: int = 20
6.3.7. List attributes¶
You should not set mutable objects as a default function argument
field()
creates new emptylist
for each objectIt does not reuse pointer
Warning
Note, You should not set mutable objects as a default function argument. More information: Argument Mutability.
class Astronaut:
def __init__(self, name, missions=[]):
self.name = name
self.missions = missions
watney = Astronaut('Mark Watney')
watney.missions.append('Ares 1')
watney.missions.append('Ares 2')
watney.missions.append('Ares 3')
watney.missions.append('Ares 4')
watney.missions.append('Ares 5')
print('Watney:', watney.missions)
# Watney: ['Ares 1', 'Ares 2', 'Ares 3', 'Ares 4', 'Ares 5']
twardowski = Astronaut('Jan Twardowski')
print('Twardowski:', twardowski.missions)
# Twardowski: ['Ares 1', 'Ares 2', 'Ares 3', 'Ares 4', 'Ares 5']
from dataclasses import dataclass, field
@dataclass
class Mission:
year: int
name: str
@dataclass
class Astronaut:
firstname: str
lastname: str
missions: list[Mission] = field(default_factory=list)
astro = Astronaut('Mark', 'Watney')
6.3.8. Dataclass parameters¶
Option |
Default |
Description (if True) |
---|---|---|
|
|
Generate |
|
|
Generate |
|
|
Generate |
|
|
Generate |
|
|
if False: the |
|
|
if |
6.3.9. init¶
Generate
__init__()
method
from dataclasses import dataclass
@dataclass(init=False)
class Point:
x: int
y: int
p = Point(10, 20)
# Traceback (most recent call last):
# TypeError: Point() takes no arguments
6.3.10. repr¶
repr=True
by defaultGenerate
__repr__()
for pretty printing
from dataclasses import dataclass
@dataclass(repr=True)
class Point:
x: int
y: int
p = Point(10, 20)
print(p)
# Point(x=10, y=20)
from dataclasses import dataclass
@dataclass(repr=False)
class Point:
x: int
y: int
p = Point(10, 20)
print(p)
# <__main__.Point object at 0x110bf5550>
6.3.11. frozen¶
frozen=False
by defaultPrevents object from modifications
from dataclasses import dataclass
@dataclass(frozen=True)
class Point:
x: int
y: int
p = Point(10, 20)
p.x = 30
# Traceback (most recent call last):
# dataclasses.FrozenInstanceError: cannot assign to field 'x'
6.3.12. eq¶
eq=True
by defaultwhen
eq=False
compare objects byid()
not valueswhen
eq=True
compare objects by value notid()
from dataclasses import dataclass
@dataclass(eq=True)
class Astronaut:
firstname: str
lastname: str
astro1 = Astronaut('Mark', 'Watney')
astro2 = Astronaut('Mark', 'Watney')
astro3 = Astronaut('Jan', 'Twardowski')
astro1 == astro1 # True
astro1 == astro2 # True
astro1 == astro3 # False
astro1 != astro1 # False
astro1 != astro2 # False
astro1 != astro3 # True
from dataclasses import dataclass
@dataclass(eq=False)
class Astronaut:
firstname: str
lastname: str
astro1 = Astronaut('Mark', 'Watney')
astro2 = Astronaut('Mark', 'Watney')
astro3 = Astronaut('Jan', 'Twardowski')
astro1 == astro1 # True
astro1 == astro2 # False
astro1 == astro3 # False
astro1 != astro1 # False
astro1 != astro2 # True
astro1 != astro3 # True
6.3.13. other flags¶
from dataclasses import dataclass
@dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False)
class Astronaut:
firstname: str
lastname: str
astro1 = Astronaut('Mark', 'Watney')
astro2 = Astronaut('Mark', 'Watney')
astro3 = Astronaut('Jan', 'Twardowski')
6.3.14. Under the hood¶
Your code:
from dataclasses import dataclass
@dataclass
class ShoppingCartItem:
name: str
unit_price: float
quantity: int = 0
def total_cost(self) -> float:
return self.unit_price * self.quantity
Dataclass will generate:
class ShoppingCartItem:
name: str
unit_price: float
quantity: int
def total_cost(self) -> float:
return self.unit_price * self.quantity
## All code below is added by dataclass
def __init__(self, name: str, unit_price: float, quantity: int = 0) -> None:
self.name = name
self.unit_price = unit_price
self.quantity = quantity
def __repr__(self):
return f'ShoppingCartItem(name={self.name!r}, unit_price={self.unit_price!r}, quantity={self.quantity!r})'
def __eq__(self, other):
if other.__class__ is self.__class__:
return (self.name, self.unit_price, self.quantity) == (other.name, other.unit_price, other.quantity)
return NotImplemented
def __ne__(self, other):
if other.__class__ is self.__class__:
return (self.name, self.unit_price, self.quantity) != (other.name, other.unit_price, other.quantity)
return NotImplemented
def __lt__(self, other):
if other.__class__ is self.__class__:
return (self.name, self.unit_price, self.quantity) < (other.name, other.unit_price, other.quantity)
return NotImplemented
def __le__(self, other):
if other.__class__ is self.__class__:
return (self.name, self.unit_price, self.quantity) <= (other.name, other.unit_price, other.quantity)
return NotImplemented
def __gt__(self, other):
if other.__class__ is self.__class__:
return (self.name, self.unit_price, self.quantity) > (other.name, other.unit_price, other.quantity)
return NotImplemented
def __ge__(self, other):
if other.__class__ is self.__class__:
return (self.name, self.unit_price, self.quantity) >= (other.name, other.unit_price, other.quantity)
return NotImplemented
6.3.15. Use Cases¶
from dataclasses import dataclass
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'),
(7.0, 3.2, 4.7, 1.4, 'versicolor'),
(7.6, 3.0, 6.6, 2.1, 'virginica'),
(4.6, 3.1, 1.5, 0.2, 'setosa')]
@dataclass
class Iris:
sepal_length: float
sepal_width: float
petal_length: float
petal_width: float
species: str
flowers = list(Iris(*row) for row in DATA[1:])
print(flowers)
# [
# Iris(sepal_length=5.8, sepal_width=2.7, petal_length=5.1, petal_width=1.9, species='virginica'),
# Iris(sepal_length=5.1, sepal_width=3.5, petal_length=1.4, petal_width=0.2, species='setosa'),
# Iris(sepal_length=5.7, sepal_width=2.8, petal_length=4.1, petal_width=1.3, species='versicolor'),
# Iris(sepal_length=6.3, sepal_width=2.9, petal_length=5.6, petal_width=1.8, species='virginica'),
# Iris(sepal_length=6.4, sepal_width=3.2, petal_length=4.5, petal_width=1.5, species='versicolor'),
# Iris(sepal_length=4.7, sepal_width=3.2, petal_length=1.3, petal_width=0.2, species='setosa'),
# Iris(sepal_length=7.0, sepal_width=3.2, petal_length=4.7, petal_width=1.4, species='versicolor'),
# Iris(sepal_length=7.6, sepal_width=3.0, petal_length=6.6, petal_width=2.1, species='virginica'),
# Iris(sepal_length=4.6, sepal_width=3.1, petal_length=1.5, petal_width=0.2, species='setosa')
# ]
6.3.16. Assignments¶
"""
* Assignment: OOP Dataclass Syntax
* 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. When `x` or `y` has negative value raise en exception `ValueError('Coordinate cannot be negative')`
3. Use `datalass` and validation in `__post_init__()`
4. 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. Gdy `x` lub `y` mają wartość ujemną podnieś wyjątek `ValueError('Coordinate cannot be negative')`
3. Użyj `datalass` i walidacji w `__post_init__()`
4. Porównaj wyniki z sekcją "Tests" (patrz poniżej)
Tests:
>>> from inspect import isclass
>>> assert isclass(Point)
>>> 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)
>>> Point(x=-1, y=0)
Traceback (most recent call last):
ValueError: Coordinate cannot be negative
>>> Point(x=0, y=-1)
Traceback (most recent call last):
ValueError: Coordinate cannot be negative
"""
# Given
from dataclasses import dataclass
"""
* Assignment: OOP Dataclass Addressbook
* Complexity: easy
* Lines of code: 12 lines
* Time: 8 min
English:
1. Use data from "Given" section (see below)
2. Model data using `dataclasses`
3. Create classes to represent `DATA`, but do not convert it
4. Fields should have deafault value set to empty `str`
5. Compare result with "Tests" section (see below)
Polish:
1. Użyj danych z sekcji "Given" (patrz poniżej)
2. Zamodeluj dane wykorzystując `dataclass`
3. Stwórz klasy do reprezentacji `DATA`, ale nie konwertuj tego
4. Pola mają mieć wartość domyślną pusty `str`
5. Porównaj wyniki z sekcją "Tests" (patrz poniżej)
Tests:
>>> from inspect import isclass
>>> assert isclass(Astronaut)
>>> assert isclass(Address)
>>> assert hasattr(Astronaut, 'firstname')
>>> assert hasattr(Astronaut, 'lastname')
>>> assert hasattr(Address, 'street')
>>> assert hasattr(Address, 'city')
>>> assert hasattr(Address, 'post_code')
>>> assert hasattr(Address, 'region')
>>> assert hasattr(Address, 'country')
"""
# Given
from dataclasses import dataclass, field
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"},
{"firstname": "Alex", "lastname": "Vogel", "addresses": [
{"street": "Linder Hoehe", "city": "Köln", "post_code": 51147, "region": "North Rhine-Westphalia", "country": "Germany"}]}
]
"""
* Assignment: OOP Dataclass JSON
* Complexity: medium
* Lines of code: 23 lines
* Time: 21 min
English:
1. Use data from "Given" section (see below)
2. You received input data in JSON format from the API
3. Using `dataclass` Model data as class `User`
4. Parse fields with dates and store as `datetime` objects
5. Parse fields with `true` and `false` values and store as `bool` objects
6. Do not create additional classes to represent `permission` filed, leave it as `list[dict]`
6. Iterate over records and create instances of this class
7. Collect all instances to one list
8. Compare result with "Tests" section (see below)
Polish:
1. Użyj danych z sekcji "Given" (patrz poniżej)
2. Otrzymałeś z API dane wejściowe w formacie JSON
3. Wykorzystując `dataclass` zamodeluj dane za pomocą klasy `User`
4. Sparsuj pola zwierające daty i zapisz je jako obiekty `datetime`
5. Sparsuj pola zawierające `true` lub `false` i zapamiętaj ich wartości jako obiekty `bool`
6. Nie twórz dodatkowych klas do reprezentacji pola `permission`, niech zostanie jako `list[dict]`
7. Iterując po rekordach twórz instancje tej klasy
8. Zbierz wszystkie instancje do jednej listy
9. Porównaj wyniki z sekcją "Tests" (patrz poniżej)
Tests:
>>> result # doctest: +NORMALIZE_WHITESPACE
[User(firstname='Melissa', lastname='Lewis', role='commander', username='mlewis', password='pbkdf2_sha256$120000$gvEBNiCeTrYa0$5C+NiCeTrYsha1PHogqvXNiCeTrY0CRSLYYAA90=', email='melissa.lewis@nasa.gov', date_of_birth=datetime.date(1995, 7, 15), last_login=datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc), 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']}]),
User(firstname='Rick', lastname='Martinez', role='pilot', username='rmartinez', password='pbkdf2_sha256$120000$aXNiCeTrY$UfCJrBh/qhXohNiCeTrYH8nsdANiCeTrYnShs9M/c=', email='rick.martinez@ansa.gov', date_of_birth=datetime.date(1996, 1, 21), last_login=None, is_active=True, is_staff=True, is_superuser=False, user_permissions=[{'communication': ['add', 'view']}, {'eclss': ['add', 'modify', 'view']}, {'science': ['add', 'modify', 'view']}]),
User(firstname='Alex', lastname='Vogel', role='chemist', username='avogel', password='pbkdf2_sha256$120000$eUNiCeTrYHoh$X32NiCeTrYZOWFdBcVT1l3NiCeTrY4WJVhr+cKg=', email='alex.vogel@esa.int', date_of_birth=datetime.date(1994, 11, 15), last_login=None, 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']}]),
User(firstname='Chris', lastname='Beck', role='crew-medical-officer', username='cbeck', password='pbkdf2_sha256$120000$3G0RNiCeTrYlaV1$mVb62WNiCeTrYQ9aYzTsSh74NiCeTrY2+c9/M=', email='chris.beck@nasa.gov', date_of_birth=datetime.date(1999, 8, 2), last_login=datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc), is_active=True, is_staff=True, is_superuser=False, user_permissions=[{'communication': ['add', 'view']}, {'medical': ['add', 'modify', 'view']}, {'science': ['add', 'modify', 'view']}]),
User(firstname='Beth', lastname='Johansen', role='sysop', username='bjohansen', password='pbkdf2_sha256$120000$QmSNiCeTrYBv$Nt1jhVyacNiCeTrYSuKzJ//WdyjlNiCeTrYYZ3sB1r0g=', email='', date_of_birth=datetime.date(2006, 5, 9), last_login=None, is_active=True, is_staff=True, is_superuser=False, user_permissions=[{'communication': ['add', 'view']}, {'science': ['add', 'modify', 'view']}]),
User(firstname='Mark', lastname='Watney', role='botanist', username='mwatney', password='pbkdf2_sha256$120000$bxS4dNiCeTrY1n$Y8NiCeTrYRMa5bNJhTFjNiCeTrYp5swZni2RQbs=', email='', date_of_birth=datetime.date(1994, 10, 12), last_login=None, is_active=True, is_staff=True, is_superuser=False, user_permissions=[{'communication': ['add', 'modify', 'view']}, {'science': ['add', 'modify', 'view']}])]
"""
# Given
import json
from dataclasses import dataclass
from datetime import date, datetime, timezone
from typing import Optional, Union
DATA = '[{"model":"authorization.user","pk":1,"fields":{"firstname":"Melissa","lastname":"Lewis","role":"commander","username":"mlewis","password":"pbkdf2_sha256$120000$gvEBNiCeTrYa0$5C+NiCeTrYsha1PHogqvXNiCeTrY0CRSLYYAA90=","email":"melissa.lewis@nasa.gov","date_of_birth":"1995-07-15","last_login":"1970-01-01T00:00:00.000Z","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"]}]}},{"model":"authorization.user","pk":2,"fields":{"firstname":"Rick","lastname":"Martinez","role":"pilot","username":"rmartinez","password":"pbkdf2_sha256$120000$aXNiCeTrY$UfCJrBh/qhXohNiCeTrYH8nsdANiCeTrYnShs9M/c=","date_of_birth":"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$X32NiCeTrYZOWFdBcVT1l3NiCeTrY4WJVhr+cKg=","email":"alex.vogel@esa.int","date_of_birth":"1994-11-15","last_login":null,"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"]}]}},{"model":"authorization.user","pk":4,"fields":{"firstname":"Chris","lastname":"Beck","role":"crew-medical-officer","username":"cbeck","password":"pbkdf2_sha256$120000$3G0RNiCeTrYlaV1$mVb62WNiCeTrYQ9aYzTsSh74NiCeTrY2+c9/M=","email":"chris.beck@nasa.gov","date_of_birth":"1999-08-02","last_login":"1970-01-01T00:00:00.000Z","is_active":true,"is_staff":true,"is_superuser":false,"user_permissions":[{"communication":["add","view"]},{"medical":["add","modify","view"]},{"science":["add","modify","view"]}]}},{"model":"authorization.user","pk":5,"fields":{"firstname":"Beth","lastname":"Johansen","role":"sysop","username":"bjohansen","password":"pbkdf2_sha256$120000$QmSNiCeTrYBv$Nt1jhVyacNiCeTrYSuKzJ//WdyjlNiCeTrYYZ3sB1r0g=","email":"","date_of_birth":"2006-05-09","last_login":null,"is_active":true,"is_staff":true,"is_superuser":false,"user_permissions":[{"communication":["add","view"]},{"science":["add","modify","view"]}]}},{"model":"authorization.user","pk":6,"fields":{"firstname":"Mark","lastname":"Watney","role":"botanist","username":"mwatney","password":"pbkdf2_sha256$120000$bxS4dNiCeTrY1n$Y8NiCeTrYRMa5bNJhTFjNiCeTrYp5swZni2RQbs=","email":"","date_of_birth":"1994-10-12","last_login":null,"is_active":true,"is_staff":true,"is_superuser":false,"user_permissions":[{"communication":["add","modify","view"]},{"science":["add","modify","view"]}]}}]' # noqa
def _clean_time(text: str) -> Union[datetime,date,None]:
if not text:
return None
try:
return datetime.strptime(text, '%Y-%m-%d').date()
except ValueError:
pass
try:
return datetime.strptime(text, '%Y-%m-%dT%H:%M:%S.%fZ').replace(tzinfo=timezone.utc)
except TypeError:
pass