¿Cómo comprobar si existe un método en Python?


83

En la función __getattr__(), si no se encuentra una variable referida, da un error. ¿Cómo puedo verificar si una variable o método existe como parte de un objeto?

import string
import logging

class Dynamo:
 def __init__(self,x):
  print "In Init def"
  self.x=x
 def __repr__(self):
  print self.x
 def __str__(self):
  print self.x
 def __int__(self):
  print "In Init def"
 def __getattr__(self, key):
    print "In getattr"
    if key == 'color':
        return 'PapayaWhip'
    else:
        raise AttributeError


dyn = Dynamo('1')
print dyn.color
dyn.color = 'LemonChiffon'
print dyn.color
dyn.__int__()
dyn.mymethod() //How to check whether this exist or not

Respuestas:


91

¿Qué tal dir()funcionar antes getattr()?

>>> "mymethod" in dir(dyn)
True

7
Esto no comprueba si es un método o una variable
hek2mgl

1
usar dir no es genial; no confirma que el nombre sea un método, por ejemplo
Tony Suffolk 66

108

Es más fácil pedir perdón que pedir permiso.

No compruebe si existe un método. No pierda ni una sola línea de código en "comprobar"

try:
    dyn.mymethod() # How to check whether this exists or not
    # Method exists and was used.  
except AttributeError:
    # Method does not exist; What now?

56
Pero tal vez él realmente no quiera llamarlo, solo para verificar si existe ese método (como es en mi caso) ...
Flavius

48
Tenga en cuenta que esto fallará si se dyn.mymethod()genera una AttributeError.
DK

10
como dice @DK, esto atrapará cualquier AttributeError que pueda ser generado por el método que se está verificando, lo cual puede ser indeseable (sin mencionar que inferiría erróneamente la ausencia del método en ese caso).
ShreevatsaR

3
Bueno en principio, y Python tiene una cultura de "excepciones como flujo de control" a diferencia de otros lenguajes. Sin embargo, si está utilizando herramientas de registro de excepciones como Sentry / Raven o New Relic, dichas excepciones deben filtrarse individualmente (si es posible) o generar ruido. Preferiría verificar si el método existe en lugar de llamarlo.
RichVel

2
Esto está mal en muchos niveles. El método en sí puede generar AttributeError y esto se detectaría ya que el método no existe. También arruina el soporte del depurador para romper las excepciones. También estoy seguro de que esto probablemente afecte el rendimiento si la cosa estuviera en bucle. Por último, pero no en la lista, es posible que no desee ejecutar el método, solo valide si existe. Debería considerar eliminar esta respuesta o al menos poner todas estas advertencias para que las personas ingenuas no se confundan.
Shital Shah

107

¿Compruebe si la clase tiene ese método?

hasattr(Dynamo, key) and callable(getattr(Dynamo, key))

o

hasattr(Dynamo, 'mymethod') and callable(getattr(Dynamo, 'mymethod'))

Puedes usar en self.__class__lugar deDynamo


23
Noneno se puede llamar, por lo que podría hacerlo callable(getattr(Dynamo, 'mymethod', None)). Usé esta respuesta porque mi super (). Mymethod () podría arrojarAttributeError
sbutler

@sbutler Interesante que funcione. Según PyCharm, la firma de getattr es def getattr(object, name, default=None):, sospecho que no es precisa porque si lo fuera, esperaría pasar None como tercer parámetro para no cambiar el comportamiento de la función.
bszom

@bszom: En un shell de Python, help(getattr)dice "Cuando se proporciona un argumento predeterminado, se devuelve cuando el atributo no existe; sin él, se genera una excepción en ese caso". - (y de hecho puede comprobar que getattr genera una excepción si falta el atributo) tan claramente, sea lo que sea PyCharm, está mal.
ShreevatsaR

@ShreevatsaR Gracias por confirmar mi sospecha. PyCharm es un IDE.
bszom

