python

title python



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

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 "<stdin>", line 1, in ? NameError: name ''function'' is not defined

La razón por la que elegí es porque

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


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); <type ''function''> >>> str(type(lambda x: x)); "<type ''function''>" # Look Maa, function! ... I ACTUALLY told my mom about this!


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 de tipo pato es preguntándoles si gritan, no para ver si caben en un recipiente del tamaño de un pato. El enfoque "compárelo directamente" dará la respuesta incorrecta para muchas funciones, como las incorporaciones.

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

8.9. tipos - Creación de tipos dinámicos y nombres para 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 "escritura 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 comprobando una función,
Utilice types.BuiltinFunctionType si tiene una demanda más específica.


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"


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 type (f) is function , pero eso no funciona.


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



La respuesta aceptada fue en el momento en que se ofreció la idea de ser correcta. Como resultado, 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 parte 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 utilizando callable() , ya que el método de ranura typeobject->tp_call no es accesible de ninguna otra manera.

Solo usa callable()


La siguiente es una "manera de reprimir" para comprobarlo. También funciona con 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


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


Lo siguiente debe devolver un booleano:

callable(x)


Los tipos incorporados que no tienen constructores en el espacio de nombres incorporado (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]: <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


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 objetivo es filtrar alguna función o detectar propiedades regulares de un objeto.


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


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 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 en el caso de los componentes integrados:

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

La forma correcta de verificar las propiedades de los objetos de tipo pato es preguntándoles si gritan, no para ver si caben en un recipiente del tamaño de un pato. No use types.FunctionType menos que tenga una idea muy específica de qué es una función.


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

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

Entonces 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


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)


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

Para obtener funciones (y por funciones entendemos 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)


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!


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


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á <class ''A''> True y <type function> 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.