Mise à jour de «Introduction à l'accueil de l'application Web Python pour les ingénieurs Web de 3e année qui sont lents»

Mise à jour du livre

Chapitre "Créer une classe Request / Response / View pour améliorer la visibilité" Le a été mis à jour.

Si vous voulez en savoir plus, veuillez "aimer" ou "me suivre" dans Book ;-)


Ce qui suit est un extrait du contenu du livre.


Refactoriser

Il y a maintenant trois points de terminaison qui génèrent des réponses dynamiques, et workerthread.py est maintenant proche de 200 lignes.

Même à ce stade, je fais beaucoup de choses différentes avec un seul fichier, donc même 200 lignes sont devenues un module désordonné avec beaucoup de mauvaise visibilité.

De plus, au fur et à mesure que vous faites évoluer cette application Web, vous aurez de plus en plus de points de terminaison. Il est évident que la maintenance ne sera pas possible si vous l'ajoutez à chaque fois à workerthread.py. On peut dire qu'il est devenu nécessaire d'améliorer la visibilité de workerthread.py en séparant les responsabilités et en divisant les fichiers.

En d'autres termes, il est temps ** que la saison du refactoring soit arrivée **.

Dans ce chapitre, nous allons supprimer «le traitement qui génère dynamiquement un corps de réponse pour chaque point final» vers un module externe.

ÉTAPE 1: Découpez simplement en fonction

Tout d'abord, découpons simplement le processus de génération HTML pour chaque point de terminaison dans un autre module.

Le nom du module à découper est «vues». En effet, il s'agit d'un module dont la responsabilité est uniquement de générer la partie vue (= corps de la requête), quelle que soit la situation HTTP, telle que la connexion ou l'analyse de l'en-tête.

Code source

study/workerthread.py https://github.com/bigen1925/introduction-to-web-application-with-python/blob/main/codes/chapter16/workerthread.py#L50-L59

study/views.py https://github.com/bigen1925/introduction-to-web-application-with-python/blob/main/codes/chapter16/views.py

Commentaire

study/workerthread.py

Lignes 57-66

            if path == "/now":
                response_body, content_type, response_line = views.now()

            elif path == "/show_request":
                response_body, content_type, response_line = views.show_request(
                    method, path, http_version, request_header, request_body
                )

            elif path == "/parameters":
                response_body, content_type, response_line = views.parameters(method, request_body)

Jusqu'à la dernière fois, j'ai écrit que le processus de génération de HTML était collant pour chaque chemin, mais j'ai d'abord décidé de couper cette partie à la fonction du module views.

par ça,

--workerthread.py reçoit la requête HTTP, l'analyse, obtient le contenu de la réponse de la fonction du module views en fonction du chemin, construit la réponse HTTP et la renvoie au client. --views.py a une fonction pour chaque chemin, reçoit le contenu de la demande et renvoie le contenu de la réponse générée dynamiquement.

La tâche de "générer dynamiquement le contenu de la réponse en fonction du chemin" a été découpée en "vues".

study/views.py

import textwrap
import urllib.parse
from datetime import datetime
from pprint import pformat
from typing import Tuple, Optional


def now() -> Tuple[bytes, Optional[str], str]:
    """
Générer du HTML pour afficher l'heure actuelle
    """
    html = f"""\
        <html>
        <body>
            <h1>Now: {datetime.now()}</h1>
        </body>
        </html>
    """
    response_body = textwrap.dedent(html).encode()

    # Content-Spécifiez le type
    content_type = "text/html; charset=UTF-8"

    #Générer une ligne de réponse
    response_line = "HTTP/1.1 200 OK\r\n"

    return response_body, content_type, response_line


def show_request(
    method: str,
    path: str,
    http_version: str,
    request_header: dict,
    request_body: bytes,
) -> Tuple[bytes, Optional[str], str]:
    """
Générer du HTML pour afficher le contenu de la requête HTTP
    """
    html = f"""\
        <html>
        <body>
            <h1>Request Line:</h1>
            <p>
                {method} {path} {http_version}
            </p>
            <h1>Headers:</h1>
            <pre>{pformat(request_header)}</pre>
            <h1>Body:</h1>
            <pre>{request_body.decode("utf-8", "ignore")}</pre>

        </body>
        </html>
    """
    response_body = textwrap.dedent(html).encode()

    # Content-Spécifiez le type
    content_type = "text/html; charset=UTF-8"

    #Générer une ligne de réponse
    response_line = "HTTP/1.1 200 OK\r\n"

    return response_body, content_type, response_line


