À quoi servent-ils? – Vrai Python

By | février 8, 2021

Python pas cher

Fonctions internes, aussi connu sous le nom fonctions imbriquées, sont des fonctions que vous définissez dans d'autres fonctions. En Python, ce type de fonction a un accès direct aux variables et aux noms définis dans la fonction englobante. Les fonctions internes ont de nombreuses utilisations, notamment comme usines de fermeture et fonctions de décorateur.

Dans ce didacticiel, vous apprendrez à:

  • Fournir encapsulation et masquez vos fonctions de l'accès externe
  • Écrire fonctions d'assistance pour faciliter la réutilisation du code
  • Créer fonctions d'usine de fermeture qui conservent l'état entre les appels
  • Code fonctions de décorateur pour ajouter un comportement aux fonctions existantes

Création de fonctions internes Python

Une fonction définie à l'intérieur d'une autre fonction est appelée fonction intérieure ou un fonction imbriquée. En Python, ce type de fonction peut accéder aux noms de la fonction englobante. Voici un exemple de création d'une fonction interne en Python:

>>>

>>> def external_func():
...     def inner_func():
...         impression("Bonjour le monde!")
...     inner_func()
...

>>> external_func()
Bonjour le monde!

Dans ce code, vous définissez inner_func () à l'intérieur external_func () pour imprimer le Bonjour le monde! message à l'écran. Pour faire ça, tu appelles inner_func () sur la dernière ligne de external_func (). C'est le moyen le plus rapide d'écrire une fonction interne en Python. Cependant, les fonctions internes offrent de nombreuses possibilités intéressantes au-delà de ce que vous voyez dans cet exemple.

La caractéristique principale des fonctions internes est leur capacité à accéder aux variables et aux objets à partir de leur fonction englobante même après le retour de cette fonction. La fonction englobante fournit un espace de noms accessible à la fonction interne:

>>>

>>> def external_func(OMS):
...     def inner_func():
...         impression(F"Bonjour, OMS")
...     inner_func()
...

>>> external_func("Monde!")
Bonjour le monde!

Vous pouvez maintenant passer une chaîne comme argument à external_func (), et inner_func () accédera à cet argument via le nom OMS. Ce nom, cependant, est défini dans la portée locale de external_func (). Les noms que vous définissez dans la portée locale d'une fonction externe sont appelés noms non locaux. Ils ne sont pas locaux du inner_func () point de vue.

Voici un exemple de création et d’utilisation d’une fonction interne plus élaborée:

>>>

>>> def factorielle(nombre):
...     # Valider l'entrée
...     si ne pas isinstance(nombre, int):
...         élever Erreur-type("Désolé. 'Nombre' doit être un entier.")
...     si nombre < 0:
...         élever ValueError("Désolé. 'Nombre' doit être zéro ou positif.")
...     # Calculer la factorielle du nombre
...     def inner_factorial(nombre):
...         si nombre <= 1:
...             revenir 1
...         revenir nombre * inner_factorial(nombre - 1)
...     revenir inner_factorial(nombre)
...

>>> factorielle(4)
24

Dans factorielle (), vous commencez par valider les données d'entrée pour vous assurer que votre utilisateur fournit un entier égal ou supérieur à zéro. Ensuite, vous définissez une fonction interne récursive appelée inner_factorial () qui effectue le calcul factoriel et renvoie le résultat. La dernière étape consiste à appeler inner_factorial ().

Le principal avantage de l'utilisation de ce modèle est que, en effectuant toute la vérification des arguments dans la fonction externe, vous pouvez ignorer en toute sécurité la vérification des erreurs dans la fonction interne et vous concentrer sur le calcul en cours.

Utilisation des fonctions internes: les bases

Les cas d'utilisation des fonctions internes de Python sont variés. Vous pouvez les utiliser pour fournir une encapsulation et masquer vos fonctions de l'accès externe, vous pouvez écrire des fonctions internes d'aide, et vous pouvez également créer des fermetures et des décorateurs. Dans cette section, vous découvrirez les deux premiers cas d'utilisation des fonctions internes, et dans les sections suivantes, vous apprendrez à créer des fonctions d'usine de fermeture et des décorateurs.

Fournir une encapsulation

Un cas d'utilisation courant des fonctions internes survient lorsque vous devez protéger ou masquer une fonction donnée de tout ce qui se passe en dehors d'elle afin que la fonction soit totalement cachée de la portée globale. Ce type de comportement est communément appelé encapsulation.

Voici un exemple qui met en évidence ce concept:

>>>

>>> def incrément(nombre):
...     def incrément_intérieur():
...         revenir nombre + 1
...     revenir incrément_intérieur()
...

