2. Tech Lead en Qnective AG
Encantador de serpientes desde v2.2
Trabajo en remoto desde 2016
Incapaz de soportarun invierno de
verdad desde2016
Apenas sé hablar español técnico
Gran amante de las listas
Como esta
Python
Venga!
Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
3. Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
p1 = Point(0, 0)
print(f'{p1}: x={p1.x}, y={p1.y}')
p2 = Point(x=0, y=0)
print(f'{p1} == {p2} -> {p1 == p2}’)
Point(x=0, y=0): x=0, y=0
Point(x=0, y=0) == Point(x=0, y=0) -> True
4. El dato estructurado
De tuplas a clases
Añadiendo funcionalidad a una clase
Dataclass
Casosmás simples
Comparabilidad
Hashabilidad
Campos
Valores por defecto
Herencia
__slots__
Funciones de apoyo
Proyectos de terceros
Alternativas
Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
6. Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
red = 255, 0, 0
orange = (255, 165, 0)
green = tuple(0, 255, 0)
>>> orange[0]
255
>>> r, g, b = orange
>>> g
165
7. Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
red = list((255, 0, 0))
orange = [255, 165, 0]
8. Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
red = dict(red=255, green=0, blue=0)
orange = {'red': 255,
'green': 165,
'blue': 0}
>>> orange[‘blue’]
0
9. ( )
Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
from collections import namedtuple
Colour = namedtuple(
'Colour', 'red green blue')
red = Colour(255, 0, 0)
orange = Colour(red=255,
green=165,
blue=0)
>>> orange.red
255
10. Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
from typing import NamedTuple
class Colour(NamedTuple):
red: int
green: int
blue: int = 0 # default!
red = Colour(255, 0)
orange = Colour(red=255, green=165)
>>> red.red
255
>>> orange.blue
0
11. Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
from types import SimpleNamespace
red = SimpleNamespace(red=255, green=0, blue=0)
>>> red.red
255
12. Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
class Colour:
def __init__(self, red, green, blue=0):
self.red = red
self.green = green
self.blue = blue
red = Colour(255, 0)
orange = Colour(red=255, green=165)
>>> red.red
255
>>> orange.blue
0
17. def __eq__(self, other):
if other.__class__ is not self.__class__:
return NotImplemented
return (self.red, self.green, self.blue) == (
other.red, other.green, other.blue)
>>> red1 = Colour(255, 0, 0)
>>> red2 = Colour(255, 0, 0)
>>> red1 == red2
True
>>> red1 is red2
False
Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
18. Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
class Colour:
def __init__(self, red, green, blue=0):
self.red = red
self.green = green
self.blue = blue
def __repr__(self):
return (f"{self.__class__.__name__}("
f"red={self.red}, green={self.green}, "
f"blue={self.blue})")
def __eq__(self, other):
if other.__class__ is not self.__class__:
return NotImplemented
return (self.red, self.green, self.blue) == (
other.red, other.green, other.blue)
19. >>> list(sorted([orange, red]))
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: '<' not supported between instances of
'Colour' and 'Colour'
Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
20. def __lt__(self, other):
if other.__class__ is self.__class__:
return (self.red, self.green, self.blue) < (
other.red, other.green, other.blue)
return NotImplemented
...
>>> list(sorted([orange, red]))
[Colour(red=255, green=0, blue=0), Colour(red=255,
green=165, blue=0)]
Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
21. class Colour:
def __init__(self, red, green, blue=0):
self.red = red
self.green = green
self.blue = blue
def __repr__(self):
return (f"{self.__class__.__name__}("
f"red={self.red}, green={self.green}, "
f"blue={self.blue})")
def __eq__(self, other):
if other.__class__ is self.__class__:
return (self.red, self.green, self.blue) == (
other.red, other.green, other.blue)
return NotImplemented
def __ge__(self, other):
if other.__class__ is self.__class__:
return (self.red, self.green, self.blue) >= (
other.red, other.green, other.blue)
return NotImplemented
def __gt__(self, other):
if other.__class__ is self.__class__:
return (self.red, self.green, self.blue) > (
other.red, other.green, other.blue)
return NotImplemented
def __le__(self, other):
if other.__class__ is self.__class__:
return (self.red, self.green, self.blue) <= (
other.red, other.green, other.blue)
return NotImplemented
def __lt__(self, other):
if other.__class__ is self.__class__:
return (self.red, self.green, self.blue) < (
other.red, other.green, other.blue)
return NotImplemented
Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
22. def __delattr__(self, name):
raise TypeError(f'cannot delete field {name}')
def __setattr__(self, name, value):
raise TypeError(f'cannot assign to field {name}’)
>>> red = Colour(255, 0, 0)
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "<input>", line 4, in __init__
File "<input>", line 48, in __setattr__
TypeError: cannot assign to field red
Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
23. def __init__(self, red, green, blue=0):
object.__setattr__(self, "red", red)
object.__setattr__(self, "green", green)
object.__setattr__(self, "blue", blue)
>>> red = Colour(255, 0, 0)
>>> red.blue = 1
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "<input>", line 23, in __setattr__
TypeError: cannot assign to field blue
Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
28. from dataclasses import dataclass
@dataclass
class Colour:
red: int
green: int
blue: int
orange = Colour(red=255, green=165, blue=0)
red = Colour(255, 0, 0)
>>> red
Colour(red=255, green=0, blue=0)
Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
29. Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
# Python 3.5
from typing import Optional
def fun(width: float,
name: Optional[str]=None) -> None:
pass
# Python 3.6
temperature: float
temperature = 'cold'
class Measurement:
speed: float
__annotations__
30. from dataclasses import dataclass
@dataclass
class Colour:
red: int
green: int
blue: int
orange = Colour(red=255, green=165, blue=0)
red = Colour(255, 0, 0)
red2 = Colour(255, 0, 0)
>>> red == red2
True
Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
31. from dataclasses import dataclass
@dataclass
class Colour:
red: int
green: int
blue: int
red = Colour(255, 0, 0)
>>> red.red = 240
>>> red
Colour(red=240, green=0, blue=0)
Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
32. from dataclasses import dataclass
@dataclass
class Colour:
red: int
green: int
blue: int
>>> list(sorted([orange, red]))
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: '<' not supported between instances of
'Colour' and 'Colour'
Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
33. @dataclass(order=True)
class Colour:
red: int
green: int
blue: int
>>> list(sorted([orange, red]))
[Colour(red=255, green=0, blue=0),
Colour(red=255, green=165, blue=0)]
Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
34. Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
def dataclass(_cls=None, *, order=False):
def wrap(cls):
return _process_class(cls, order)
# See if we're being called as @dataclass
# or @dataclass().
if _cls is None:
# We're called with parens.
return wrap
# We're called as @dataclass without parens.
return wrap(_cls)
35. @dataclass(order=True)
class Colour:
red: int
green: int
blue: int
>>> hash(red)
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: unhashable type: 'Colour'
Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
37. @dataclass(order=True, frozen=True)
class Colour:
red: int
green: int
blue: int
>>> red.red = 240
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "<string>", line 3, in __setattr__
dataclasses.FrozenInstanceError: cannot assign
to field 'red'
Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
38. Dataclass
@dataclass(order=True, frozen=True)
class Colour:
red: int
green: int
blue: int
Claseartesanal
• class Colour:
def __init__(self, red, green, blue=0):
object.__setattr__(self, "red", red)
object.__setattr__(self, "green", green)
object.__setattr__(self, "blue", blue)
def __repr__(self):
return (f"{self.__class__.__name__}("
f"red={self.red}, green={self.green}, "
f"blue={self.blue})")
def __eq__(self, other):
if other.__class__ is self.__class__:
return (self.red, self.green, self.blue) == (
other.red, other.green, other.blue)
return NotImplemented
def __ge__(self, other):
if other.__class__ is self.__class__:
return (self.red, self.green, self.blue) >= (
other.red, other.green, other.blue)
return NotImplemented
def __gt__(self, other):
if other.__class__ is self.__class__:
return (self.red, self.green, self.blue) > (
other.red, other.green, other.blue)
return NotImplemented
def __le__(self, other):
if other.__class__ is self.__class__:
return (self.red, self.green, self.blue) <= (
other.red, other.green, other.blue)
return NotImplemented
def __lt__(self, other):
if other.__class__ is self.__class__:
return (self.red, self.green, self.blue) < (
other.red, other.green, other.blue)
return NotImplemented
def __delattr__(self, name):
raise TypeError(f'cannot delete field {name}')
def __setattr__(self, name, value):
raise TypeError(f'cannot assign to field {name}')
def __hash__(self):
return hash((self.red, self.green, self.blue))
Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
40. Los campos de una dataclass
Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
41. @dataclass
class Colour:
red: int
green: int
blue: int = 0
orange = Colour(red=255, green=165)
red = Colour(255, 0)
>>> red
Colour(red=255, green=0, blue=0)
Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
42. from typing import List
@dataclass
class Employee:
name: str
surname: str
children: List[str] = []
john = Employee('John', 'Green')
hank = Employee('Hank', 'Green’)
john.children.append('Henry')
Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
43. Traceback (most recent call last):
File "<input>", line 3, in <module>
File "dataclasses.py", line 966, in dataclass
return wrap(_cls)
File "dataclasses.py", line 958, in wrap
return _process_class(cls, init, repr, eq, order,
unsafe_hash, frozen)
File "dataclasses.py", line 809, in _process_class
for name, type in cls_annotations.items()]
File "dataclasses.py", line 809, in <listcomp>
for name, type in cls_annotations.items()]
File "dataclasses.py", line 702, in _get_field
raise ValueError(f'mutable default
{type(f.default)} for field '
ValueError: mutable default <class 'list'> for field
children is not allowed: use default_factory
Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
44. from dataclasses import dataclass, field
from typing import List
@dataclass
class Employee:
name: str
surname: str
children: List[str] = field(default_factory=list)
john = Employee('John', 'Green')
hank = Employee('Hank', 'Green')
john.children.append('Henry')
john.children.append('Alice’)
>>> hank.children
[]
Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
45. Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
__annotations__
dataclasses.Field
__dataclass_fields__
47. Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
from typing import ClassVar
@dataclass
class Colour:
MAX: ClassVar[int] = 255
red: int
green: int
blue: int = 0
red = Colour(255, 0)
orange = Colour(255, 165)
>>> red.MAX
255
49. Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
@dataclass
class Point1d:
x: float
@dataclass
class Point2d(Point1d):
y: float
@dataclass
class Point3d(Point2d):
z: float
p1 = Point1d(11.1)
p2 = Point2d(22.2, 33.3)
p3 = Point3d(44.4, 55.5, 66.6)
>>> p1, p2, p3
(Point1d(x=11.1), Point2d(x=22.2, y=33.3), Point3d(x=44.4, y=55.5,
z=66.6))
50. Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
@dataclass
class Colour:
__slots__ = (
'red’,
'green’,
'blue')
red: int
green: int
blue: int
>>> red.check = True
Traceback (most recent call last):
File "<input>", line 1, in <module>
AttributeError: 'Colour' object has no attribute
'check'
Rapidez de acceso
Restricción de creación de campos
Ahorro de espacio
¡Sin valores por defecto!
52. fields Lista los campos guardados. Funciona con instancias y
clases.
asdict Convierte un objeto en diccionario de forma recursiva
astuple Convierte un objeto en tupla de forma recursiva
replace Crea una copia (no profunda) de un objeto con algunos
campos cambiados de valor.
is_dataclass Comprueba si un objeto o clase es una dataclass
make_dataclass Crea una nueva clase con dataclass
Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
53. fields Lista los campos guardados. Funciona con instancias y
clases.
Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
>>> dataclasses.fields(red)
(Field(name='red’,...
Field(name='green',type=<class
'int'>,default=<dataclasses._MISSING_TYPE object at
0x0000028B13380208>,default_factory=<dataclasses._MISSI
NG_TYPE object at
0x0000028B13380208>,init=True,repr=True,hash=None,compa
re=True,metadata=mappingproxy({}),_field_type=_FIELD),
Field(name='blue’,...))
54. asdict Convierte un objeto en diccionario de forma recursiva
Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
>>> dataclasses.asdict(red)
{'red': 255, 'green': 0, 'blue': 0}
55. astuple Convierte un objeto en tupla de forma recursiva
Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
>>> dataclasses.astuple(red)
(255, 0, 0)
56. replace Crea una copia (no profunda) de un objeto con algunos
campos cambiados de valor.
Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
>>> red2 = dataclasses.replace(red, blue=12)
>>> red2
Colour(red=255, green=0, blue=12)
57. is_dataclass Comprueba si un objeto o clase es una dataclass
Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
>>> dataclasses.is_dataclass(red)
True
>>> dataclasses.is_dataclass(Colour)
True
>>> dataclasses.is_dataclass([])
False
58. make_dataclass Crea una nueva clase con dataclass
Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
from dataclasses import make_dataclass
Colour = make_dataclass('Colour',
fields=[('red', int), ('green', int),
('blue', int)])
red = Colour(255, 0, 0)
orange = Colour(255, 165, 0)
60. dataslots Decorador with_slots, añade slots a una
dataclass.
dataclass-factory Crea instancias de dataclasses a partir de
diccionarios. Inverso a asdict.
strictclasses Decorador que hace que se comprueben tipos
en asignación.
dataclassinspector Intenta mostrar el equivalente al código
generado por una dataclass. Intenta.
fistro Genera instancias de dataclasses con datos
para pruebas. Fixtures.
dataclasses-jsonschema Crea JSON schema a partir de una dataclass.
Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
62. Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
import attr
@attr.s
class Colour:
red: int = attr.ib()
green: int = attr.ib()
blue: int = attr.ib()
red = Colour(255, 0, 0)
>>> red
Colour(red=255, green=0, blue=0)
63. Funciona desde Python 2.7
Soporta __slots__
Validadores
Convertidores
Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
65. El dato estructurado
De tuplas a clases
Añadiendo funcionalidad a una clase
Dataclass
Casosmás simples
Comparabilidad
Hashabilidad
Campos
Valores por defecto
Herencia
__slots__
Funciones de apoyo
Proyectos de terceros
Alternativas
Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
66. Dataclass
@dataclass(order=True, frozen=True)
class Colour:
red: int
green: int
blue: int
Claseartesanal
• class Colour:
def __init__(self, red, green, blue=0):
object.__setattr__(self, "red", red)
object.__setattr__(self, "green", green)
object.__setattr__(self, "blue", blue)
def __repr__(self):
return (f"{self.__class__.__name__}("
f"red={self.red}, green={self.green}, "
f"blue={self.blue})")
def __eq__(self, other):
if other.__class__ is self.__class__:
return (self.red, self.green, self.blue) == (
other.red, other.green, other.blue)
return NotImplemented
def __ge__(self, other):
if other.__class__ is self.__class__:
return (self.red, self.green, self.blue) >= (
other.red, other.green, other.blue)
return NotImplemented
def __gt__(self, other):
if other.__class__ is self.__class__:
return (self.red, self.green, self.blue) > (
other.red, other.green, other.blue)
return NotImplemented
def __le__(self, other):
if other.__class__ is self.__class__:
return (self.red, self.green, self.blue) <= (
other.red, other.green, other.blue)
return NotImplemented
def __lt__(self, other):
if other.__class__ is self.__class__:
return (self.red, self.green, self.blue) < (
other.red, other.green, other.blue)
return NotImplemented
def __delattr__(self, name):
raise TypeError(f'cannot delete field {name}')
def __setattr__(self, name, value):
raise TypeError(f'cannot assign to field {name}')
def __hash__(self):
return hash((self.red, self.green, self.blue))
Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
67. Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
Añade.
Otro.
Campo.
69. Jacobo de Vera | @jovianjake | PyDay Tenerife 2018
Todd Ehlers en Flickr
Philippe_ en Flickr
Leo Hidalgo en Flickr
Antonio Trogu en Flickr
Bruce Baugh en Flickr
VivaAntarctica en Flickr
eightydaysjet en Flickr
Farrukh en Flickr
Andy Wilson en Flickr
Valerie Reneé en Flickr
Paul Keller en Flickr
Marc Wesel en Flickr
Matteo Tarenghi en Flickr
JD Hancock en Flickr
Smabs Sputzer (1956-2017) en Flickr
Loco Races en Flickr
Cameron Daigle en Flickr