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

688

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

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

>>> isinstance(x, function)

Mais cela 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 c'est parce que

>>> type(x)
<type 'function'>
Daniel H
la source
37
Je suis déprimé par le nombre de réponses de travail autour du problème en recherchant un certain appel attribut ou une fonction appelable ... Une manière propre est sur le type (a) == types.functionType comme suggéré par @ryan
ASTER
44
@AsTeR La bonne façon de vérifier les propriétés des objets de type canard est de leur demander s'ils charlatanent, et non de voir s'ils tiennent dans un conteneur de la taille d'un canard. L'approche "comparer directement" donnera la mauvaise réponse pour de nombreuses fonctions, comme les fonctions intégrées.
John Feminella
3
@JohnFeminella Bien que je sois d'accord avec vous sur le principe. L'OP n'a pas demandé si c'était appelable, juste si c'était une fonction. On pourrait peut-être soutenir qu'il avait besoin d'une distinction entre, par exemple, les fonctions et les classes?
McKay
3
Pour mes besoins, je suis venu ici parce que je voulais utiliser insepct.getsourcesur une variété d'objets, et il importe en fait non pas si l'objet était appelable mais si c'était quelque chose qui donnerait `` une fonction '' type(obj). Depuis que Google m'a conduit ici, je dirais que le commentaire d'AsTeR était la réponse la plus utile (pour moi). Il existe de nombreux autres endroits sur Internet à découvrir __call__ou à découvrir callable.
tsbertalan
4
@AsTeR Il s'agit de types.FunctionType, avec un majuscule F.
Ben Mares

Réponses:

893

Si c'est pour Python 2.x ou pour Python 3.2+, vous pouvez également utiliser callable(). Auparavant, il était obsolète, mais il n'est plus obsolète, vous pouvez donc le réutiliser. 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 a un __call__attribut. Vous pouvez le faire avec:

hasattr(obj, '__call__')

L' types.FunctionTypesapproche souvent suggérée n'est pas correcte car elle ne couvre pas de nombreux cas que vous souhaiteriez probablement qu'elle passe, comme avec les fonctions intégrées:

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

>>> callable(open)
True

La bonne façon de vérifier les propriétés des objets de type canard est de leur demander s'ils charlatanent, et non de voir s'ils tiennent dans un conteneur de la taille d'un canard. Ne l'utilisez types.FunctionTypeque si vous avez une idée très précise de ce qu'est une fonction.

John Feminella
la source
73
Cela ne vous dira pas non plus si c'est une fonction - juste si elle peut être appelée.
Chris B.
23
Dépend de l'application, que la distinction soit importante ou non; Je suppose que vous avez raison, ce n'est pas le cas pour la question d'origine, mais c'est loin d'être certain.
Chris B.
5
Les classes peuvent être associées à une fonction d' appel . Ce n'est donc certainement pas une bonne méthode pour faire la distinction. La méthode de Ryan est meilleure.
Brian Bruggeman
43
le concept de "typage du canard" en fait la meilleure réponse, par exemple "qu'importe si c'est une fonction tant qu'elle se comporte comme une?"
jcomeau_ictx
8
Il existe des cas où la distinction entre un appelable et une fonction est cruciale, par exemple lors de l'écriture d'un décorateur (voir mon commentaire sur la réponse de Ryan).
Turion
266

Les types intégrés qui n'ont pas de constructeurs dans l'espace de noms intégré (par exemple, fonctions, générateurs, méthodes) sont dans le typesmodule. Vous pouvez utiliser types.FunctionTypedans un isinstanceappel:

In [1]: import types
In [2]: types.FunctionType
Out[2]: <type 'function'>
In [3]: def f(): pass
   ...:
In [4]: isinstance(f, types.FunctionType)
Out[4]: True
In [5]: isinstance(lambda x : None, types.FunctionType)
Out[5]: 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, il 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.Randominstance cachée ):

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