def parameters(
    method: str,
    request_body: bytes,
) -> Tuple[bytes, Optional[str], str]:
    """
Afficher le HTML affichant les paramètres POST
    """

    #Renvoie 405 pour les requêtes GET
    if method == "GET":
        response_body = b"<html><body><h1>405 Method Not Allowed</h1></body></html>"
        content_type = "text/html; charset=UTF-8"
        response_line = "HTTP/1.1 405 Method Not Allowed\r\n"

    elif method == "POST":
        post_params = urllib.parse.parse_qs(request_body.decode())
        html = f"""\
            <html>
            <body>
                <h1>Parameters:</h1>
                <pre>{pformat(post_params)}</pre>                        
            </body>
            </html>
        """
        response_body = textwrap.dedent(html).encode()

        # Content-Spécifiez le type
        content_type = "text/html; charset=UTF-8"

        #Générer une ligne de réponse
        response_line = "HTTP/1.1 200 OK\r\n"

    return response_body, content_type, response_line

Ce n'est pas non plus particulièrement difficile. Je viens de lancer le processus de génération dynamique de la réponse qui a été initialement écrite dans workerthread.py.


Il est bon de couper la fonction vues, mais comme c'est le cas maintenant, le nombre d'arguments est différent pour chaque fonction, "Le pseudonyme qui traite ce chemin a besoin des arguments de ceci et cela, et la fonction qui traite ce chemin a besoin des arguments de ceci et cela et cela ..." Et ainsi de suite, l'appelant doit connaître les détails de l'appelant.

Dans le monde de la programmation, on sait que le code source devient simple lorsqu'un module est fait de sorte que les détails de l'autre module ne soient pas connus autant que possible.

Dans la prochaine ÉTAPE, procédons un peu plus à la refactorisation et réalisons cela.

ÉTAPE 2: Unifier l'interface de la fonction vues

Maintenant, la classe WorkerThread a besoin de connaître les détails de la fonction views car elle ne peut pas être appelée sans savoir de quoi et combien d'arguments elle a besoin pour chaque fonction.

Un moyen simple de se débarrasser de cette situation est de ** "Je ne sais pas quel paramètre chaque fonction utilise, mais je vais tout passer quand même" **.

Code source

Jetons un coup d'œil au code source.

study/workerthread.py https://github.com/bigen1925/introduction-to-web-application-with-python/blob/main/codes/chapter16-2/workerthread.py

study/views.py https://github.com/bigen1925/introduction-to-web-application-with-python/blob/main/codes/chapter16-2/views.py

Commentaire

study/views.py Commençons par views.py

Lignes 8-14, 66-72

def now(
    method: str,
    path: str,
    http_version: str,
    request_header: dict,
    request_body: bytes,
) -> Tuple[bytes, Optional[str], str]:
def parameters(
    method: str,
    path: str,
    http_version: str,
    request_header: dict,
    request_body: bytes,
) -> Tuple[bytes, Optional[str], str]:

Les arguments sont unifiés dans toutes les fonctions d'affichage afin que toutes les informations de demande puissent être reçues. Nous n'utilisons pas ces arguments dans la fonction, mais en les rendant disponibles, l'appelant n'a pas à penser à "ce qui est nécessaire et ce qui n'est pas nécessaire".

study/workerthread.py Vient ensuite le côté qui appelle la fonction de vue.

Lignes 29-34

    #Correspondance entre les fonctions de chemin et de vue
    URL_VIEW = {
        "/now": views.now,
        "/show_request": views.show_request,
        "/parameters": views.parameters,
    }

La correspondance entre les fonctions de chemin et de vue est définie comme une constante. Il s'agit d'un ** dictionnaire avec chemin comme clé et ** fonction de vue correspondant au chemin comme valeur.

Colonne: les fonctions python sont des objets primaires

Selon la langue, vous serez peut-être surpris de "définir une" fonction "comme valeur de dictionnaire (ou tableau associatif)" ou "d'assigner une" fonction "à une variable" comme décrit ci-dessus.

