809 votes

Comment détecter si une variable Python est une fonction ?

J'ai une variable, x et je veux savoir si elle pointe vers une fonction ou non.

J'avais espéré pouvoir faire quelque chose comme ça :

>>> isinstance(x, function)

Mais ça me donne :

Traceback (most recent call last):
  File "<stdin>", line 1, in ?
NameError: name 'function' is not defined

La raison pour laquelle j'ai choisi ça est que

>>> type(x)
<type 'function'>

45 votes

Je suis déprimée par le nombre de réponses qui contournent le problème en cherchant des appelez attribut ou fonction appelable... Une façon propre est de type(a) == types.functionType comme suggéré par @ryan

48 votes

La bonne façon de vérifier les propriétés des objets de type canard est de leur demander s'ils font coin-coin, et non de voir s'ils rentrent dans un récipient de la taille d'un canard. L'approche "comparer directement" donnera une mauvaise réponse pour de nombreuses fonctions, comme les buildins.

3 votes

@JohnFeminella Bien que je sois d'accord avec vous sur le principe. Le PO n'a pas demandé si elle était appelable, juste si c'était une fonction. On pourrait peut-être faire valoir qu'il avait besoin d'une distinction entre, par exemple, les fonctions et les classes ?

1035voto

John Feminella Points 116878

Si c'est pour Python 2.x ou pour Python 3.2+, vous pouvez utiliser callable() . Il était auparavant déprécié, mais il ne l'est plus, et vous pouvez donc l'utiliser à nouveau. Vous pouvez lire la discussion ici : http://bugs.python.org/issue10518 . Vous pouvez le faire avec :

callable(obj)

Si c'est pour Python 3.x mais avant 3.2, vérifiez si l'objet possède un __call__ attribut. Vous pouvez le faire avec :

hasattr(obj, '__call__')

L'idée souvent suggérée types.FunctionTypes o inspect.isfunction (les deux font exactement la même chose ) s'accompagne d'un certain nombre d'avertissements. Elle renvoie False pour les fonctions non-Python. La plupart des sites fonctions intégrées par exemple, sont implémentés en C et non en Python, ils retournent donc False :

>>> isinstance(open, types.FunctionType)
False
>>> callable(open)
True

donc types.FunctionType pourrait vous donner des résultats surprenants. La bonne façon de vérifier les propriétés des objets de type canard est de leur demander s'ils font coin-coin, et non de voir s'ils tiennent dans un récipient de la taille d'un canard.

87 votes

Cela ne vous dira pas non plus s'il s'agit d'une fonction, mais simplement si elle peut être appelée.

28 votes

La question de savoir si la distinction est importante ou non dépend de l'application. Je pense que vous avez raison de dire qu'elle ne l'est pas pour la question initiale, mais c'est loin d'être certain.

6 votes

Les classes peuvent avoir un appelez qui lui est rattachée. Ce n'est donc pas une bonne méthode pour faire la distinction. La méthode de Ryan est meilleure.

303voto

Ryan Points 7423

Les types intégrés qui n'ont pas de constructeurs dans l'espace de noms intégré (par exemple, les fonctions, les générateurs, les méthodes) se trouvent dans l'espace de noms intégré. types module. Vous pouvez utiliser types.FunctionType dans un isinstance appeler :

>>> import types
>>> types.FunctionType
<class 'function'>

>>> def f(): pass

>>> isinstance(f, types.FunctionType)
True
>>> isinstance(lambda x : None, types.FunctionType)
True

Notez que cela utilise une notion très spécifique de "fonction" qui n'est généralement pas ce dont vous avez besoin. Par exemple, elle rejette zip (techniquement une classe) :

>>> type(zip), isinstance(zip, types.FunctionType)
(<class 'type'>, False)

open (les fonctions intégrées ont un type différent) :

>>> type(open), isinstance(open, types.FunctionType)
(<class 'builtin_function_or_method'>, False)

