Skip to content

Latest commit

 

History

History
875 lines (576 loc) · 26 KB

index.rst

File metadata and controls

875 lines (576 loc) · 26 KB
.. toctree::
   :glob:
   :hidden:

   *

¿Qué hay de nuevo en Python 3.11?

.. revealjs_section::
    :data-transition: zoom


images/python-logo.*

👋

Marzo 2020

.. revealjs_section::
    :data-transition: convex


Estamos de vuelta

>>> import datetime
>>> import humanize
>>> delta = datetime.datetime(2022, 10, 27) - datetime.datetime(2020, 3, 4)
>>> humanize.i18n.activate("es_ES")
>>> print(repr(humanize.precisedelta(delta)))
"2 años, 7 meses y 23 días"

Python Málaga

images/python-malaga-logo.*

Sobre mí Nekmo

Programando en Python desde 2006

.. revealjs_section::
    :data-transition: concave


Python 2.5

¿Qué hay de nuevo en Python 3.11?

Listado de cambios

  • PEP 654: Exception Groups y except*.
  • PEP 678: Enriquecer excepciones con notas.
  • PEP 680: tomllib.
  • PEP 657: Mejoras en las indicaciones de error en los tracebacks.
  • Opción -P en la línea de comandos y variable de entorno PYTHONSAFEPATH.
  • PEP 646: Variadic Generics.
  • PEP 655: TypedDict Required/NotRequired.
  • PEP 673: Tipo Self.
  • PEP 675: Tipo de cadena literal arbitraria
  • PEP 681: Data Class Transforms
  • Módulos obsoletos (PEP 594), Py_UNICODE API eliminada (PEP 624) y macros convertidas a funciones estáticas en línea (PEP 670).
.. revealjs_section::
    :data-transition: zoom


Gracias

PEP 654: Exception Groups y except*

.. revealjs_break::
    :notitle:


.. revealjs-code-block:: python
   :data-line-numbers: 1-2,7,12|11-14|5-7|13-14

    class NameError(Exception):
        pass


    def validate_name(value: str) -> None:
        if not value.istitle():
            raise NameError("El nombre debe empezar por mayúscula.")


    form = {"name": "nekmo"}
    try:
        validate_name(form["name"])
    except NameError as err:
        print(err)  # Salta el error


.. revealjs_break::
    :notitle:
    :data-transition: zoom


images/validacion-formulario.png

.. revealjs_break::
    :notitle:


.. revealjs-code-block:: python
   :data-line-numbers: 13,17|8-10,14,19|21|22-23

    from typing import Iterable, Tuple, Dict, Callable


    class NumberError(Exception):
        pass


    def validate_age(value: str) -> None:
        if not value.isdigit():
            raise NumberError("La edad debe ser un valor numérico.")


    form = {"name": "nekmo", "age": "diez"}
    form_validations = [("name", validate_name), ("age", validate_age)]
    exceptions = []

    for form_key, input_validation in input_validations:
        try:
            input_validation(form[form_key])
        except Exception as err:
            exceptions.append(value)
    if exceptions:
        raise ExceptionGroup("errors message", exceptions)


.. revealjs_break::
    :notitle:

images/json.png

except*

.. revealjs_break::
    :notitle:

.. revealjs-code-block:: python
   :data-line-numbers: 1-8|3-5|6-8

    try:
        read_inputs()
    except* NameError as eg:
        # Si hay errores NameError esto se llama
        print(f"Errores en el nombre: {eg.exceptions}")
    except* NumberError as eg:
        # Y si hay errores NumberError, esto también
        print(f"Errores numéricos: {eg.exceptions}")

.. revealjs_break::
    :notitle:

raise ExceptionGroup("nested",
    [
         ValueError(654),
         ExceptionGroup("imports",
             [
                 ImportError("no_such_module"),
                 ModuleNotFoundError("another_module"),
             ]
         ),
         TypeError("int"),
     ]

¡Continuamos!

.. revealjs_section::
    :data-background-color: #000000
    :data-background-image: _static/applause.gif

PEP 678: Enriquecer excepciones con notas

.. revealjs_break::
    :notitle:

.. revealjs-code-block:: python
   :data-line-numbers: 2|3|4|5

    try:
         raise TypeError('bad type')
    except Exception as e:
         e.add_note('¡Ah, ah, ah! ¡No has dicho la palabra mágica!')
         raise

.. revealjs_break::
    :notitle:

.. revealjs-code-block:: python
   :data-line-numbers: 1-4|4

    Traceback (most recent call last):
      File "<stdin>", line 2, in <module>
    TypeError: bad type
    ¡Ah, ah, ah! ¡No has dicho la palabra mágica!

.. revealjs_break::
    :notitle:

+ Exception Group Traceback (most recent call last):
|   File "test.py", line 4, in test
|     def test(x):
|
|   File "hypothesis/core.py", line 1202, in wrapped_test
|     raise the_error_hypothesis_found
|     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| ExceptionGroup: Hypothesis found 2 distinct failures.
+-+---------------- 1 ----------------
    | Traceback (most recent call last):
    |   File "test.py", line 6, in test
    |     assert x > 0
    |     ^^^^^^^^^^^^
    | AssertionError: assert -1 > 0
    |
    | Falsifying example: test(
    |     x=-1,
    | )
    +---------------- 2 ----------------
    | Traceback (most recent call last):
    |   File "test.py", line 5, in test
    |     assert x < 0
    |     ^^^^^^^^^^^^
    | AssertionError: assert 0 < 0
    |
    | Falsifying example: test(
    |     x=0,
    | )
    +------------------------------------

PEP 657: Mejoras en las indicaciones de error en los tracebacks

.. revealjs_break::
    :notitle:
    :data-auto-animate:


.. revealjs-code-block:: python
   :data-line-numbers: 5-6

    Traceback (most recent call last):
      File "distance.py", line 11, in <module>
        print(manhattan_distance(p1, p2))
      File "distance.py", line 6, in manhattan_distance
        return abs(point_1.x - point_2.x) + abs(point_1.y - point_2.y)
    AttributeError: 'NoneType' object has no attribute 'x'

.. revealjs_break::
    :notitle:
    :data-auto-animate:


.. revealjs-code-block:: python
   :data-line-numbers: 4,6-8

    Traceback (most recent call last):
      File "distance.py", line 11, in <module>
        print(manhattan_distance(p1, p2))
              ^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "distance.py", line 6, in manhattan_distance
        return abs(point_1.x - point_2.x) + abs(point_1.y - point_2.y)
                               ^^^^^^^^^
    AttributeError: 'NoneType' object has no attribute 'x'

.. revealjs_break::
    :notitle:

.. revealjs-code-block:: python
   :data-line-numbers: 11-13

    Traceback (most recent call last):
      File "query.py", line 37, in <module>
        magic_arithmetic('foo')
      File "query.py", line 18, in magic_arithmetic
        return add_counts(x) / 25
               ^^^^^^^^^^^^^
      File "query.py", line 24, in add_counts
        return 25 + query_user(user1) + query_user(user2)
                    ^^^^^^^^^^^^^^^^^
      File "query.py", line 32, in query_user
        return 1 + query_count(db, response['a']['b']['c']['user'], retry=True)
                                   ~~~~~~~~~~~~~~~~~~^^^^^
    TypeError: 'NoneType' object is not subscriptable

PEP 680: Tomllib

Tom's Obvious, Minimal Language

.. revealjs_break::
    :notitle:

images/tom.png

.. revealjs_break::
    :notitle:

# This is a TOML document

title = "TOML Example"

[owner]
name = "Tom Preston-Werner"
dob = 1979-05-27T07:32:00-08:00

[database]
enabled = true
ports = [ 8000, 8001, 8002 ]
data = [ ["delta", "phi"], [3.14] ]
temp_targets = { cpu = 79.5, case = 72.0 }

[servers]

[servers.alpha]
ip = "10.0.0.1"
role = "frontend"
.. revealjs_break::
    :notitle:

import tomllib


with open("fichero.toml") as f:
    tomllib.load(f)

Novedades en el tipado

.. revealjs_break::
    :notitle:

images/badum.png

PEP 646: Variadic Generics

.. revealjs_break::
    :notitle:

.. revealjs-code-block:: python
   :data-line-numbers: 9-14|5,18|10-11,18-19|13-14,18,21|1-21

    from typing import Tuple, Generic, TypeVarTuple, TypeVar


    T = TypeVar('T')
    Ts = TypeVarTuple('Ts')  # Esta es la novedad


    # Aquí usamos el TypeVarTuple como definición para el tipo
    class Array(Generic[*Ts]):
        def multiply(self, x: int) -> Tuple[*Ts]:  # Y aquí como return
            ...

        def add_dimension(self, t: T) -> Tuple[T, *Ts]:
            ...


    # Ts en este caso será [float, int, str]
    my_array: Array[float, int, str] = Array()
    my_array.multiply(2)  # El tipo devuelto será Tuple[float, int, str]
    # El tipo devuelto será Tuple[str, float, int, str]
    my_array.add_dimension("spam")

.. revealjs_break::
    :notitle:
    :data-background-color: #000000
    :data-background-image: _static/confused.gif


PEP 655: TypedDict Required/NotRequired

.. revealjs_break::
    :notitle:
    :data-auto-animate:

.. revealjs-code-block:: python
   :data-line-numbers: 1-9|4

    from typing import TypedDict


    class Person(TypedDict, total=False):
        name: str  # Queremos que sea obligatorio pero no lo es
        surname: str  # Queremos que sea obligatorio pero no lo es
        age: int

    person: Person = {"name": "Juan", "surname": "Luna"}


.. revealjs_break::
    :notitle:
    :data-auto-animate:


.. revealjs-code-block:: python
   :data-line-numbers: 4-10|4,9

    from typing import TypedDict


    class PersonRequired(TypedDict, total=True):
        name: str
        surname: str


    class Person(PersonRequired, total=False):
        age: int

    person: Person = {"name": "Juan", "surname": "Luna"}

.. revealjs_break::
    :notitle:
    :data-auto-animate:


.. revealjs-code-block:: python
   :data-line-numbers: 1-10|7

    from typing import NotRequired, Required, TypedDict


    class Person(TypedDict):  # total=True por defecto
        name: str
        surname: str
        age: NotRequired[int]  # age no será requerido por el NotRequired[]


    person: Person = {"name": "Juan", "surname": "Luna"}

.. revealjs_break::
    :notitle:
    :data-transition: zoom

.. revealjs-code-block:: python
   :data-line-numbers: 1-10|4|5-6

    from typing import NotRequired, Required, TypedDict


    class Person(TypedDict, total=False):
        name: Required[str]
        surname: Required[str]
        age: int


    person: Person = {"name": "Juan", "surname": "Luna"}

.. revealjs_break::
    :notitle:
    :data-background-color: #000000
    :data-background-image: _static/thumb.gif


PEP 673: Tipo Self

.. revealjs_break::
    :notitle:


.. revealjs-code-block:: python
   :data-line-numbers: 10-11

    from typing import Self


    class Customer:

        def __init__(self, name: str, age: int):
            self.name = name
            self.age = age

        def __copy__(self) -> Self:
            return self.__class__(self.name, self.age)

PEP 675: LiteralString

.. revealjs_break::
    :notitle:


.. revealjs-code-block:: python
   :data-line-numbers: 1-11|8|11

    from typing import LiteralString, Iterable, Any


    def execute(sql: LiteralString, *params: Iterable[Any]):
        ...

   # Esta línea validará, porque pasamos los parámetros de forma segura
   execute("SELECT * FROM data WHERE user_id = ?", [123])

   # Esta línea dará error, porque se modifica el string de entrada previamente
   execute(f"SELECT * FROM data WHERE user_id = {user_id}")  # MEEH! Error.

Más seguridad

.. revealjs_section::
    :data-background-color: #000000
    :data-background-image: _static/security.gif


PEP 681: Data Class Transforms

.. revealjs_break::
    :notitle:

# La clase ``ModelBase`` está definida en la biblioteca.
@typing.dataclass_transform()
class ModelBase: ...


# La clase ``ModelBase`` puede ser usado para crear nuevos modelos,
# similar a como se hace en estos frameworks.
class CustomerModel(ModelBase):
    id: int
.. revealjs_break::
    :notitle:

def dataclass_transform(
    *,
    eq_default: bool = True,
    order_default: bool = False,
    kw_only_default: bool = False,
    field_specifiers: tuple[type | Callable[..., Any], ...] = (),
    **kwargs: Any,
) -> Callable[[_T], _T]: ...

¿Qué otras novedades hay?

.. revealjs-fragments::

    * Nuevo argumento ``-P`` en la línea de comandos y variable de entorno ``PYTHONSAFEPATH`` para
      **evitar ejecutar código inseguro**.
    * **PEP 594**: Eliminar módulos muertos de la librería estándar (deprecated, a eliminar en 3.13).
    * **PEP 624**: Eliminadas las APIs de codificación de Py_UNICODE.
    * **PEP 670**: Convertir macros a funciones en la API en C de Python.
    * **¡Y es más rápido!** (10-60% respecto Python 3.10).


Más información

https://docs.python.org/3.11/whatsnew/3.11.html

¿Cómo puedo conseguirlo?

.. revealjs_section::
    :data-transition: convex


.. revealjs-fragments::

    **¡¡Ya disponible!!**

¿Desde los fuentes?

https://www.build-python-from-source.com/

Python te necesita

.. revealjs_section::
    :data-background-color: #ffffff
    :data-background-size: contain
    :data-background-image: _static/sam.png
    :data-transition: zoom

¡Muchas gracias!

.. revealjs-fragments::

    **Referencias**

    * `Qué hay de nuevo en Python 3.11 (doc oficial) <https://docs.python.org/3.11/whatsnew/3.11.html>`_.
    * `Preview exception groups (Real Python) <https://realpython.com/python311-exception-groups/>`_.
    * `Python variadic generics (Anthony Explains) <https://www.youtube.com/watch?v=hAj3nGzeSiQ>`_.

¿Y la presentación?

.. revealjs_section::
    :data-transition: zoom

github:Nekmo/python311-presentacion

.. revealjs_break::
    :data-background-color: #ffffff
    :data-background-size: contain
    :data-background-image: _static/qr.png
    :notitle:


Contactar