Si vous faites quelque chose de spécifique aux types.FunctionTypeinstances, comme décompiler leur bytecode ou inspecter les variables de fermeture, utilisez types.FunctionType, mais si vous avez juste besoin qu'un objet puisse être appelé comme une fonction, utilisez callable.

Ryan
la source
5
+1 répondant à la question. Cependant, essayer de deviner si un objet est une fonction - ou même s'il s'agit d'un objet appelable - est généralement une erreur. Sans plus d'informations de l'OP, il est difficile de le rejeter d'
emblée
47
Il retournera en fait False pour les fonctions intégrées, comme 'open' pour par exemple. Donc, pour être précis, vous devrez utiliser isinstance (f, (types.FunctionType, types.BuiltinFunctionType)). Et bien sûr, si vous voulez strictement des fonctions, pas des callables ni des méthodes.
Lukasz Korzybski
5
@ ŁukaszKorzybski et pour être plus précis ... vous devriez également vérifier functools.partial: isinstance(f, (types.FunctionType, types.BuiltinFunctionType, functools.partial))ou vérifier f.funcdans un tel cas.
estani
3
@bobince, que diriez-vous de ce cas d'utilisation: je veux écrire un décorateur @fooque je peux utiliser à la fois comme @fooet comme @foo(some_parameter). Il doit ensuite vérifier avec quoi il est appelé, par exemple la fonction à décorer (premier cas) ou le paramètre (le deuxième cas, dans lequel il doit renvoyer un autre décorateur).
Turion
types.BuiltinFunctionTypeest également le type de méthodes intégrées ("normales") , que vous ne voulez probablement pas autoriser, si vous ne suivez pas la callableroute.
user2357112 prend en charge Monica
92

Depuis Python 2.1, vous pouvez importer à isfunctionpartir du inspectmodule.

>>> from inspect import isfunction
>>> def f(): pass
>>> isfunction(f)
True
>>> isfunction(lambda x: x)
True
Paolo
la source
3
Bien, mais il semble renvoyer False pour les fonctions intégrées comme openet hasattr.
Zecc
12
@Zecc isbuiltin est fait pour ça.
Paolo
13
Voir la inspect.isfunctiondocstring: "Retourne vrai si l'objet est une fonction définie par l'utilisateur."
Mark Mikofski
4
Notez que 'isfunction' ne reconnaît pas les fonctions functool.partial.
ishmael
74

La réponse acceptée était au moment où elle a été jugée correcte. Il s'avère qu'il n'y a pas de substitut pour callable(), qui est de retour dans Python 3.2: Spécifiquement, callable()vérifie le tp_callchamp de l'objet testé. Il n'y a pas d'équivalent Python simple. 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 y jeter une clé à molette en retirant le __call__de la classe. Et juste pour garder les choses encore plus excitantes, ajoutez un faux __call__à l'instance!

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

Notez que ce n'est vraiment pas 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 hasattrc'est faux :

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

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

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

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

Étant donné que nous avons utilisé cette vérification plus tôt et supprimé la méthode, abc.ABCMeta le résultat est mis en cache. On peut dire que c'est un bug dans abc.ABCMeta. Cela dit, il n'y a vraiment pas de façon possible , il pourrait produire un résultat plus précis que le résultat qu'en utilisant callable()lui - même, puisque la typeobject->tp_call méthode de fente n'est pas accessible de toute autre manière.

Utilisez simplement callable()

SingleNegationElimination
la source
5
Illustration étonnante des pièges de l' hasattr(o, '__call__')approche et pourquoi callable(), si disponibles, supérieurs.
MestreLion
39

Les éléments suivants doivent renvoyer un booléen:

callable(x)
Nikhil Chelliah
la source
1
Cela résout son problème, mais il a toujours créé un mystère: si x est de la classe 'fonction' dans le module intégré , et que l'aide (x .__ class__) décrit la "fonction de classe", pourquoi "fonction" est "apparemment" non définie "?
Ken
1
"fonction" n'est pas un mot-clé ou un type intégré. Le type de fonctions est défini dans le module "types", comme "types.FunctionType"
Chris B.
19