Mais en python, c'est un traitement légitime.

Les objets qui peuvent être traités comme des valeurs, comme l'affectation à des variables et le passage à des opérations et des fonctions (en tant qu'arguments et valeurs de retour), sont appelés ** objets de première classe **. En python ** tous les objets sont des objets primaires ** et les fonctions ne font pas exception.

Par conséquent, il est possible d'affecter une fonction à une variable ou de créer une fonction qui reçoit une fonction et renvoie une fonction.

Ce dernier est connu sous le nom de "métaprogrammation" et toute personne intéressée devrait le vérifier.

Ligne 64-69

            #S'il existe une fonction de vue correspondant au chemin, récupérez la fonction et appelez-la pour générer une réponse
            if path in self.URL_VIEW:
                view = self.URL_VIEW[path]
                response_body, content_type, response_line = view(
                    method, path, http_version, request_header, request_body
                )

path in self.URL_VIEW vérifie si la clé dans le dictionnaire self.URL_VIEW contient path. En d'autres termes, nous vérifions si la fonction de vue correspondant au chemin est enregistrée.

S'il a été enregistré, la valeur du dictionnaire correspondant à cette clé est acquise et affectée à la variable «view». Autrement dit, la variable «view» est affectée à la ** fonction de vue ** (plutôt que la valeur de retour de l'appel de la fonction de vue).

Dans la dernière ligne, view (~~) est utilisé pour appeler la fonction affectée à la variable view et obtenir la valeur de retour.


Il est intéressant de noter que ** toutes les fonctions de vue acceptent désormais les mêmes arguments (méthode, chemin, http_version, request_header, request_body), ce qui fait abstraction de la fonction de vue. ** **

Auparavant, les arguments étaient différents pour chaque fonction, donc même si vous disiez "appeler la fonction de vue", vous ne pouviez pas l'appeler correctement à moins de savoir "quel type de fonction est la fonction". Cependant, en unifiant les arguments (= unifiant l'interface), ** "Je ne sais pas quelle est la fonction, mais je peux l'appeler quand même" **.

Cela élimine le besoin de branchement if selon le chemin (ou la fonction) dans workerthread.

Colonne: Abstraction

De cette manière, «il n'est pas nécessaire de traiter des choses concrètes en extrayant seulement une partie des propriétés communes des choses concrètes» s'appelle ** abstracting **. , C'est une technique très importante en programmation.

Dans ce cas, en unifiant l'interface des fonctions concrètes telles que now () show_rewuest () parameters "Prend cinq arguments méthode, chemin, http_version, request_header, request_body et renvoie deux valeurs response_body, response_line " En extrayant (= abstrait) uniquement la propriété, l'appelant "Je ne sais pas combien de fonctions il s'agit, mais je l'appelle avec 5 arguments." Cela signifie que cela peut être géré comme ça.

Ou vous pourriez dire "interface unifiée pour l'abstraction".

ÉTAPE 3: Simplifiez l'interface de la fonction d'affichage

C'est bien que la fonction de vue ait une interface commune et que l'appelant ait une meilleure vue, mais il y a cinq arguments.

Le fait qu'une requête HTTP contienne beaucoup d'informations est un fait inévitable, mais il est difficile de les distribuer et de les stocker dans des variables séparées.

Alors, créons une classe qui exprime la requête HTTP et rassemblons les informations.

Cela simplifie également l'interface de la fonction d'affichage.


Continuez avec Book!

Chapitre "Créer une classe Request / Response / View pour améliorer la visibilité"

Recommended Posts

