Как проверить, существует ли метод в Python?

83

В функции __getattr__(), если указанная переменная не найдена, выдается ошибка. Как я могу проверить, существует ли переменная или метод как часть объекта?

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
Раджив
источник

Ответы:

91

Как насчет dir()функции раньше getattr()?

>>> "mymethod" in dir(dyn)
True
Михал Шрайер
источник
7
Это не проверяет, является ли это методом или переменной
hek2mgl
1
использование dir не очень хорошо - это не подтверждает, что имя является, например, методом
Тони Саффолк 66,
109

Проще просить прощения, чем разрешения.

Не проверяйте, существует ли метод. Не тратьте ни единой строчки кода на "проверку"

try:
    dyn.mymethod() # How to check whether this exists or not
    # Method exists and was used.  
except AttributeError:
    # Method does not exist; What now?
С.Лотт
источник
57
Но, возможно, он действительно не хочет называть это, просто чтобы проверить, существует ли этот метод (как в моем случае) ...
Флавий
49
Обратите внимание, что это не удастся, если dyn.mymethod()возникает AttributeErrorсам.
DK
10
как говорит @DK, это перехватит любую AttributeError, которая может быть вызвана проверяемым методом, что может быть нежелательным (не говоря уже о том, что в этом случае это будет ошибочным выводом об отсутствии метода).
ShreevatsaR
3
В принципе, это хорошо, и в Python есть культура «исключения как поток управления», в отличие от других языков. Однако, если вы используете инструменты регистрации исключений, такие как Sentry / Raven или New Relic, такие исключения должны быть отфильтрованы индивидуально (если это возможно) или генерировать шум. Я бы предпочел проверить, существует ли метод, а не вызывать его.
RichVel
2
Это не правильно на многих уровнях. Сам метод может вызвать ошибку AttributeError, и это будет обнаружено, поскольку метод не существует! Это также разрушает поддержку отладчика из-за исключения исключений. Я также уверен, что это, вероятно, повлияет на производительность, если все будет в цикле. Последний, но не список, который я мог бы не захотеть выполнять, просто проверьте, существует ли он. Вам следует подумать об удалении этого ответа или, по крайней мере, о всех этих предупреждениях, чтобы наивные люди не вводили в заблуждение.
Шитал Шах,
107

Проверить, есть ли у класса такой метод?

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

или же

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

Вы можете использовать self.__class__вместоDynamo

seriyPS
источник
23
Noneне вызывается, поэтому вы можете просто сделать callable(getattr(Dynamo, 'mymethod', None)). Я использовал этот ответ, потому что мой super (). Mymethod () мог выброситьAttributeError
sbutler
@sbutler Интересно, что работает. Согласно PyCharm, подпись для getattr, def getattr(object, name, default=None):я подозреваю, не точна, потому что если бы это было так, я бы ожидал, что передача None в качестве третьего параметра не изменит поведение функции.
bszom
@bszom: в оболочке python help(getattr): «Когда задан аргумент по умолчанию, он возвращается, если атрибут не существует; без него в этом случае возникает исключение». - (и действительно, вы можете проверить, что getattr вызывает исключение, если атрибут отсутствует) так ясно, что что бы ни было PyCharm, это неправильно.
ShreevatsaR
@ShreevatsaR Спасибо, что подтвердили мои подозрения. PyCharm - это IDE.
bszom
4
ПРОЩЕ версия: Если вы хотите , чтобы проверить , если текущий экземпляр класса имеет атрибут , и это отозвано, просто сделать это: if hasattr(self, "work") and callable(self.work). Это проверяет, имеет ли экземпляр рабочий атрибут (может быть либо переменной, либо функцией), а затем проверяет, может ли он быть вызван (что означает, что это функция).
Митч МакМаберс
14

Вы можете попробовать использовать модуль inspect:

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

is_method(dyn, 'mymethod')
винт83
источник
12

Я использую служебную функцию ниже. Он работает с лямбда-выражениями, методами класса, а также с методами экземпляра.

Полезный метод

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

Пример использования

Определим тестовый класс

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

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

Теперь ты можешь попробовать,

>>> 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
Этот ответ более подходит (по крайней мере, на мой взгляд), чем другие ответы, потому что он не использует общий подход (используя tryоператор) и проверяет всякий раз, когда член является фактической функцией. отличная доля!
ymz 07
4

Как насчет того, чтобы поискать это dyn.__dict__?

try:
    method = dyn.__dict__['mymethod']
except KeyError:
    print "mymethod not in dyn"
де Странджис
источник
Методы с префиксом подчеркивания по соглашению означают «частные».
xtofl
префикс с двойным подчеркиванием плюс суффикс с двойным подчеркиванием означает, что `` это обычно используется самим интерпретатором Python '', и обычно есть способ добиться того же эффекта от пользовательской программы для наиболее распространенных случаев использования (в этом случае это будет с использованием точечной нотации для атрибута), но использовать его не запрещено и не неправильно, если в вашем случае это действительно требуется.
deStrangis
Это не запрещено. Ошибочность субъективна: это зависит от того, насколько вы хотите запутать программистов, которые придерживаются соглашений: не используйте это, если у вас нет альтернативы.
xtofl
3

Может быть, так, если все методы вызываются

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
user6559980
источник
1

Если ваш метод находится за пределами класса, и вы не хотите запускать его и вызывать исключение, если оно не существует:

'mymethod' in globals()

Gerowam
источник
1
>>> def myadd (x, y): return x + y >>> 'myadd' в globals () True
gerowam
-1

Думаю, тебе стоит посмотреть на inspectупаковку. Это позволяет «обернуть» некоторые вещи. Когда вы используете dirметод, он также перечисляет встроенные методы, унаследованные методы и все другие атрибуты, делающие возможными коллизии, например:

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)

Массив, который вы получаете, dir(Two)содержит как f_oneи, так f_twoи множество встроенных вещей. С помощью inspectвы можете сделать это:

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

В этих примерах по-прежнему перечислены оба метода в двух классах, но если вы хотите ограничить проверку только функцией в определенном классе, потребуется немного больше работы, но это абсолютно возможно.

благоговение
источник