callable(x) sera de retour vrai si l'objet passé peut être appelé en Python, mais la fonction n'existe pas en Python 3.0, et à proprement parler ne distinguera pas:

class A(object):
    def __call__(self):
        return 'Foo'

def B():
    return 'Bar'

a = A()
b = B

print type(a), callable(a)
print type(b), callable(b)

Vous obtiendrez <class 'A'> Trueet <type function> Trueen sortie.

isinstancefonctionne parfaitement pour déterminer si quelque chose est une fonction (essayez isinstance(b, types.FunctionType)); si vous êtes vraiment intéressé à savoir si quelque chose peut être appelé, vous pouvez soit l'utiliser, hasattr(b, '__call__')soit l'essayer.

test_as_func = True
try:
    b()
except TypeError:
    test_as_func = False
except:
    pass

Bien sûr, cela ne vous dira pas si c'est appelable mais lance un TypeErrorquand il s'exécute, ou n'est pas appelable en premier lieu. Cela ne vous concerne peut-être pas.

Chris B.
la source
8
L'appeler est une mauvaise idée. Que se passe-t-il si cela a des effets secondaires, ou fait réellement quelque chose mais prend très longtemps?
asmeurer
@asmeurer - Sinon, pourquoi auriez-vous besoin de savoir si c'est une fonction si vous ne l'appelez pas?
detly
1
@detly: pour le débogage, je souhaite régulièrement imprimer toutes les variables d'un objet, les méthodes ne me sont généralement pas utiles, je ne voudrais donc pas les exécuter. En fin de compte, je viens de lister toutes les propriétés non appelables avec les valeurs correspondantes :)
Wolph
2
Ce n'est pas parce que vous n'appelez pas qu'il ne sera pas appelé. Peut-être que vous faites de la répartition.
asmeurer
4
Il y a un gros problème avec l'utilisation des exceptions pour savoir si elle était appelable ou non; que se passe-t-il s'il est appelable, mais l'appeler soulève une exception que vous recherchez? Vous allez tous les deux ignorer en silence une erreur et diagnostiquer à tort si elle était appelable. Lorsque vous utilisez EAFP, vous voulez vraiment éviter d'en mettre trop, mais il n'y a aucun moyen de le faire pour ce cas d'utilisation.
Ben
15

Si vous voulez détecter tout ce qui ressemble syntaxiquement à une fonction: une fonction, une méthode, un fun / meth intégré, lambda ... mais exclure les objets appelables (objets avec la __call__méthode définie), alors essayez celui-ci:

import types
isinstance(x, (types.FunctionType, types.BuiltinFunctionType, types.MethodType, types.BuiltinMethodType, types.UnboundMethodType))

J'ai comparé cela avec le code de is*()vérification du inspectmodule et l'expression ci-dessus est beaucoup plus complète, surtout si votre objectif est de filtrer les fonctions ou de détecter les propriétés régulières d'un objet.

Marcin Wojnarski
la source
Merci de me diriger vers le typesmodule. Je testais une make_stemmer()usine qui renvoyait parfois une fonction et parfois une Stemmerinstance appelable , et j'avais besoin de détecter la différence.
plaques
7

Essayez d'utiliser callable(x).

maxyfc
la source
6

Si vous avez appris C++, vous devez être familier avec, function objectou functorsignifie tout objet qui peut be called as if it is a function.

En C ++, an ordinary functionest un objet fonction, tout comme un pointeur de fonction; plus généralement, il en est de même d'un objet d'une classe qui définit operator(). En C ++ 11 et supérieur, the lambda expressionc'est functoraussi le cas.

Similitude, en Python, ce functorssont tous callable. An ordinary functionpeut être appelable, a lambda expressionpeut être appelable, a functional.partialpeut être appelable, les instances de class with a __call__() methodpeuvent être appelables.


Ok, revenons à la question: I have a variable, x, and I want to know whether it is pointing to a function or not.

Si vous voulez évaluer la météo, l'objet agit comme une fonction, alors la callableméthode suggérée par @John Feminellaest correcte.

