Mise à jour de 'source/part-3-django-concepts/models.adoc'
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Fred 2021-08-18 15:08:49 +02:00
parent bd3cd12959
commit bc725d59dd
1 changed files with 82 additions and 2 deletions

View File

@ -146,8 +146,85 @@ class Runner(models.Model):
----
==== Shell
==== Protocoles de langage (*dunder methods*)
The Python data model specifies a lot of specially named methods that can be overridden in your custom classes to provide
them with additional syntax capabilities.
You can recognize these methods by their specific naming conventions that wrap the method name with double underscores.
Because of this, they are sometimes referred to as dunder methods.
It is simply shorthand for double underscores.The most common and obvious example of such dunder methods is __init__(),
which is used for class instance initialization:
[source,python]
----
class CustomUserClass:
def __init__(self, initiatization_argument):
...
----
En fait, l'intérêt concerne surtout la représentation de nos modèles, puisque chaque classe du modèle est représentée par
la définition d'un objet Python.
Nous pouvons donc utiliser ces mêmes *dunder methods* (*double-underscores methods*) pour étoffer les protocoles du langage.
These methods, either alone or when defined in a specific combination, constitute the so-called language protocols.
If we say that an object implements a specific language protocol, it means that it is compatible with a specific part of the Python language syntax. The following is a table of the most common protocols within the Python language.Protocol nameMethodsDescriptionCallable protocol__call__()Allows objects to be called with parentheses:instance()Descriptor protocols__set__(), __get__(), and __del__()Allows us to manipulate the attribute access pattern of classes (see the Descriptors section)Container protocol__contains__()Allows us to test whether or not an object contains some value using the in keyword:value in instance
Python in Comparison with Other LanguagesIterable protocol__iter__()Allows objects to be iterated using the forkeyword:for value in instance: ...Sequence protocol__getitem__(),__len__()Allows objects to be indexed with square bracket syntax and queried for length using a built-in function:item = instance[index]length = len(instance)Each operator available in Python has its own protocol and operator overloading happens by implementing the dunder methods of that protocol. Python provides over 50 overloadable operators that can be divided into five main groups:• Arithmetic operators • In-place assignment operators• Comparison operators• Identity operators• Bitwise operatorsThat's a lot of protocols so we won't discuss all of them here. We will instead take a look at a practical example that will allow you to better understand how to implement operator overloading on your own
A full list of available dunder methods can be found in the Data model section of the official Python documentation available
at https://docs.python.org/3/reference/datamodel.html.
All operators are also exposed as ordinary functions in the operators module.
The documentation of that module gives a good overview of Python operators.
It can be found at https://docs.python.org/3.9/library/operator.html
The `__add__()` method is responsible for overloading the `+` (plus sign) operator and here it allows us to add
two matrices together.
Only matrices of the same dimensions can be added together.
This is a fairly simple operation that involves adding all matrix elements one by one to form a new matrix.
The `__sub__()` method is responsible for overloading the `` (minus sign) operator that will be responsible for matrix subtraction.
To subtract two matrices, we use a similar technique as in the operator:
[source,python]
----
def __sub__(self, other):
if (len(self.rows) != len(other.rows) or len(self.rows[0]) != len(other.rows[0])):
raise ValueError("Matrix dimensions don't match")
return Matrix([[a - b for a, b in zip(a_row, b_row)] for a_row, b_row in zip(self.rows, other.rows) ])
----
And the following is the last method we add to our class:
[source,python]
----
def __mul__(self, other):
if not isinstance(other, Matrix):
raise TypeError(f"Don't know how to multiply {type(other)} with Matrix")
if len(self.rows[0]) != len(other.rows):
raise ValueError("Matrix dimensions don't match")
rows = [[0 for _ in other.rows[0]] for _ in self.rows]
for i in range(len (self.rows)):
for j in range(len (other.rows[0])):
for k in range(len (other.rows)):
rows[i][j] += self.rows[i][k] * other.rows[k][j]
return Matrix(rows)
----
The last overloaded operator is the most complex one.
This is the `*` operator, which is implemented through the `__mul__()` method.
In linear algebra, matrices don't have the same multiplication operation as real numbers.
Two matrices can be multiplied if the first matrix has a number of columns equal to the number of rows of the second matrix.
The result of that operation is a new matrix where each element is a dot product of the corresponding row of the first matrix
and the corresponding column of the second matrix.
Here we've built our own implementation of the matrix to present the idea of operators overloading.
Although Python lacks a built-in type for matrices, you don't need to build them from scratch.
The NumPy package is one of the best Python mathematical packages and among others provides native support for matrix algebra.
You can easily obtain the NumPy package from PyPI
(Voir Expert Python Programming, 4th Edition, page 142-144)
==== Constructeurs
@ -177,7 +254,10 @@ class Item(models.Model):
return i
----
Mieux encore: on pourrait passer par un `ModelManager` pour limiter le couplage; l'accès à une information stockée en base de données ne se ferait dès lors qu'au travers de cette instance et pas directement au travers du modèle. De cette manière, on limite le couplage des classes et on centralise l'accès.
Mieux encore: on pourrait passer par un `ModelManager` pour limiter le couplage;
l'accès à une information stockée en base de données ne se ferait dès lors qu'au travers
de cette instance et pas directement au travers du modèle. De cette manière, on limite le
couplage des classes et on centralise l'accès.
[source,python]
----