Daniel H.
tengo la variable x
y quiero saber si es 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í eso es porque
>>> type(x)
<type 'function'>
Juan Feminella
Si esto es para Python 2.x o para Python 3.2+, puede usar callable()
. Solía estar en desuso, pero ahora no está en desuso, por lo que puede usarlo nuevamente. Puedes leer la discusión aquí: http://bugs.python.org/issue10518. Puedes hacer esto con:
callable(obj)
Si esto es para Python 3.x pero anterior a 3.2, verifique si el objeto tiene un __call__
atributo. Puedes hacer esto con:
hasattr(obj, '__call__')
El sugerido a menudo types.FunctionTypes
o inspect.isfunction
enfoque (ambos lo hacen exactamente lo mismo) viene con una serie de advertencias. Vuelve False
para funciones que no son de Python. La mayoría funciones integradaspor ejemplo, se implementan en C y no en Python, por lo que devuelven False
:
>>> isinstance(open, types.FunctionType)
False
>>> callable(open)
True
asi que types.FunctionType
podría darte resultados sorprendentes. La forma adecuada de verificar las propiedades de los objetos tipo pato es preguntarles si graznan, no para ver si caben en un recipiente del tamaño de un pato.
-
Esto tampoco le dirá si es una función, solo si se puede llamar.
– Chris B.
9 de marzo de 2009 a las 4:02
-
Depende de la aplicación si la distinción importa o no; Sospecho que tiene razón en que no es así para la pregunta original, pero eso está lejos de ser seguro.
– Chris B.
9 de marzo de 2009 a las 5:33
-
Las clases pueden tener un llamar función que se le atribuye. Así que este definitivamente no es un buen método para distinguir. El método de Ryan es mejor.
-Brian Bruggeman
2 de diciembre de 2011 a las 20:12
-
el concepto de “escribir pato” hace que esta sea la mejor respuesta, por ejemplo, “¿qué importa si es una función siempre que se comporte como tal?”
– jcomeau_ictx
2 de enero de 2012 a las 4:02
-
Hay casos de uso en los que la distinción entre un invocable y una función es crucial, por ejemplo, al escribir un decorador (ver mi comentario sobre la respuesta de Ryan).
– Turión
6 de diciembre de 2013 a las 22:26
Ryan
Los tipos integrados que no tienen constructores en el espacio de nombres integrado (por ejemplo, funciones, generadores, métodos) están en el types
módulo. Puedes usar types.FunctionType
en un isinstance
llamar:
>>> import types
>>> types.FunctionType
<class 'function'>
>>> def f(): pass
>>> isinstance(f, types.FunctionType)
True
>>> isinstance(lambda x : None, types.FunctionType)
True
Tenga en cuenta que esto utiliza una noción muy específica de “función” que generalmente no es lo que necesita. Por ejemplo, rechaza zip
(técnicamente una clase):
>>> type(zip), isinstance(zip, types.FunctionType)
(<class 'type'>, False)
open
(las funciones integradas tienen un tipo diferente):
>>> type(open), isinstance(open, types.FunctionType)
(<class 'builtin_function_or_method'>, False)
y random.shuffle
(técnicamente un método de un oculto random.Random
instancia):
>>> type(random.shuffle), isinstance(random.shuffle, types.FunctionType)
(<class 'method'>, False)
Si estás haciendo algo específico para types.FunctionType
instancias, como descompilar su código de bytes o inspeccionar variables de cierre, use types.FunctionType
pero si solo necesita que se pueda llamar a un objeto como una función, use callable
.
-
+1 respondiendo la pregunta. Sin embargo, tratar de adivinar si un objeto es una función, o incluso si es un objeto invocable, suele ser un error. Sin más información del OP, es difícil descartarlo, por supuesto, pero aún así …
– bobince
9 de marzo de 2009 a las 4:49
-
En realidad, devolverá False para las funciones integradas, como ‘abrir’, por ejemplo. Entonces, para ser específico, deberá usar isinstance (f, (types.FunctionType, types.BuiltinFunctionType)). Y, por supuesto, si desea estrictamente solo funciones, no invocables ni métodos.
– Lukasz Korzybski
20 de abril de 2011 a las 14:06
-
@ŁukaszKorzybski y para ser más precisos… también deberías buscar functools.partial:
isinstance(f, (types.FunctionType, types.BuiltinFunctionType, functools.partial))
o revisandof.func
en cuyo caso.– estani
17 de enero de 2013 a las 12:04
-
@bobince, ¿qué tal este caso de uso: quiero escribir un decorador
@foo
que puedo usar tanto como@foo
y como@foo(some_parameter)
. Luego necesita verificar con qué se está llamando, por ejemplo, la función para decorar (primer caso) o el parámetro (el segundo caso, en el que necesita devolver un decorador adicional).– Turión
6 de diciembre de 2013 a las 22:24
-
types.BuiltinFunctionType
es también el tipo de (“normal”) incorporado métodosque probablemente no quiera permitir, si no va alcallable
ruta.– usuario2357112
12 de noviembre de 2018 a las 19:42
Pablo
Desde Python 2.1 puedes importar isfunction
desde el inspect
módulo.
>>> from inspect import isfunction
>>> def f(): pass
>>> isfunction(f)
True
>>> isfunction(lambda x: x)
True
-
Bien, pero parece devolver False para funciones integradas como
open
yhasattr
.– Zecc
4 mayo 2013 a las 19:43
-
-
Ver el
inspect.isfunction
cadena de documentación: “Devuelve verdadero si el objeto es una función definida por el usuario”.-Mark Mikofski
6 de agosto de 2013 a las 20:27
-
Tenga en cuenta que ‘isfunction’ no reconoce las funciones functool.partial.
– ismael
5 de diciembre de 2013 a las 19:16
-
los código fuente completo de
isfunction
esreturn isinstance(object, types.FunctionType)
por lo que viene con las advertencias señaladas en la respuesta de @Ryan.– Boris Verjovskiy
23 de enero de 2021 a las 21:24
Eliminación de negación única
En el momento en que se ofreció, se pensó que la respuesta aceptada era correcta. Resulta que hay sin sustituto por callable()
que está de vuelta en Python 3.2: Específicamente, callable()
comprueba el tp_call
campo del objeto que se está probando. No hay un equivalente simple de Python. 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 en esto quitando el __call__
de la clase Y solo para mantener las cosas aún más emocionantes, agregue un falso __call__
a la instancia!
>>> del Spam.__call__
>>> can_o_spam.__call__ = lambda *args: 'OK?'
Tenga en cuenta que esto realmente no es invocable:
>>> 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
es equivocado:
>>> hasattr(can_o_spam, '__call__')
True
can_o_spam
tiene ese atributo después de todo; simplemente no se usa al llamar a la instancia.
Aún más sutil, isinstance()
también se equivoca en esto:
>>> isinstance(can_o_spam, collections.Callable)
True
Debido a que usamos esta verificación antes y luego eliminamos el método, abc.ABCMeta
almacena en caché el resultado. Podría decirse que esto es un error en abc.ABCMeta
. Dicho esto, realmente no hay forma posible de que pudo producir un resultado más preciso que el resultado que usando callable()
mismo, desde el typeobject->tp_call
el método de ranura no es accesible de ninguna otra manera.
Solo usa callable()
Lo siguiente debería devolver un valor booleano:
callable(x)
-
Eso resuelve su problema, pero aún crea un misterio: si x es de clase ‘función’ en el módulo incorporadoy help(x.__class__) describe “función de clase”, ¿por qué “función” aparentemente “no está definida”?
– ken
9 de marzo de 2009 a las 3:52
-
“función” no es una palabra clave ni un tipo integrado. El tipo de funciones se define en el módulo “types”, como “types.FunctionType”
– Chris B.
9 de marzo de 2009 a las 4:03
Lutz Prechelt
Resultado
invocable (x) | hasattr(x, ‘__llamar__’) | inspeccionar.isfunction(x) | inspeccionar.ismethod(x) | inspeccionar.isgeneratorfunction(x) | inspeccionar.isroutinefunction(x) | inspeccionar.isasyncgenfunction(x) | isinstance(x, escribiendo. Llamable) | isinstance(x, tipos.BuiltinFunctionType) | isinstance(x, tipos.BuiltinMethodType) | esinstancia(x, tipos.TipoFunción) | isinstance(x, tipos.MethodType) | isinstance(x, tipos.LambdaType) | isinstance(x, functools.parcial) | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
impresión | √ | √ | × | × | × | × | × | √ | √ | √ | × | × | × | × |
función | √ | √ | √ | × | × | × | × | √ | × | × | √ | × | √ | × |
functools.parcial | √ | √ | × | × | × | × | × | √ | × | × | × | × | × | √ |
√ | √ | √ | × | × | × | × | √ | × | × | √ | × | √ | × | |
generador | √ | √ | √ | × | √ | × | × | √ | × | × | √ | × | √ | × |
función_async | √ | √ | √ | × | × | √ | × | √ | × | × | √ | × | √ | × |
generador_async | √ | √ | √ | × | × | × | √ | √ | × | × | √ | × | √ | × |
A | √ | √ | × | × | × | × | × | √ | × | × | × | × | × | × |
metanfetamina | √ | √ | √ | × | × | × | × | √ | × | × | √ | × | √ | × |
metanfetamina | √ | √ | × | √ | × | × | × | √ | × | × | × | √ | × | × |
metástasis | √ | √ | √ | × | × | × | × | √ | × | × | √ | × | √ | × |
import types
import inspect
import functools
import typing
def judge(x):
name = x.__name__ if hasattr(x, '__name__') else 'functools.partial'
print(name)
print('\ttype({})={}'.format(name, type(x)))
print('\tcallable({})={}'.format(name, callable(x)))
print('\thasattr({}, \'__call__\')={}'.format(name, hasattr(x, '__call__')))
print()
print('\tinspect.isfunction({})={}'.format(name, inspect.isfunction(x)))
print('\tinspect.ismethod({})={}'.format(name, inspect.ismethod(x)))
print('\tinspect.isgeneratorfunction({})={}'.format(name, inspect.isgeneratorfunction(x)))
print('\tinspect.iscoroutinefunction({})={}'.format(name, inspect.iscoroutinefunction(x)))
print('\tinspect.isasyncgenfunction({})={}'.format(name, inspect.isasyncgenfunction(x)))
print()
print('\tisinstance({}, typing.Callable)={}'.format(name, isinstance(x, typing.Callable)))
print('\tisinstance({}, types.BuiltinFunctionType)={}'.format(name, isinstance(x, types.BuiltinFunctionType)))
print('\tisinstance({}, types.BuiltinMethodType)={}'.format(name, isinstance(x, types.BuiltinMethodType)))
print('\tisinstance({}, types.FunctionType)={}'.format(name, isinstance(x, types.FunctionType)))
print('\tisinstance({}, types.MethodType)={}'.format(name, isinstance(x, types.MethodType)))
print('\tisinstance({}, types.LambdaType)={}'.format(name, isinstance(x, types.LambdaType)))
print('\tisinstance({}, functools.partial)={}'.format(name, isinstance(x, functools.partial)))
def func(a, b):
pass
partial = functools.partial(func, a=1)
_lambda = lambda _: _
def generator():
yield 1
yield 2
async def async_func():
pass
async def async_generator():
yield 1
class A:
def __call__(self, a, b):
pass
def meth(self, a, b):
pass
@classmethod
def classmeth(cls, a, b):
pass
@staticmethod
def staticmeth(a, b):
pass
for func in [print,
func,
partial,
_lambda,
generator,
async_func,
async_generator,
A,
A.meth,
A.classmeth,
A.staticmeth]:
judge(func)
Tiempo
Elija los tres métodos más comunes:
veces | |
---|---|
invocable (x) | 0.86 |
hasattr(x, ‘__llamar__’) | 1.36 |
isinstance(x, escribiendo. Llamable) | 12.19 |
import typing
from timeit import timeit
def x():
pass
def f1():
return callable(x)
def f2():
return hasattr(x, '__call__')
def f3():
return isinstance(x, typing.Callable)
print(timeit(f1, number=10000000))
print(timeit(f2, number=10000000))
print(timeit(f3, number=10000000))
# 0.8643081
# 1.3563508
# 12.193492500000001
-
Eso resuelve su problema, pero aún crea un misterio: si x es de clase ‘función’ en el módulo incorporadoy help(x.__class__) describe “función de clase”, ¿por qué “función” aparentemente “no está definida”?
– ken
9 de marzo de 2009 a las 3:52
-
“función” no es una palabra clave ni un tipo incorporado. El tipo de funciones se define en el módulo “types”, como “types.FunctionType”
– Chris B.
9 de marzo de 2009 a las 4:03
Gary van der Merwe
La herramienta 2to3 de Python (http://docs.python.org/dev/library/2to3.html) sugiere:
import collections
isinstance(obj, collections.Callable)
Parece que este fue elegido en lugar del hasattr(x, '__call__')
método debido a http://bugs.python.org/issue7006.
-
También se menciona en el informe de error sobre traer de vuelta
callable()
para py3.3: bugs.python.org/issue10518#msg122309– Luckydonald
19 de agosto de 2018 a las 7:56
Estoy deprimido por la cantidad de respuestas que solucionan el problema al buscar algunas llamar atributo o función invocable … Una forma limpia es sobre tipo (a) == tipos. Tipo de función como lo sugiere @ryan
– AsTeR
20 de septiembre de 2013 a las 12:09
@AsTeR La forma correcta de verificar las propiedades de los objetos tipo pato es preguntarles si graznan, no para ver si caben en un contenedor del tamaño de un pato. El enfoque de “compararlo directamente” dará la respuesta incorrecta para muchas funciones, como funciones integradas.
– Juan Feminella
2 de junio de 2014 a las 16:58
@JohnFeminella Aunque estoy de acuerdo contigo en principio. El OP no preguntó si se podía llamar, solo si es una función. ¿Quizás se podría argumentar que necesitaba una distinción entre, por ejemplo, funciones y clases?
– McKay
28 de febrero de 2017 a las 18:15
Para mis propósitos, vine aquí porque quería usar
insepct.getsource
en una variedad de objetos, y en realidad no importa si el objeto era invocable sino si era algo que daría ‘función’ paratype(obj)
. Dado que Google me llevó aquí, diría que el comentario de AsTeR fue la respuesta más útil (para mí). Hay muchos otros lugares en Internet para que la gente los descubra.__call__
ocallable
.– tsbertalan
07/12/2017 a las 21:23
@AsTeR Es types.FunctionType, con F mayúscula.
– Ben Yeguas
8 de noviembre de 2019 a las 17:32