Як перевірити, чи існує метод у 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 не є чудовим - це не підтверджує, що назва, наприклад, є методом
Tony Suffolk 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, і це буде виявлено, оскільки метод не існує! Це також руйнує підтримку налагоджувача, щоб зламати винятки. Я також впевнений, що це, мабуть, впливає на продуктивність, якщо щось було в циклі. Останній, але не список, я міг би не хотіти виконувати метод, просто перевірити, якщо він існує. Вам слід подумати про те, щоб видалити цю відповідь або, принаймні, додати всі ці застереження, щоб наївні люди не вводили в оману.
Shital Shah

107

Перевірити, чи клас має такий метод?

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

або

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

Ви можете використовувати self.__class__замістьDynamo


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). Це перевіряє, чи має екземпляр атрибут роботи (може бути змінною або функцією), а потім перевіряє, чи можна викликати (тобто це функція).
Mitch McMabers

14

Ви можете спробувати скористатися модулем 'inspect':

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

is_method(dyn, 'mymethod')

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

4

Як щодо того, щоб шукати це в dyn.__dict__?

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

методи з префіксом underbar за домовленістю означають "приватний".
xtofl

префікс double-underbar плюс суфікс double-underbar означає `` це зазвичай використовується самим інтерпретатором 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

1

Якщо ваш метод знаходиться поза класом, і ви не хочете його запускати та викликати виняток, якщо він не існує:

'mymethod' in globals()


1
>>> def myadd (x, y): повернути x + y >>> 'myadd' у глобальних () 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')

У цих прикладах все ще перелічені обидва методи в двох класах, але якщо ви хочете обмежити перевірку функціонуванням лише в певному класі, це вимагає трохи більше роботи, але це абсолютно можливо.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.