web-dev-qa-db-fra.com

Fonction imbriquée dans Python

Quels avantages ou implications pourrions-nous obtenir avec Python code comme ceci:

class some_class(parent_class):
    def doOp(self, x, y):
        def add(x, y):
            return x + y
        return add(x, y)

J'ai trouvé cela dans un projet open-source, faisant quelque chose d'utile à l'intérieur de la fonction imbriquée, mais ne faisant absolument rien à l'extérieur, sauf l'appeler. (Le code réel peut être trouvé ici .) Pourquoi quelqu'un pourrait-il le coder comme ça? Y a-t-il un avantage ou un effet secondaire à écrire le code à l'intérieur de la fonction imbriquée plutôt que dans la fonction externe normale?

78
Hosam Aly

Normalement, vous le faites pour faire fermetures:

def make_adder(x):
    def add(y):
        return x + y
    return add

plus5 = make_adder(5)
print(plus5(12))  # prints 17

Les fonctions internes peuvent accéder aux variables à partir de la portée englobante (dans ce cas, la variable locale x). Si vous n'accédez à aucune variable de la portée englobante, ce sont vraiment des fonctions ordinaires avec une portée différente.

103
Adam Rosenfield

Mis à part les générateurs de fonctions, où la création de fonctions internes est presque la définition d'un générateur de fonctions, la raison pour laquelle je crée des fonctions imbriquées est d'améliorer la lisibilité. Si j'ai une fonction minuscule qui ne sera invoquée que par la fonction externe, j'inline la définition pour que vous n'ayez pas à sauter pour déterminer ce que fait cette fonction. Je peux toujours déplacer la méthode interne en dehors de la méthode d'encapsulation si je trouve un besoin de réutiliser la fonction à une date ultérieure.

Exemple de jouet:

import sys

def Foo():
    def e(s):
        sys.stderr.write('ERROR: ')
        sys.stderr.write(s)
        sys.stderr.write('\n')
    e('I regret to inform you')
    e('that a shameful thing has happened.')
    e('Thus, I must issue this desultory message')
    e('across numerous lines.')
Foo()
52
Ross Rogers

Un avantage potentiel de l'utilisation de méthodes internes est qu'il vous permet d'utiliser des variables locales de méthode externe sans les passer comme arguments.

def helper(feature, resultBuffer):
  resultBuffer.print(feature)
  resultBuffer.printLine()
  resultBuffer.flush()

def save(item, resultBuffer):

  helper(item.description, resultBuffer)
  helper(item.size, resultBuffer)
  helper(item.type, resultBuffer)

peut être écrit comme suit, qui se lit sans doute mieux

def save(item, resultBuffer):

  def helper(feature):
    resultBuffer.print(feature)
    resultBuffer.printLine()
    resultBuffer.flush()

  helper(item.description)
  helper(item.size)
  helper(item.type)
24
Kuba

Je ne peux pas imaginer une bonne raison pour un code comme ça.

Peut-être qu'il y avait une raison pour la fonction interne dans les révisions plus anciennes, comme d'autres Ops.

Par exemple, cela rend légèrement plus logique:

class some_class(parent_class):
    def doOp(self, op, x, y):
        def add(x, y):
            return x + y
        def sub(x,y):
            return x - y
        return locals()[op](x,y)

some_class().doOp('add', 1,2)

mais alors la fonction interne devrait être des méthodes de classe ("privées") à la place:

class some_class(object):
    def _add(self, x, y):
        return x + y
    def doOp(self, x, y):
        return self._add(x,y)
8
Jochen Ritzel

L'idée derrière les méthodes locales est similaire aux variables locales: ne pollue pas l'espace de noms plus grand. Évidemment, les avantages sont limités car la plupart des langues ne fournissent pas directement de telles fonctionnalités.

6
avguchenko

Êtes-vous sûr que le code était exactement comme ça? La raison normale de faire quelque chose comme ça est de créer un partiel - une fonction avec des paramètres intégrés. L'appel de la fonction externe renvoie un appelable qui n'a besoin d'aucun paramètre et peut donc être stocké et utilisé quelque part, il est impossible de transmettre des paramètres. Cependant, le code que vous avez publié ne fera pas cela - il appelle la fonction immédiatement et renvoie le résultat, plutôt que l'appelable. Il pourrait être utile de publier le code réel que vous avez vu.

1
Daniel Roseman