Si vous le souhaitez judge whether a object is just an ordinary function or not(pas une instance de classe appelable ou une expression lambda), alors le xtypes.XXXsuggéré par @Ryanest un meilleur choix.

Ensuite, je fais une expérience en utilisant ces codes:

#!/usr/bin/python3
# 2017.12.10 14:25:01 CST
# 2017.12.10 15:54:19 CST

import functools
import types
import pprint

Définissez une classe et une fonction ordinaire.

class A():
    def __call__(self, a,b):
        print(a,b)
    def func1(self, a, b):
        print("[classfunction]:", a, b)
    @classmethod
    def func2(cls, a,b):
        print("[classmethod]:", a, b)
    @staticmethod
    def func3(a,b):
        print("[staticmethod]:", a, b)

def func(a,b):
    print("[function]", a,b)

Définissez les foncteurs:

#(1.1) built-in function
builtins_func = open
#(1.2) ordinary function
ordinary_func = func
#(1.3) lambda expression
lambda_func  = lambda a : func(a,4)
#(1.4) functools.partial
partial_func = functools.partial(func, b=4)

#(2.1) callable class instance
class_callable_instance = A()
#(2.2) ordinary class function
class_ordinary_func = A.func1
#(2.3) bound class method
class_bound_method = A.func2
#(2.4) static class method
class_static_func = A.func3

Définissez la liste des foncteurs et la liste des types:

## list of functors
xfuncs = [builtins_func, ordinary_func, lambda_func, partial_func, class_callable_instance, class_ordinary_func, class_bound_method, class_static_func]
## list of type
xtypes = [types.BuiltinFunctionType, types.FunctionType, types.MethodType, types.LambdaType, functools.partial]

Jugez si le foncteur peut être appelé. Comme vous pouvez le voir, ils sont tous appelables.

res = [callable(xfunc)  for xfunc in xfuncs]
print("functors callable:")
print(res)

"""
functors callable:
[True, True, True, True, True, True, True, True]
"""

Jugez le type du foncteur (types.XXX). Ensuite, les types de foncteurs ne sont pas tous les mêmes.

res = [[isinstance(xfunc, xtype) for xtype in xtypes] for xfunc in xfuncs]

## output the result
print("functors' types")
for (row, xfunc) in zip(res, xfuncs):
    print(row, xfunc)

"""
functors' types
[True, False, False, False, False] <built-in function open>
[False, True, False, True, False] <function func at 0x7f1b5203e048>
[False, True, False, True, False] <function <lambda> at 0x7f1b5081fd08>
[False, False, False, False, True] functools.partial(<function func at 0x7f1b5203e048>, b=4)
[False, False, False, False, False] <__main__.A object at 0x7f1b50870cc0>
[False, True, False, True, False] <function A.func1 at 0x7f1b5081fb70>
[False, False, True, False, False] <bound method A.func2 of <class '__main__.A'>>
[False, True, False, True, False] <function A.func3 at 0x7f1b5081fc80>
"""

Je dessine un tableau des types de foncteurs appelables en utilisant les données.

entrez la description de l'image ici

Ensuite, vous pouvez choisir les types de foncteurs qui conviennent.

tel que:

def func(a,b):
    print("[function]", a,b)

>>> callable(func)
True
>>> isinstance(func,  types.FunctionType)
True
>>> isinstance(func, (types.BuiltinFunctionType, types.FunctionType, functools.partial))
True
>>> 
>>> isinstance(func, (types.MethodType, functools.partial))
False
Kinght 金
la source
6

Comme réponse acceptée, John Feminella a déclaré que:

La bonne façon de vérifier les propriétés des objets de type canard est de leur demander s'ils charlatanent, et non de voir s'ils tiennent dans un conteneur de la taille d'un canard. L'approche "comparer directement" donnera la mauvaise réponse pour de nombreuses fonctions, comme les fonctions intégrées.

Même s'il existe deux bibliothèques pour distinguer strictement les fonctions, je dessine un tableau comparable exhaustif:

8.9. types - Création de type dynamique et noms pour les types intégrés - Documentation Python 3.7.0

30.13. inspect - Inspecter des objets vivants - Documentation Python 3.7.0

#import inspect             #import types
['isabstract',
 'isasyncgen',              'AsyncGeneratorType',
 'isasyncgenfunction', 
 'isawaitable',
 'isbuiltin',               'BuiltinFunctionType',
                            'BuiltinMethodType',
 'isclass',
 'iscode',                  'CodeType',
 'iscoroutine',             'CoroutineType',
 'iscoroutinefunction',
 'isdatadescriptor',
 'isframe',                 'FrameType',
 'isfunction',              'FunctionType',
                            'LambdaType',
                            'MethodType',
 'isgenerator',             'GeneratorType',
 'isgeneratorfunction',
 'ismethod',
 'ismethoddescriptor',
 'ismodule',                'ModuleType',        
 'isroutine',            
 'istraceback',             'TracebackType'
                            'MappingProxyType',
]

Le "typage canard" est une solution privilégiée à usage général:

def detect_function(obj):
    return hasattr(obj,"__call__")

In [26]: detect_function(detect_function)
Out[26]: True
In [27]: callable(detect_function)
Out[27]: True

Quant à la fonction intégrée

In [43]: callable(hasattr)
Out[43]: True

Quand aller encore une étape pour vérifier si la fonction intégrée ou la fonction définie par l'utilisateur

#check inspect.isfunction and type.FunctionType
In [46]: inspect.isfunction(detect_function)
Out[46]: True
In [47]: inspect.isfunction(hasattr)
Out[47]: False
In [48]: isinstance(detect_function, types.FunctionType)
Out[48]: True
In [49]: isinstance(getattr, types.FunctionType)
Out[49]: False
#so they both just applied to judge the user-definded

Déterminez si builtin function

In [50]: isinstance(getattr, types.BuiltinFunctionType)
Out[50]: True
In [51]: isinstance(detect_function, types.BuiltinFunctionType)
Out[51]: False

Sommaire

Utilisez callablepour esquiver le type en vérifiant une fonction,
utilisez types.BuiltinFunctionTypesi vous avez spécifié la demande.

Calcul
la source
5

Une fonction est juste une classe avec une __call__méthode, vous pouvez donc faire

hasattr(obj, '__call__')

Par exemple:

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

>>> x = 2
>>> hasattr(x, '__call__')
False

C'est la "meilleure" façon de le faire, mais selon la raison pour laquelle vous devez savoir si c'est appelable ou note, vous pouvez simplement le mettre dans un bloc try / execpt:

try:
    x()
except TypeError:
    print "was not callable"

C'est discutable si try / except est plus Python'y que do if hasattr(x, '__call__'): x().. Je dirais que hasattrc'est plus précis, car vous n'attraperez pas accidentellement la mauvaise TypeError, par exemple:

>>> def x():
...     raise TypeError
... 
>>> hasattr(x, '__call__')
True # Correct
>>> try:
...     x()
... except TypeError:
...     print "x was not callable"
... 
x was not callable # Wrong!
dbr
la source
Utilisez la gestion des exceptions pour vous protéger contre un comportement inattendu uniquement, jamais pour le flux logique - ce n'est certainement pas Pythonic.
gotgenes
Eh bien, hasattr fait essentiellement un getattr dans un bloc try / except (bien qu'en C). blog.jancewicz.net/2007/10/reflection-hasattr.html
dbr
@dbr: Mais hasattr est plus esthétique.
Nikhil Chelliah
5

Voici quelques autres façons:

def isFunction1(f) :
    return type(f) == type(lambda x: x);

def isFunction2(f) :
    return 'function' in str(type(f));

Voici comment j'ai trouvé le second:

>>> type(lambda x: x);
<type 'function'>
>>> str(type(lambda x: x));
"<type 'function'>"
# Look Maa, function! ... I ACTUALLY told my mom about this!
Sumukh Barve
la source
C'est sympa! Devrait fonctionner sur toutes les versions de python2.x et python3.x!
Saurav Kumar
4

Au lieu de vérifier pour '__call__'( ce qui est exclusif à des fonctions), vous pouvez vérifier si une fonction définie par l' utilisateur a des attributs func_name, func_docetc. Cela ne fonctionne pas pour les méthodes.

>>> def x(): pass
... 
>>> hasattr(x, 'func_name')
True

Une autre façon de vérifier est d'utiliser la isfunction()méthode du inspectmodule.

>>> import inspect
>>> inspect.isfunction(x)
True

Pour vérifier si un objet est une méthode, utilisez inspect.ismethod()

Stefan van den Akker
la source
4

Étant donné que les classes ont également une __call__méthode, je recommande une autre solution:

class A(object):
    def __init__(self):
        pass
    def __call__(self):
        print 'I am a Class'

MyClass = A()

def foo():
    pass

print hasattr(foo.__class__, 'func_name') # Returns True
print hasattr(A.__class__, 'func_name')   # Returns False as expected

print hasattr(foo, '__call__') # Returns True
print hasattr(A, '__call__')   # (!) Returns True while it is not a function
guneysus
la source
1
d'accord avec votre réponse, la réponse de John Feminella hasattr(obj, '__call__')est ambiguë.
GoingMyWay
4

Notez que les classes Python sont également appelables.

Pour obtenir des fonctions (et par fonctions, nous entendons des fonctions standard et des lambdas), utilisez:

import types

def is_func(obj):
    return isinstance(obj, (types.FunctionType, types.LambdaType))


def f(x):
    return x


assert is_func(f)
assert is_func(lambda x: x)
Łukasz Rogalski
la source
2

Quelle que soit la fonction est une classe, vous pouvez prendre le nom de la classe d'instance x et comparer:


if(x.__class__.__name__ == 'function'):
     print "it's a function"
Katsu
la source
2

Les solutions utilisant hasattr(obj, '__call__')et callable(.)mentionnées dans certaines des réponses ont un inconvénient principal: les deux retournent également Truepour les classes et les instances de classes avec une __call__()méthode. Par exemple.

>>> import collections
>>> Test = collections.namedtuple('Test', [])
>>> callable(Test)
True
>>> hasattr(Test, '__call__')
True

Une bonne façon de vérifier si un objet est une fonction définie par l'utilisateur (et rien d'autre que ça) est d'utiliser isfunction(.):

>>> import inspect
>>> inspect.isfunction(Test)
False
>>> def t(): pass
>>> inspect.isfunction(t)
True

Si vous devez rechercher d'autres types, jetez un œil à inspecter - Inspecter des objets vivants .

Gerald Senarclens de Grancy
la source
2

Un vérificateur de fonction exact

callable est une très bonne solution. Cependant, je voulais traiter cela de la manière opposée à John Feminella. Au lieu de le traiter comme ceci:

La bonne façon de vérifier les propriétés des objets de type canard est de leur demander s'ils charlatanent, et non de voir s'ils tiennent dans un conteneur de la taille d'un canard. L'approche "comparer directement" donnera la mauvaise réponse pour de nombreuses fonctions, comme les fonctions intégrées.

Nous allons le traiter comme ceci:

La bonne façon de vérifier si quelque chose est un canard n'est pas de voir s'il peut charlatan, mais plutôt de voir s'il s'agit vraiment d'un canard à travers plusieurs filtres, au lieu de simplement vérifier s'il ressemble à un canard à la surface.

Comment pourrions-nous le mettre en œuvre

Le module 'types' possède de nombreuses classes pour détecter les fonctions, les plus utiles étant types.FunctionType , mais il y en a aussi beaucoup d'autres, comme un type de méthode, un type intégré et un type lambda. Nous considérerons également un objet 'functools.partial' comme une fonction.

