¿Cómo detecto si una variable de Python es una función?

Tengo una variable, x , y quiero saber si está apuntando a una función o no.

Esperaba poder hacer algo como:

 >>> isinstance(x, function) 

Pero eso me da:

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

La razón por la que elegí es porque

 >>> type(x)  

Si esto es para Python 2.x o para Python 3.2+, también puede usar callable() . Solía ​​estar en desuso, pero ahora está en desuso, por lo que puede usarlo de nuevo. Puede leer la discusión aquí: http://bugs.python.org/issue10518 . Puedes hacer esto con:

 callable(obj) 

Si esto es para Python 3.x pero antes de la __call__ 3.2, verifique si el objeto tiene un atributo __call__ . Puedes hacer esto con:

 hasattr(obj, '__call__') 

Los types.FunctionTypes sugeridos con types.FunctionTypes enfoque de los types.FunctionTypes no es correcto porque no cubre muchos casos que presumiblemente querría que se aprobaran, como ocurre con los componentes integrados:

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

La forma correcta de verificar las propiedades de los objetos tipificados por el pato es preguntarles si se curren, no para ver si caben en un contenedor del tamaño de un pato. No utilice types.FunctionType menos que tenga una idea muy específica de qué es una función.

Los tipos incorporados que no tienen constructores en el espacio de nombres integrado (por ejemplo, funciones, generadores, métodos) están en el módulo de types . Puede usar types.FunctionType en una llamada de instancia.

 In [1]: import types In [2]: types.FunctionType Out[2]:  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 

Desde Python 2.1 puede importar la isfunction desde el módulo de inspect .

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

La respuesta aceptada fue en el momento en que se ofreció la idea de ser correcta. Resulta que no hay sustituto para callable() , que está de vuelta en Python 3.2: Específicamente, callable() verifica el campo tp_call del objeto que se está probando. No hay un equivalente de Python simple. La mayoría de las pruebas sugeridas son correctas la mayoría del tiempo:

 >>> 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 

Podemos lanzar una llave inglesa eliminando el __call__ de la clase. Y solo para mantener las cosas más emocionantes, ¡agregue un __call__ falso a la instancia!

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

Note que esto realmente no es llamable:

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

callable() devuelve el resultado correcto:

 >>> callable(can_o_spam) False 

Pero hasattr está mal :

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

can_o_spam tiene ese atributo después de todo; Simplemente no se utiliza al llamar a la instancia.

Aún más sutil, isinstance() también se equivoca:

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

Debido a que usamos esta verificación antes y luego abc.ABCMeta el método, abc.ABCMeta almacena en caché el resultado. Podría decirse que este es un error en abc.ABCMeta . Dicho esto, realmente no hay forma posible de que pueda producir un resultado más preciso que el resultado que el uso de callable() , ya que el método de ranura typeobject->tp_call no es accesible de ninguna otra manera.

Solo usa callable()

Lo siguiente debe devolver un booleano:

 callable(x) 

La herramienta 2to3 de Python ( http://docs.python.org/dev/library/2to3.html ) sugiere:

 import collections isinstance(obj, collections.Callable) 

Parece que se eligió este hasattr(x, '__call__') lugar del hasattr(x, '__call__') debido a http://bugs.python.org/issue7006 .

callable(x) devolverá verdadero si se puede llamar al objeto pasado en Python, pero la función no existe en Python 3.0, y hablando correctamente no distinguirá entre:

 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) 

Obtendrá True y True como resultado.

isinstance funciona perfectamente bien para determinar si algo es una función (intente isinstance(b, types.FunctionType) ); Si está realmente interesado en saber si se puede llamar algo, puede usar hasattr(b, '__call__') o simplemente intentarlo.

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

Esto, por supuesto, no le dirá si es llamable pero lanza un TypeError cuando se ejecuta, o no es llamable en primer lugar. Eso puede no importarte.

Si desea detectar todo lo que parece sintácticamente una función: una función, un método, fun / meth incorporado, lambda … pero excluye los objetos que se pueden __call__ (objetos con el método __call__ definido), intente esto:

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

Comparé esto con el código de is*() verifica en el módulo de inspect y la expresión anterior es mucho más completa, especialmente si su objective es filtrar alguna función o detectar propiedades regulares de un objeto.

Trate de usar callable(x) .

Una función es solo una clase con un método __call__ , así que puedes hacer

 hasattr(obj, '__call__') 

Por ejemplo:

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

Esa es la “mejor” forma de hacerlo, pero dependiendo de por qué necesita saber si es llamable o nota, podría ponerlo en un bloque try / execpt:

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

Es discutible si try / except es más Python’y que hacerlo if hasattr(x, '__call__'): x() .. Yo diría que hasattr es más preciso, ya que por casualidad no detectará el TypeError incorrecto, por ejemplo:

 >>> def x(): ... raise TypeError ... >>> hasattr(x, '__call__') True # Correct >>> try: ... x() ... except TypeError: ... print "x was not callable" ... x was not callable # Wrong! 

Aquí hay un par de otras maneras:

 def isFunction1(f) : return type(f) == type(lambda x: x); def isFunction2(f) : return 'function' in str(type(f)); 