4
Versión más simple: Si desea comprobar si la instancia actual clase tiene un atributo y es exigible, simplemente hacer esto: if hasattr(self, "work") and callable(self.work). Esto verifica si la instancia tiene un atributo de trabajo (puede ser una variable o una función) y luego verifica si se puede llamar (lo que significa que es una función).
Mitch McMabers

14

Puede intentar usar el módulo 'inspeccionar':

import inspect
def is_method(obj, name):
    return hasattr(obj, name) and inspect.ismethod(getattr(obj, name))

is_method(dyn, 'mymethod')

12

Utilizo la siguiente función de utilidad. Funciona con lambda, métodos de clase y métodos de instancia.

Método de utilidad

def has_method(o, name):
    return callable(getattr(o, name, None))

Ejemplo de uso

Definamos la clase de prueba

class MyTest:
  b = 'hello'
  f = lambda x: x

  @classmethod
  def fs():
    pass
  def fi(self):
    pass

Ahora puedes intentarlo

>>> a = MyTest()                                                    
>>> has_method(a, 'b')                                         
False                                                          
>>> has_method(a, 'f')                                         
True                                                           
>>> has_method(a, 'fs')                                        
True                                                           
>>> has_method(a, 'fi')                                        
True                                                           
>>> has_method(a, 'not_exist')                                       
False                                                          

2
Esta respuesta es más adecuada (al menos en mi opinión) que otras respuestas porque no usa un enfoque general (usando una trydeclaración) y verifica cada vez que el miembro es una función real. gran parte!
ymz

4

¿Qué tal buscarlo en dyn.__dict__?

try:
    method = dyn.__dict__['mymethod']
except KeyError:
    print "mymethod not in dyn"

métodos con prefijo underbar por convención significa 'privado'.
xtofl

El prefijo de doble subrayado más el sufijo de doble subrayado significa 'esto normalmente lo usa el intérprete de Python' y, por lo general, hay alguna forma de lograr el mismo efecto del programa de un usuario para los casos de uso más comunes (en este caso sería usando notación de puntos para un atributo), pero no está prohibido ni es incorrecto usarlo si su caso de uso realmente lo requiere.
deStrangis

No esta prohibido. El error es subjetivo: depende de cuánto quieras confundir a los programadores que se adhieren a las convenciones: no lo uses a menos que no tengas otra alternativa.
xtofl

3

Tal vez así, asumiendo que todos los métodos son invocables

app = App(root) # some object call app 
att = dir(app) #get attr of the object att  #['doc', 'init', 'module', 'button', 'hi_there', 'say_hi']

for i in att: 
    if callable(getattr(app, i)): 
        print 'callable:', i 
    else: 
        print 'not callable:', i

1

Si su método está fuera de una clase y no desea ejecutarlo y generar una excepción si no existe:

'mymethod' in globals()


1
>>> def myadd (x, y): return x + y >>> 'myadd' en globals () Verdadero
gerowam

-1

Creo que deberías mirar el inspectpaquete. Te permite 'envolver' algunas de las cosas. Cuando usa el dirmétodo, también enumera los métodos integrados, los métodos heredados y todos los demás atributos que hacen posibles las colisiones, por ejemplo:

class One(object):

    def f_one(self):
        return 'class one'

class Two(One):

    def f_two(self):
        return 'class two'

if __name__ == '__main__':
    print dir(Two)

La matriz de la que obtiene dir(Two)contiene f_oney f_twoy muchas cosas integradas. Con inspectusted puede hacer esto:

class One(object):

    def f_one(self):
        return 'class one'

class Two(One):

    def f_two(self):
        return 'class two'

if __name__ == '__main__':
    import inspect

    def testForFunc(func_name):
        ## Only list attributes that are methods
        for name, _ in inspect.getmembers(Two, inspect.ismethod):
            if name == func_name:
                return True
        return False

    print testForFunc('f_two')

Estos ejemplos aún enumeran ambos métodos en las dos clases, pero si desea limitar la inspección para que solo funcione en una clase específica, requiere un poco más de trabajo, pero es absolutamente posible.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.