et random.shuffle (techniquement, une méthode d'une random.Random instance) :

>>> type(random.shuffle), isinstance(random.shuffle, types.FunctionType)
(<class 'method'>, False)

Si vous faites quelque chose de spécifique pour types.FunctionType comme la décompilation de leur bytecode ou l'inspection des variables de fermeture, utilisent types.FunctionType mais si vous avez juste besoin qu'un objet soit appelable comme une fonction, utilisez callable .

5 votes

+1 répondant à la question. Cependant, essayer de deviner si un objet est une fonction - ou même s'il s'agit de n'importe quel objet appelable - est généralement une erreur. Sans plus d'informations de la part du PO, il est difficile de l'écarter d'emblée, bien sûr, mais quand même...

48 votes

Il retournera en fait False pour les fonctions intégrées, comme 'open' par exemple. Donc pour être spécifique, vous devrez utiliser isinstance(f, (types.FunctionType, types.BuiltinFunctionType)). Et bien sûr, si vous ne voulez que des fonctions, pas de callables ni de méthodes.

5 votes

@ukaszKorzybski et pour être plus précis... vous devriez aussi vérifier pour functools.partial : isinstance(f, (types.FunctionType, types.BuiltinFunctionType, functools.partial)) ou de vérifier f.func dans un tel cas.

101voto

Guandalino Points 3287

Depuis Python 2.1 vous pouvez importer isfunction de la inspect module.

>>> from inspect import isfunction
>>> def f(): pass
>>> isfunction(f)
True
>>> isfunction(lambda x: x)
True

3 votes

Sympa, mais il semble renvoyer False pour les fonctions intégrées comme open et hasattr .

14 votes

@Zecc isbuiltin est pour cela.

14 votes

Voir le inspect.isfunction docstring : "Retourne vrai si l'objet est une fonction définie par l'utilisateur."

79voto

IfLoop Points 59461

La réponse acceptée était, au moment où elle a été proposée, considérée comme correcte. Or, il s'avère s'avère, il y a aucun substitut para callable() qui est de retour dans Python 3.2 : Plus précisément, callable() vérifie le tp_call de l'objet en question testé. Il n'y a pas d'équivalent en Python. La plupart des tests proposés sont corrects la plupart du temps :

>>> class Spam(object):
...     def __call__(self):
...         return 'OK'
>>> can_o_spam = Spam()

>>> can_o_spam()
'OK'
>>> callable(can_o_spam)
True
>>> hasattr(can_o_spam, '__call__')
True
>>> import collections
>>> isinstance(can_o_spam, collections.Callable)
True

Nous pouvons faire une entorse à cette règle en supprimant l'option __call__ de la classe. Et pour rendre les choses encore plus excitantes, ajoutez un faux __call__ à l'instance !

>>> del Spam.__call__
>>> can_o_spam.__call__ = lambda *args: 'OK?'

Remarquez que ce n'est pas vraiment appelable :

>>> can_o_spam()
Traceback (most recent call last):
  ...
TypeError: 'Spam' object is not callable

callable() renvoie le résultat correct :

>>> callable(can_o_spam)
False

Mais hasattr es mauvais :

>>> hasattr(can_o_spam, '__call__')
True

can_o_spam possède cet attribut après tout ; il n'est simplement pas utilisé lors de l'appel de l'instance.

Encore plus subtile, isinstance() se trompe également :

>>> isinstance(can_o_spam, collections.Callable)
True

Parce que nous avons utilisé ce contrôle plus tôt et avons ensuite supprimé la méthode, abc.ABCMeta met en cache le résultat. On peut soutenir qu'il s'agit d'un bogue dans abc.ABCMeta . Cela dit, il n'y a vraiment aucun moyen possible pourrait produisent un résultat plus précis que le résultat qu'en utilisant callable() lui-même, puisque le typeobject->tp_call n'est pas accessible d'une autre manière.

Il suffit d'utiliser callable()

6 votes

Une illustration étonnante des pièges de hasattr(o, '__call__') approche et pourquoi callable() si elle est disponible, est supérieure.

26voto

nh2 Points 4421

L'outil 2to3 de Python ( http://docs.python.org/dev/library/2to3.html ) suggère :

import collections
isinstance(obj, collections.Callable)

Il semble que cela ait été choisi au lieu de la hasattr(x, '__call__') en raison de http://bugs.python.org/issue7006 .

1 votes

Il est également mentionné dans le rapport de bogue sur le fait de ramener l'accès à l'Internet. callable() pour py3.3 : bugs.python.org/issue10518#msg122309

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X