Mise à jour de «Introduction à l'accueil de l'application Web Python pour les ingénieurs Web de 3e année qui sont lents»
Mise à jour de «Introduction à l'accueil de l'application Web Python pour les ingénieurs Web de 3e année qui sont lents»
Mise à jour de «Introduction à l'accueil de l'application Web Python pour les ingénieurs Web de 3e année qui sont lents»
Mise à jour de «Introduction à l'accueil de l'application Web Python pour les ingénieurs Web de 3e année qui sont lents»
Mise à jour de «Introduction à l'accueil de l'application Web Python pour les ingénieurs Web de 3e année qui sont lents»
Mise à jour de «Introduction à l'accueil de l'application Web Python pour les ingénieurs Web de 3e année qui sont lents»
[Introduction à l'application Udemy Python3 +] 43. instruction for else
Introduction à Python pour, pendant
[Introduction à l'application Udemy Python3 +] 42. pour instruction, instruction break et instruction continue
[Présentation de l'application Udemy Python3 +] 58. Lambda
[Présentation de l'application Udemy Python3 +] 31. Commentaire
[Présentation de l'application Udemy Python3 +] 57. Décorateur
[Présentation de l'application Udemy Python3 +] 56. Clôture
[Présentation de l'application Udemy Python3 +] 59. Générateur
[Introduction à l'application Udemy Python3 +] Résumé
Premiers pas avec Python pour les non-ingénieurs
Explication facile à comprendre de l'application Web Python (Django) même pour les débutants (5) [Introduction au fonctionnement de la base de données avec le shell Django]
[Introduction à Udemy Python3 + Application] 18. Méthode List
[Introduction à Udemy Python3 + Application] 63. Notation d'inclusion du générateur
[Introduction à l'application Udemy Python3 +] 28. Type collectif
[Introduction à l'application Udemy Python3 +] 33. instruction if
[Introduction à Udemy Python3 + Application] 13. Méthode de caractères
[Introduction à l'application Udemy Python3 +] 55. Fonctions intégrées
[Introduction à l'application Udemy Python3 +] 48. Définition des fonctions
[Introduction à l'application Udemy Python3 +] 10. Valeur numérique
[Introduction à l'application Udemy Python3 +] 21. Type Taple
[Introduction à l'application Udemy Python3 +] 45. fonction enumerate
[Introduction à l'application Udemy Python3 +] 41. fonction d'entrée
[Introduction à l'application Udemy Python3 +] 17. Opération de liste
[Introduction à l'application Udemy Python3 +] 65. Gestion des exceptions
[Introduction à l'application Udemy Python3 +] 11. Chaîne de caractères
[Introduction à l'application Udemy Python3 +] 44. fonction range
[Introduction à l'application Udemy Python3 +] 46. fonction zip
[Introduction à l'application Udemy Python3 +] 24. Type de dictionnaire
[Python] Conception d'applications Web pour l'apprentissage automatique
Une introduction à Python pour l'apprentissage automatique
[Introduction à Udemy Python3 + Application] 8. Déclaration de variable
[Introduction à Udemy Python3 + Application] 29. Méthode Set
[Introduction à l'application Udemy Python3 +] 16. Type de liste
[Introduction à Udemy Python3 + Application] 61. Notation d'inclusion de dictionnaire
[Introduction à l'application Udemy Python3 +] 22. Déballage de taples
Une introduction à Python pour les programmeurs en langage C
[Introduction à Udemy Python3 + Application] 47. Traitez le dictionnaire avec une instruction for
Prenons la version gratuite "Introduction à Python pour l'apprentissage automatique" en ligne jusqu'au 27/04
Une introduction aux applications Web Python auto-conçues pour un ingénieur Web de troisième année paresseux
Explication facile à comprendre de l'application Web Python (Django) même pour les débutants (4) [Route setting / Introduction to MTV design patterns]
Mis à jour vers Python 2.7.9
[Introduction à Udemy Python3 + Application] 26. Copie du dictionnaire
[Introduction à l'application Udemy Python3 +] 23. Comment utiliser Tapuru
[Introduction à Udemy Python3 + Application] 60. Notation d'inclusion de liste
[Introduction à Udemy Python3 + Application] 19. Copie de la liste
[Introduction à Udemy Python3 + Application] 38. Lors du jugement Aucun
Introduction à Tornado (1): Framework Web Python démarré avec Tornado
[Introduction à l'application Udemy Python3 +] 40. Instruction while else
[Introduction à Udemy Python3 + Application] 62. Définir la notation d'inclusion
Étapes pour développer une application Web en Python
[Introduction à l'application Udemy Python3 +] 64. Espace de noms et portée
[Introduction à Python3 Jour 20] Chapitre 9 Démêler le Web (9.1-9.4)
[Introduction à Udemy Python3 + Application] 67. Arguments de ligne de commande
[Introduction à l'application Udemy Python3 +] 9. Tout d'abord, imprimez avec print
Introduction à la programmation (Python) TA Tendency pour les débutants