Así es como se me ocurrió el segundo:

 >>> type(lambda x: x);  >>> str(type(lambda x: x)); "" # Look Maa, function! ... I ACTUALLY told my mom about this! 

Tenga en cuenta que las clases de Python también son invocables.

Para obtener funciones (y por funciones queremos decir funciones estándar y lambdas) use:

 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) 

Si ha aprendido C++ , debe estar familiarizado con el function object o el functor , significa cualquier objeto al que se be called as if it is a function .

En C ++, an ordinary function es un objeto de función, y también lo es un puntero de función; de manera más general, también lo es un objeto de una clase que define operator() . En C ++ 11 y superior, the lambda expression es el functor .

Similitud, en Python, todos esos functors son callable . An ordinary function puede ser invocable, a lambda expression puede invocarse, un functional.partial puede invocarse, las instancias de class with a __call__() method pueden invocarse.


Bien, vuelva a la pregunta: I have a variable, x, and I want to know whether it is pointing to a function or not.

Si desea juzgar el clima, el objeto actúa como una función, entonces el método callable sugerido por @John Feminella está bien.

Si desea judge whether a object is just an ordinary function or not (no una instancia de clase invocable o una expresión lambda), entonces el xtypes.XXX sugerido por @Ryan es una mejor opción.

Luego hago un experimento usando esos códigos:

 #!/usr/bin/python3 # 2017.12.10 14:25:01 CST # 2017.12.10 15:54:19 CST import functools import types import pprint 

Definir una clase y una función ordinaria.

 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) 

Definir los funtores:

 #(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 

Defina la lista de functores y la lista de tipos:

 ## 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] 

Juez si el funtor es invocable. Como puedes ver, todos son invocables.

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

Juzgue el tipo de functor (tipos.XXX). Entonces los tipos de funtores no son todos iguales.

 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]  [False, True, False, True, False]  [False, True, False, True, False]  at 0x7f1b5081fd08> [False, False, False, False, True] functools.partial(, b=4) [False, False, False, False, False] <__main__.A object at 0x7f1b50870cc0> [False, True, False, True, False]  [False, False, True, False, False] > [False, True, False, True, False]  """ 

Dibujé una tabla de tipos de functor callable usando los datos.

introduzca la descripción de la imagen aquí

Luego puedes elegir los tipos de functores que más te convengan.

como:

 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 

Como las clases también tienen el método __call__ , recomiendo otra solución:

 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 

Como respuesta aceptada, John Feminella declaró que:

La forma correcta de verificar las propiedades de los objetos tipificados por el pato es preguntarles si se curren, no para ver si caben en un contenedor del tamaño de un pato. El enfoque “compárelo directamente” dará la respuesta incorrecta para muchas funciones, como los incorporados.

A pesar de que hay dos libretas para distinguir funciones estrictamente, dibujo una tabla exhaustiva y comparable:

8.9. tipos – Creación de tipos dynamics y nombres para los tipos incorporados – Documentación de Python 3.7.0

30.13. inspeccionar – inspeccionar objetos vivos – documentación de 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', ] 

La “tipificación de pato” es una solución preferida para fines generales:

 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 

En cuanto a la función incorporada.

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

Cuando vaya un paso más para verificar si la función incorporada o la función definida por el usuario

 #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 

Determine si la builtin function

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

Resumen

Emplear callable al tipo de pato que verifica una función,
Utilice types.BuiltinFunctionType si tiene una demanda más específica.

En lugar de buscar '__call__' (que no es exclusivo de las funciones), puede verificar si una función definida por el usuario tiene atributos func_name , func_doc , etc. Esto no funciona con los métodos.

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

Otra forma de verificar es mediante el método isfunction() del módulo de inspect .

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

Para verificar si un objeto es un método, use inspect.ismethod()

Cualquiera que sea la función es una clase, puede tomar el nombre de la clase de instancia x y comparar:

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

Las soluciones que utilizan hasattr(obj, '__call__') y callable(.) Mencionadas en algunas de las respuestas tienen un inconveniente principal: ambas también devuelven True para clases e instancias de clases con un __call__() . P.ej.

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

Una forma adecuada de verificar si un objeto es una función definida por el usuario (y nada más que eso) es usar isfunction(.) :

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

Si necesita buscar otros tipos, consulte Inspeccionar: inspeccionar objetos vivos .

En Python3 se me ocurrió el type (f) == type (lambda x:x) que produce True si f es una función y False si no lo es. Pero creo que prefiero isinstance (f, types.FunctionType) , que se siente menos ad hoc. Quería hacer que type (f) is function , pero eso no funciona.

Siguiendo las respuestas anteriores, se me ocurrió esto:

 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) 

Si el código continuará para realizar la llamada si el valor es llamable, simplemente realice la llamada y capture TypeError .

 def myfunc(x): try: x() except TypeError: raise Exception("Not callable") 

La siguiente es una “manera de reprimir” para comprobarlo. También funciona con lambda.

 def a():pass type(a) # str(type(a))=="" #True b = lambda x:x*2 str(type(b))=="" #True