La manière simple de vérifier s'il s'agit d'une fonction consiste à utiliser une condition isinstance sur tous ces types. Auparavant, je voulais créer une classe de base qui hérite de tout ce qui précède, mais je suis incapable de le faire, car Python ne nous permet pas d'hériter de certaines des classes ci-dessus.

Voici un tableau de quelles classes peuvent classer quelles fonctions:

Tableau des fonctions de kinght- 金 Dessus de table de fonction par kinght- 金

Le code qui le fait

Maintenant, c'est le code qui fait tout le travail que nous avons décrit ci-dessus.

from types import BuiltinFunctionType, BuiltinMethodType,  FunctionType, MethodType, LambdaType
from functools import partial

def is_function(obj):
  return isinstance(obj, (BuiltinFunctionType, BuiltinMethodType,  FunctionType, MethodType, LambdaType, partial))

#-------------------------------------------------

def my_func():
  pass

def add_both(x, y):
  return x + y

class a:
  def b(self):
    pass

check = [

is_function(lambda x: x + x),
is_function(my_func),
is_function(a.b),
is_function(partial),
is_function(partial(add_both, 2))

]

print(check)
>>> [True, True, True, False, True]

Le faux était is_function (partial), parce que c'est une classe, pas une fonction, et c'est exactement des fonctions, pas des classes. Voici un aperçu à partir duquel vous pouvez essayer le code.

Conclusion

callable (obj) est la méthode préférée pour vérifier si un objet est une fonction si vous voulez passer en tapant du canard sur des absolus .

Notre fonction is_function (obj) personnalisée , peut-être avec quelques modifications est la méthode préférée pour vérifier si un objet est une fonction si vous ne comptez aucune instance de classe appelable comme une fonction, mais uniquement les fonctions définies intégrées , ou avec lambda , def , ou partielle .

Et je pense que tout se termine. Bonne journée!

Corman
la source
1

En Python3, j'ai trouvé ce type (f) == type (lambda x:x)qui donne Truesi fest une fonction et Falsesi elle ne l'est pas. Mais je pense que je préfère isinstance (f, types.FunctionType), ce qui semble moins ponctuel. Je voulais le faire type (f) is function, mais cela ne fonctionne pas.

Aaron
la source
0

Suite aux réponses précédentes, j'ai trouvé ceci:

from pprint import pprint

def print_callables_of(obj):
    li = []
    for name in dir(obj):
        attr = getattr(obj, name)
        if hasattr(attr, '__call__'):
            li.append(name)
    pprint(li)
Jabba
la source
0

Vous pouvez essayer ceci:

if obj.__class__.__name__ in ['function', 'builtin_function_or_method']:
    print('probably a function')

ou même quelque chose de plus bizarre:

if "function" in lower(obj.__class__.__name__):
    print('probably a function')
tinnick
la source
-1

Si le code continue d'exécuter l'appel si la valeur est appelable, effectuez simplement l'appel et interceptez TypeError.

def myfunc(x):
  try:
    x()
  except TypeError:
    raise Exception("Not callable")
Roger Dahl
la source
4
C'est dangereux; vous n'avez aucune idée des effets secondaires x.
cwallenpoole
-2

Ce qui suit est un "moyen de repr" pour le vérifier. Cela fonctionne également avec lambda.

def a():pass
type(a) #<class 'function'>
str(type(a))=="<class 'function'>" #True

b = lambda x:x*2
str(type(b))=="<class 'function'>" #True
Vova
la source
-3

Cela fonctionne pour moi:

str(type(a))=="<class 'function'>"
Rafael De Acha
la source
1
Et qu'est-ce que cela nous dit si le résultat est une chaîne vide? Pour une fonction, je reçois "<type 'function'>", pour un entier, je reçois "<type 'int'>", donc je ne vois pas comment cela fonctionne pour vous: /
pawamoy
Ne fonctionne maintenant que pour Python 3 :) En fonction également de l'intention initiale de la question, elle serait incomplète: la fonction intégrée devrait-elle openêtre considérée comme une fonction? str(type(open))donne <class 'builtin_function_or_method'>en Python 3.
pawamoy