>>> incrément(dix)
11

>>> # Appeler inner_increment ()
>>> incrément_intérieur()
Traceback (dernier appel le plus récent):
  Fichier "", ligne 1, dans 
    incrément_intérieur()
NameError: le nom 'incrément_intérieur' n'est pas défini

Dans cet exemple, vous ne pouvez pas accéder incrément_interne () directement. Si vous essayez de le faire, vous obtenez un NameError. C'est parce que incrément() se cache totalement incrément_interne (), vous empêchant d'y accéder depuis la portée globale.

Fonctions internes de l'assistant de construction

Parfois, vous avez une fonction qui exécute le même morceau de code à plusieurs endroits dans son corps. Par exemple, supposons que vous souhaitiez écrire une fonction pour traiter un fichier CSV contenant des informations sur les points d'accès Wi-Fi à New York. Pour trouver le nombre total de hotspots à New York ainsi que l'entreprise qui en fournit la plupart, vous créez le script suivant:

# hotspots.py

importer csv
de collections importer Compteur

def process_hotspots(fichier):
    def most_common_provider(file_obj):
        points chauds = []
        avec file_obj comme csv_file:
            contenu = csv.DictReader(csv_file)

            pour rangée dans contenu:
                points chauds.ajouter(rangée[[[["Fournisseur"])

        compteur = Compteur(points chauds)
        impression(
            F"Il y a len(points chauds)    Points d'accès Wi-Fi à New York. n"
            F"compteur.Le plus commun(1)[[[[0][[[[0]    a le plus avec "
            F"compteur.Le plus commun(1)[[[[0][[[[1]. "
        )

    si isinstance(fichier, str):
        # Vous avez un chemin de fichier basé sur une chaîne
        file_obj = ouvert(fichier, "r")
        most_common_provider(file_obj)
    autre:
        # Vous avez un objet fichier
        most_common_provider(fichier)

Ici, processus_hotspots () prend fichier comme argument. La fonction vérifie si fichier est un chemin basé sur une chaîne vers un fichier physique ou un objet fichier. Ensuite, il appelle la fonction interne d'aide most_common_provider (), qui prend un objet fichier et effectue les opérations suivantes:

  1. Lisez le contenu du fichier dans un générateur qui produit des dictionnaires en utilisant csv.DictReader.
  2. Créez une liste de fournisseurs Wi-Fi.
  3. Comptez le nombre de points d'accès Wi-Fi par fournisseur à l'aide d'un collections. objet.
  4. Imprimez un message avec les informations récupérées.

Si vous exécutez la fonction, vous obtenez la sortie suivante:

>>>

>>> de points chauds importer process_hotspots

>>> file_obj = ouvert("./NYC_Wi-Fi_Hotspot_Locations.csv", "r")
>>> process_hotspots(file_obj)
Il y a 3319 hotspots Wi-Fi à New York.
LinkNYC - Citybridge a le plus avec 1868.

>>> process_hotspots("./NYC_Wi-Fi_Hotspot_Locations.csv")
Il y a 3319 hotspots Wi-Fi à New York.
LinkNYC - Citybridge a le plus avec 1868.

Que vous appeliez processus_hotspots () avec un chemin de fichier basé sur une chaîne ou avec un objet fichier, vous obtenez le même résultat.

Utilisation des fonctions d'assistance internes et privées

En règle générale, vous créez des fonctions internes d'aide comme most_common_provider () lorsque vous souhaitez fournir une encapsulation. Vous pouvez également créer des fonctions internes si vous pensez que vous n'allez pas les appeler ailleurs que la fonction contenant.

Bien que l'écriture de vos fonctions d'assistance en tant que fonctions internes donne le résultat souhaité, vous serez probablement mieux servi en les extrayant en tant que fonctions de niveau supérieur. Dans ce cas, vous pouvez utiliser un trait de soulignement (_) dans le nom de la fonction pour indiquer qu'elle est privée pour le module ou la classe en cours. Cela vous permettra d'accéder à vos fonctions d'assistance depuis n'importe où ailleurs dans le module ou la classe en cours et de les réutiliser si nécessaire.

L'extraction de fonctions internes dans des fonctions privées de haut niveau peut rendre votre code plus propre et plus lisible. Cette pratique peut produire des fonctions qui appliquent par conséquent le principe de responsabilité unique.

État de conservation avec fonctions internes: fermetures

En Python, les fonctions sont des citoyens de premier ordre. Cela signifie qu'ils sont à égalité avec tout autre objet, tel que des nombres, des chaînes, des listes, des tuples, des modules, etc. Vous pouvez les créer ou les détruire dynamiquement, les stocker dans des structures de données, les transmettre comme arguments à d'autres fonctions, les utiliser comme valeurs de retour, etc.

Vous pouvez également créer des fonctions d'ordre supérieur en Python. Fonctions d'ordre supérieur sont des fonctions qui opèrent sur d'autres fonctions en les prenant comme arguments, en les renvoyant, ou les deux.

Tous les exemples de fonctions internes que vous avez vus jusqu'à présent sont des fonctions ordinaires qui se trouvent juste être imbriquées dans d'autres fonctions. À moins que vous n'ayez besoin de cacher vos fonctions au monde extérieur, il n'y a aucune raison spécifique pour qu'elles soient imbriquées. Vous pouvez définir ces fonctions comme des fonctions privées de premier niveau, et vous serez prêt à partir.

Dans cette section, vous découvrirez fermeture des fonctions d'usine. Les fermetures sont des fonctions créées dynamiquement qui sont retournées par d'autres fonctions. Leur principale caractéristique est qu'ils ont un accès complet aux variables et aux noms définis dans l'espace de noms local où la fermeture a été créée, même si la fonction englobante est retournée et a fini de s'exécuter.

En Python, lorsque vous retournez un objet de fonction interne, l'interpréteur emballe la fonction avec son environnement contenant ou sa fermeture. L'objet fonction conserve un instantané de toutes les variables et noms définis dans sa portée. Pour définir une fermeture, vous devez suivre trois étapes:

  1. Créez une fonction intérieure.
  2. Variables de référence de la fonction englobante.
  3. Renvoie la fonction interne.

Avec ces connaissances de base, vous pouvez commencer à créer vos fermetures tout de suite et profiter de leur caractéristique principale: état de conservation entre les appels de fonction.

Conservation de l'état dans une fermeture

Une fermeture amène la fonction interne à conserver l'état de son environnement lorsqu'elle est appelée. La fermeture n'est pas la fonction interne elle-même mais la fonction interne avec son environnement englobant. La fermeture capture les variables locales et le nom dans la fonction contenante et les conserve.

Prenons l'exemple suivant:

    1# powers.py
    2
    3def générer de l'énergie(exposant):
    4    def Puissance(base):
    5        revenir base ** exposant
    6    revenir Puissance

Voici ce qui se passe dans cette fonction:

  • Ligne 3 crée générer de l'énergie(), qui est une fonction d'usine de fermeture. Cela signifie qu'il crée une nouvelle fermeture à chaque fois qu'il est appelé, puis la renvoie à l'appelant.
  • Ligne 4 définit Puissance(), qui est une fonction interne qui prend un seul argument, base, et renvoie le résultat de l'expression base ** exposant.
  • Ligne 6 Retour Puissance en tant qu'objet de fonction, sans l'appeler.

Où est-ce que Puissance() obtenir la valeur de exposant de? C'est là que la fermeture entre en jeu. Dans cet exemple, Puissance() obtient la valeur de exposant de la fonction extérieure, générer de l'énergie(). Voici ce que fait Python lorsque vous appelez générer de l'énergie():

  1. Définissez une nouvelle instance de Puissance(), qui prend un seul argument base.
  2. Prenez un instantané de l'état environnant de Puissance(), qui inclut exposant avec sa valeur actuelle.
  3. Revenir Puissance() avec tout son état environnant.

De cette façon, lorsque vous appelez l'instance de Puissance() retourné par générer de l'énergie(), vous verrez que la fonction se souvient de la valeur de exposant:

>>>

>>> de pouvoirs importer générer de l'énergie

>>> lever_two = générer de l'énergie(2)
>>> lever_three = générer de l'énergie(3)

>>> lever_two(4)
16
>>> lever_two(5)
25

>>> lever_three(4)
64
>>> lever_three(5)
125

Dans ces exemples, rise_two () se souvient que exposant = 2, et rise_three () se souvient que exposant = 3. Notez que les deux fermetures se souviennent de leurs exposant entre les appels.

Considérons maintenant un autre exemple:

>>>

>>> def has_permission(page):
...     def autorisation(Nom d'utilisateur):
...         si Nom d'utilisateur.inférieur() == "admin":
...             revenir F"'Nom d'utilisateur'a accès à page. "
...         autre:
...             revenir F"'Nom d'utilisateur'n'a pas accès à page. "
...     revenir autorisation
...

>>> check_admin_page_permision = has_permission("Page d'administration")

>>> check_admin_page_permision("admin")
"'admin' a accès à la page d'administration."

>>> check_admin_page_permision("John")
"'john' n'a pas accès à la page d'administration."

La fonction interne vérifie si un utilisateur donné dispose des autorisations appropriées pour accéder à une page donnée. Vous pouvez rapidement modifier cela pour saisir l'utilisateur en session pour vérifier s'il dispose des informations d'identification correctes pour accéder à un certain itinéraire.

Au lieu de vérifier si l'utilisateur est égal à "admin", vous pouvez interroger une base de données SQL pour vérifier l'autorisation, puis renvoyer la vue correcte selon que les informations d'identification sont correctes.

Vous créez généralement des fermetures qui ne modifient pas leur état de clôture, ou des fermetures avec un état de clôture statique, comme vous l'avez vu dans les exemples ci-dessus. Cependant, vous pouvez également créer des fermetures qui modifient leur état englobant en utilisant des objets mutables, tels que des dictionnaires, des ensembles ou des listes.

Supposons que vous deviez calculer la moyenne d'un ensemble de données. Les données viennent dans un flux de mesures successives du paramètre à analyser, et vous avez besoin de votre fonction pour conserver les mesures précédentes entre les appels. Dans ce cas, vous pouvez coder une fonction d'usine de fermeture comme ceci:

>>>

>>> def signifier():
...     échantillon = []
...     def intérieure_moyenne(nombre):
...         échantillon.ajouter(nombre)
...         revenir somme(échantillon) / len(échantillon)
...     revenir intérieure_moyenne
...

>>> sample_mean = signifier()
>>> sample_mean(100)
100,0
>>> sample_mean(105)
102,5
>>> sample_mean(101)
102,0
>>> sample_mean(98)
101,0

La fermeture attribuée à sample_mean conserve l'état de échantillon entre les appels successifs. Même si vous définissez échantillon dans signifier(), il est toujours disponible dans la fermeture, vous pouvez donc le modifier. Dans ce cas, échantillon fonctionne comme une sorte d'état de clôture dynamique.

Modification de l'état de fermeture

Normalement, les variables de fermeture sont complètement cachées du monde extérieur. Cependant, vous pouvez fournir getter et setter fonctions internes pour eux:

>>>

>>> def make_point(X, y):
...     def point():
...         impression(F"Point(X, y) ")
...     def get_x():
...         revenir X
...     def get_y():
...         revenir y
...     def set_x(valeur):
...         non local X
...         X = valeur
...     def set_y(valeur):
...         non local y
...         y = valeur
...     # Attachez des getters et des setters
...     point.get_x = get_x
...     point.set_x = set_x
...     point.get_y = get_y
...     point.set_y = set_y
...     revenir point
...

>>> point = make_point(1, 2)
>>> point.get_x()
1
>>> point.get_y()
2
>>> point()
Point (1, 2)

>>> point.set_x(42)
>>> point.set_y(sept)
>>> point()
Point (42, 7)

Ici, make_point () renvoie une fermeture qui représente un point objet. Cet objet a des fonctions getter et setter attachées. Vous pouvez utiliser ces fonctions pour obtenir un accès en lecture et en écriture aux variables X et y, qui sont définis dans la portée ci-jointe et sont expédiés avec la fermeture.

Même si cette fonction crée des fermetures qui peuvent fonctionner plus rapidement qu’une classe équivalente, vous devez savoir que cette technique ne fournit pas de fonctionnalités majeures, notamment l’héritage, les propriétés, les descripteurs et les méthodes de classe et statiques. Si vous souhaitez approfondir cette technique, consultez Outil simple pour simuler des classes à l'aide de fermetures et de portées imbriquées (recette Python).

Ajout d'un comportement avec des fonctions internes: décorateurs

Les décorateurs Python sont un autre cas d'utilisation populaire et pratique pour les fonctions internes, en particulier pour les fermetures. Décorateurs sont des fonctions d'ordre supérieur qui prennent un appelable (fonction, méthode, classe) comme argument et renvoient un autre appelable.

Vous pouvez utiliser les fonctions de décorateur pour ajouter des responsabilités à un appelable existant de manière dynamique et étendre son comportement de manière transparente sans affecter ou modifier l'appelable d'origine.

Pour créer un décorateur, il vous suffit de définir un appelable (une fonction, une méthode ou une classe) qui accepte un objet fonction comme argument, le traite et renvoie un autre objet fonction avec un comportement supplémentaire.

Une fois que votre fonction de décorateur est en place, vous pouvez l'appliquer à n'importe quel appelable. Pour ce faire, vous devez utiliser le symbole at (@) devant le nom du décorateur, puis placez-le sur sa propre ligne immédiatement avant l'appelable décoré:

@décorateur
def decor_func():
    # Corps de fonction ...
    passer

Cette syntaxe fait décorateur() prendre automatiquement decor_func () comme argument et le traite dans son corps. Cette opération est un raccourci pour l'affectation suivante:

decor_func = décorateur(decor_func)

Voici un exemple de création d'une fonction de décorateur pour ajouter de nouvelles fonctionnalités à une fonction existante:

>>>

>>> def add_messages(func):
...     def _add_messages():
...         impression("C'est mon premier décorateur")
...         func()
...         impression("Au revoir!")
...     revenir _add_messages
...

>>> @add_messages
... def saluer():
...     impression("Bonjour le monde!")
...

>>> saluer()
C'est mon premier décorateur
Bonjour le monde!
Au revoir!

Dans ce cas, vous utilisez @add_messages décorer saluer(). Cela ajoute de nouvelles fonctionnalités à la fonction décorée. Maintenant quand tu appelles saluer(), au lieu de simplement imprimer Bonjour le monde!, votre fonction imprime deux nouveaux messages.

Les cas d'utilisation des décorateurs Python sont variés. En voici quelques uns:

Une pratique courante pour le débogage du code Python consiste à insérer des appels à impression() pour vérifier les valeurs des variables, pour confirmer qu'un bloc de code est exécuté, et ainsi de suite. Ajouter et supprimer des appels à impression() peut être ennuyeux et vous risquez d'en oublier certains. Pour éviter cette situation, vous pouvez écrire un décorateur comme celui-ci:

>>>

>>> def déboguer(func):
...     def _déboguer(*args, **kwargs):
...         résultat = func(*args, **kwargs)
...         impression(
...             F"func.__Nom__(args: args, kwargs: kwargs) -> résultat"
...         )
...         revenir résultat
...     revenir _déboguer
...

>>> @déboguer
... def ajouter(une, b):
...     revenir une + b
...

>>> ajouter(5, 6)
add (args: (5, 6), kwargs: ) -> 11
11

Cet exemple fournit déboguer(), qui est un décorateur qui prend une fonction comme argument et imprime sa signature avec la valeur actuelle de chaque argument et sa valeur de retour correspondante. Vous pouvez utiliser ce décorateur pour déboguer vos fonctions. Une fois que vous obtenez le résultat souhaité, vous pouvez supprimer l'appel de décorateur @déboguer, et votre fonction sera prête pour la prochaine étape.

Voici un dernier exemple de création d'un décorateur. Cette fois, vous réinstallez générer de l'énergie() en tant que fonction décoratrice:

>>>

>>> def générer de l'énergie(exposant):
...     def Puissance(func):
...         def Puissance intérieure(*args):
...             base = func(*args)
...             revenir base ** exposant
...         revenir Puissance intérieure
...     revenir Puissance
...

>>> @générer de l'énergie(2)
... def lever_two(n):
...     revenir n
...
>>> lever_two(sept)
49

>>> @générer de l'énergie(3)
... def lever_three(n):
...     revenir n
...
>>> lever_three(5)
125

Cette version de générer de l'énergie() produit les mêmes résultats que vous avez obtenus dans l'implémentation d'origine. Dans ce cas, vous utilisez à la fois une fermeture pour vous souvenir exposant et un décorateur qui renvoie une version modifiée de la fonction d'entrée, func ().

Ici, le décorateur doit prendre un argument (exposant), vous devez donc avoir deux niveaux imbriqués de fonctions internes. Le premier niveau est représenté par Puissance(), qui prend la fonction décorée comme argument. Le deuxième niveau est représenté par Puissance intérieure(), qui emballe l'argument exposant dans args, effectue le calcul final de la puissance et renvoie le résultat.

Conclusion

Si vous définissez une fonction dans une autre fonction, vous créez un fonction intérieure, également appelée fonction imbriquée. En Python, les fonctions internes ont un accès direct aux variables et aux noms que vous définissez dans la fonction englobante. Cela vous permet de créer des fonctions d'assistance, des fermetures et des décorateurs.

Dans ce didacticiel, vous avez appris à:

  • Fournir encapsulation en imbriquant des fonctions dans d'autres fonctions
  • Écrire fonctions d'assistance pour réutiliser des morceaux de code
  • Mettre en place fermeture des fonctions d'usine cet état de conservation entre les appels
  • Construire fonctions de décorateur pour fournir de nouvelles fonctionnalités

Vous êtes maintenant prêt à profiter des nombreuses utilisations des fonctions internes dans votre propre code. Si vous avez des questions ou des commentaires, assurez-vous de les partager dans la section des commentaires ci-dessous.

[ad_2]