Jak sprawdzić, czy metoda istnieje w Pythonie?

83

W funkcji __getattr__(), jeśli zmienna, do której odnosi się odwołanie, nie zostanie znaleziona, spowoduje to błąd. Jak mogę sprawdzić, czy zmienna lub metoda istnieje jako część obiektu?

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
Rajeev
źródło

Odpowiedzi:

91

A co z dir()funkcją wcześniej getattr()?

>>> "mymethod" in dir(dyn)
True
Michał Šrajer
źródło
7
To nie sprawdza, czy jest to metoda, czy zmienna
hek2mgl
1
używanie dir nie jest świetne - nie potwierdza, że ​​nazwa jest na przykład metodą
Tony Suffolk 66
109

Łatwiej jest prosić o przebaczenie niż o pozwolenie.

Nie sprawdzaj, czy metoda istnieje. Nie trać ani jednej linii kodu na „sprawdzanie”

try:
    dyn.mymethod() # How to check whether this exists or not
    # Method exists and was used.  
except AttributeError:
    # Method does not exist; What now?
S.Lott
źródło
57
Ale może naprawdę nie chce tego nazywać, tylko po to, by sprawdzić, czy istnieje taka metoda (tak jak jest w moim przypadku) ...
Flavius
49
Zauważ, że to się nie powiedzie, jeśli dyn.mymethod()podniesie AttributeErrorsam.
DK,
10
jak mówi @DK, spowoduje to przechwycenie każdego błędu AttributeError, który może zostać wywołany przez sprawdzaną metodę, co może być niepożądane (nie wspominając o tym, że błędnie wywnioskowałoby to o braku metody w tym przypadku).
ShreevatsaR
3
Zasadniczo dobre, a Python ma kulturę „wyjątków jako przepływu sterowania” w przeciwieństwie do innych języków. Jeśli jednak używasz narzędzi do rejestrowania wyjątków, takich jak Sentry / Raven lub New Relic, takie wyjątki muszą być odfiltrowywane indywidualnie (jeśli to możliwe) lub generować szum. Wolałbym raczej sprawdzić, czy metoda istnieje, niż ją wywoływać.
RichVel
2
To jest złe na tak wielu poziomach. Sama metoda może zgłosić błąd AttributeError i zostanie to wykryte, ponieważ metoda nie istnieje! Niszczy również obsługę debugera, aby przerywać wyjątki. Jestem również pewien, że prawdopodobnie wpłynie to na wydajność, jeśli coś się zapętli. Ostatnia, ale nie lista, może nie chcę wykonywać metody, po prostu sprawdź, czy istnieje. Powinieneś rozważyć usunięcie tej odpowiedzi lub przynajmniej umieścić wszystkie te ostrzeżenia, aby naiwni ludzie nie dali się zwieść.
Shital Shah,
107

Sprawdź, czy klasa ma taką metodę?

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

lub

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

Możesz użyć self.__class__zamiastDynamo

seriyPS
źródło
23
Nonenie jest wywoływalne, więc możesz to zrobić callable(getattr(Dynamo, 'mymethod', None)). Użyłem tej odpowiedzi, ponieważ moja super (). Mymethod () mogła rzucićAttributeError
sbutler
@sbutler Ciekawe, że to działa. Według PyCharm podpis dla getattr jest def getattr(object, name, default=None):podejrzewany, że nie jest dokładny, ponieważ gdyby tak było, spodziewałbym się, że podanie None jako trzeciego parametru nie zmieni zachowania funkcji.
bszom
@bszom: W powłoce Pythona help(getattr)mówi: „Gdy podano domyślny argument, jest on zwracany, gdy atrybut nie istnieje; bez niego zgłaszany jest wyjątek w takim przypadku”. - (i rzeczywiście możesz sprawdzić, czy getattr zgłasza wyjątek, jeśli brakuje atrybutu), więc wyraźnie, niezależnie od PyCharm, jest źle.
ShreevatsaR
@ShreevatsaR Dziękuję za potwierdzenie moich podejrzeń. PyCharm to IDE.
bszom
4
Prostsza wersja: Jeśli chcesz sprawdzić, czy obecna klasa instancji atrybut i jest wymagalne, po prostu to zrobić: if hasattr(self, "work") and callable(self.work). Sprawdza, czy instancja ma atrybut pracy (może to być zmienna lub funkcja), a następnie sprawdza, czy można ją wywołać (co oznacza, że ​​jest to funkcja).
Mitch McMabers
14

Możesz spróbować użyć modułu „inspect”:

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

is_method(dyn, 'mymethod')
vint83
źródło
12

Używam poniżej funkcji użytkowej. Działa na lambdzie, metodach klas oraz metodach instancji.

Metoda użytkowa

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

Przykładowe użycie

Zdefiniujmy klasę testową

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

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

Teraz możesz spróbować,

>>> 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                                                          
Shital Shah
źródło
2
Ta odpowiedź jest bardziej odpowiednia (przynajmniej moim zdaniem) niż inne odpowiedzi, ponieważ nie używa podejścia ogólnego (użycie tryinstrukcji) i sprawdza, gdy element członkowski jest rzeczywistą funkcją. świetny udział!
ymz
4

Co powiesz na to, żeby to sprawdzić dyn.__dict__?

try:
    method = dyn.__dict__['mymethod']
except KeyError:
    print "mymethod not in dyn"
deStrangis
źródło
Metody z prefiksem podkreślonym według konwencji oznacza „prywatne”.
xtofl
przedrostek podwójnego podkreślenia plus przyrostek podwójnego podkreślenia oznacza `` jest to normalnie używane przez sam interpreter Pythona '' i zazwyczaj istnieje sposób, aby osiągnąć ten sam efekt z programu użytkownika w większości przypadków użycia (w tym przypadku byłoby to używanie notacji kropkowej dla atrybutu), ale nie jest ani zabronione, ani niewłaściwe używanie go, jeśli Twój przypadek użycia naprawdę tego wymaga.
deStrangis
Nie jest to zabronione. Błąd jest subiektywny: zależy od tego, jak bardzo chcesz zmylić programistów przestrzegających konwencji: nie używaj go, chyba że nie masz alternatywy.
xtofl
3

Może w ten sposób, zakładając, że wszystkie metody można wywołać

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
źródło
1

Jeśli twoja metoda jest poza klasą i nie chcesz jej uruchamiać i zgłaszać wyjątek, jeśli nie istnieje:

'mymethod' in globals()

gerowam
źródło
1
>>> def myadd (x, y): return x + y >>> 'myadd' in globals () True
gerowam
-1

Myślę, że powinieneś spojrzeć na inspectopakowanie. Pozwala na „zawinięcie” niektórych rzeczy. Kiedy używasz dirmetody, wyświetla ona również listę wbudowanych metod, metod dziedziczonych i wszystkich innych atrybutów umożliwiających kolizje, np .:

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)

Tablica, z której otrzymujesz, dir(Two)zawiera zarówno f_onei, jak i f_twowiele wbudowanych elementów. Dzięki temu inspectmożesz to zrobić:

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

W tych przykładach nadal wymieniono obie metody z dwóch klas, ale jeśli chcesz ograniczyć inspekcję tylko do funkcji w określonej klasie, wymaga to nieco więcej pracy, ale jest to absolutnie możliwe.

aweis
źródło