Jak się dowiedzieć, czy obiekt ma atrybut w Pythonie

1631

Czy w Pythonie istnieje sposób na określenie, czy obiekt ma jakiś atrybut? Na przykład:

>>> a = SomeClass()
>>> a.someProperty = value
>>> a.property
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: SomeClass instance has no attribute 'property'

Jak rozpoznać, czy ama atrybut propertyprzed użyciem?

Lucas Gabriel Sánchez
źródło

Odpowiedzi:

2335

Spróbuj hasattr():

if hasattr(a, 'property'):
    a.property

EDYCJA: Zobacz odpowiedź zweiterlinde poniżej, która oferuje dobre porady dotyczące proszenia o wybaczenie! Bardzo pytonowe podejście!

Ogólna praktyka w Pythonie polega na tym, że jeśli właściwość prawdopodobnie będzie tam przez większość czasu, po prostu wywołaj ją i pozwól, aby wyjątek się rozprzestrzenił, lub zatrzymaj go za pomocą bloku try / wyjątek. Będzie to prawdopodobnie szybsze niż hasattr. Jeśli właściwość prawdopodobnie nie będzie tam przez większość czasu lub nie masz pewności, użycie hasattrbędzie prawdopodobnie szybsze niż wielokrotne wpadanie w blok wyjątków.

Jarret Hardie
źródło
19
Wydaje się, że działa również w celu sprawdzania funkcji w przestrzeni nazw, np .: import string hasattr(string, "lower")
riwiera
15
hasattrjest dokładnie taki sam, jak użycie try/ except AttributeError: dokumentacja hasattr (w Pythonie 2.7) mówi, że używa wyjątków od chwytania rąk getattr.
Jeff Tratner
8
@JeffTratner: hasattrniestety nie jest dokładnie taka sama jak try: ... except AttributeError:w Pythonie 2.x ponieważ hasattrbędzie złapać wszystkie wyjątek . Proszę zobaczyć moją odpowiedź na przykład i proste obejście.
Martin Geisler
1
@MartinGeisler Dobra uwaga - to nie to samo, że wychwytuje wszystkie wyjątki. Nie jestem pewien, która wersja jest bardziej poprawna - naprawdę zależy od założeń, nad którymi pracujesz i kto wywołuje twoją funkcję. Dzięki za wytłumaczenie.
Jeff Tratner,
2
hasattrjest również najlepszym wyborem, jeśli rozumiesz, np.[a.property for a in list_of_as if hasattr(a, "property")]
Rafael Martins
631

Jak odpowiedział Jarret Hardie, załatwi sprawęhasattr . Chciałbym jednak dodać, że wielu członków społeczności Python zaleca strategię „łatwiej prosić o wybaczenie niż o pozwolenie” (EAFP) niż „patrzeć, zanim skoczysz” (LBYL). Zobacz te referencje:

EAFP vs LBYL (jak do tej pory był trochę rozczarowany)
EAFP vs. LBYL @Code Like a Pythonista: Idiomatic Python

to znaczy:

try:
    doStuff(a.property)
except AttributeError:
    otherStuff()

... jest preferowany do:

if hasattr(a, 'property'):
    doStuff(a.property)
else:
    otherStuff()
zweiterlinde
źródło
257
Ale jak sprawdzisz, czy to a.property spowodowało AttributeError, a nie coś w doStuff ()? Wygląda na to, że nie. Myślę, że naprawdę łatwiej jest prosić o wybaczenie, ale wiele razy jest to również nieprawidłowe.
jpalecek
282
EAFP wydaje się ... szalony. HasAttr telegrafuje do przyszłych programistów serwisowych, którzy sprawdzają konkretny atrybut. Otrzymanie wyjątku nic nie mówi przyszłym programistom i może poprowadzić kogoś do króliczej nory.
Ethan Heilman,
74
@ e5: masz rację w tym przypadku, ale w wielu przypadkach EAFP jest jedyną prawidłową opcją. Na przykład, jeśli sprawdzisz istnienie pliku, a następnie go otworzysz, oczekując, że na pewno będzie istniał, kod jest niepoprawny: plik może zostać usunięty lub jego nazwa zostanie zmieniona między sprawdzeniem a użyciem. Nazywa się to błędem TOCTOU (Time-Of-Check-To-Time-of-Use) i oprócz powodowania awarii może być również źródłem luk w zabezpieczeniach.
Max
15
@EthanHeilman jest szalony tylko wtedy, gdy istnieje źródło niejednoznaczności w źródle wyjątku, którego w większości przypadków można uniknąć dzięki dobrym projektom. Dobrze ułożona struktura logiki w ramach try / wyjątkiem / wreszcie generalnie zapewnia bardziej niezawodną (mniej podatną na błędy programisty) logikę niż zaśmiecanie kodu za pomocą zapobiegawczych sprawdzeń if dla każdego fragmentu kodu. Sprawia również, że błędy są bardzo wyraźne i pozwala konsumującym programistom na bezpośrednią odpowiedź na nie.
Peter M. Elias
64
Większość skarg na niejasności wynika po prostu z tego, że przykładowy kod ma słabą strukturę. Jedyną rzeczą w środku try:powinna być próba dostępu do atrybutu; nie ma też powodu, aby zawijać wykonanie doStuff. Nadal istnieje jednak pewien potencjał niejednoznaczności: jeśli propertyjest obliczoną właściwością zamiast zwykłego atrybutu, jej implementacja może wzrosnąć AttributeErrorwewnętrznie. Dlatego w prawie wszystkich rzeczywistych sytuacjach takich jak ta getattrjest lepsza niż hasattrzłapanie lub złapanie AttributeError.
Carl Meyer
483

Możesz użyć hasattr()lub złapać AttributeError, ale jeśli naprawdę chcesz tylko wartość atrybutu z wartością domyślną, jeśli go nie ma, najlepszą opcją jest użycie getattr():

getattr(a, 'property', 'default value')
Carl Meyer
źródło
14
To rozwiązuje oba wyżej wymienione problemy: a) Dwuznaczność źródła możliwego błędu AttributeEr, b) Zachowanie podejścia EAFP.
Peter M. Elias
6
To także 25% linii kodu. To z pewnością musi być najlepsze rozwiązanie.
fatuhoku
16
To najlepsze rozwiązanie „jeśli naprawdę chcesz tylko wartość atrybutu z wartością domyślną”. Chociaż uważam, że tak właśnie chce wielu ludzi, którzy twierdzą, że chcą wykryć obecność atrybutu, OP faktycznie poprosił o to drugie, więc rozsądne jest, aby bezpośrednie odpowiedzi na to pytanie (hasattr, AttributeError) były wymienione wyżej .
Carl Meyer,
41

Myślę, że to, czego szukasz, to hasattr . Polecam jednak coś takiego, jeśli chcesz wykryć właściwości Pythona -

try:
    getattr(someObject, 'someProperty')         
except AttributeError:
    print "Doesn't exist"
else
    print "Exists"

Wadą jest to, że __get__wychwytywane są również błędy atrybutów w kodzie właściwości .

W przeciwnym razie zrób-

if hasattr(someObject, 'someProp'):
    #Access someProp/ set someProp
    pass

Dokumenty: http://docs.python.org/library/functions.html
Ostrzeżenie:
Powodem mojej rekomendacji jest to, że hasattr nie wykrywa właściwości.
Link: http://mail.python.org/pipermail/python-dev/2005-December/058498.html

batbrat
źródło
więc twoja rada nie jest wbudowana - zaimplementuj ją!
SilentGhost,
Nie do końca, nie używaj wbudowanego IFF, który chcesz wykryć właściwości. W przeciwnym razie hasattr jest całkowicie dobry.
batbrat
3
hasattrogólnie dobrze wykrywa właściwości. Po prostu traktuje zgłoszenie wyjątku w propertyfunkcji -wrapped jako oznaczające, że taki atrybut nie istnieje; połączony post na liście mailingowej Pythona dotyczy właściwości, która podnosi wyjątek podczas próby uzyskania do niego dostępu. Dla wszystkich praktycznych celów wspomniany atrybut nie istnieje, ponieważ nigdy nie wygeneruje wartości. Ponadto, hasattrtylko ogólnie tłumi wyjątki dotyczące Py 3.1 i wcześniejszych; w wersji 3.2+ tłumi tylko (zastępuje przez Falsereturn) AttributeError.
ShadowRanger
32

Według pydoc, hasattr (obj, prop) po prostu wywołuje getattr (obj, prop) i wyłapuje wyjątki. Tak więc równie ważne jest zawinięcie dostępu do atrybutu instrukcją try i wyłapanie AttributeError, tak jak wcześniej należy użyć hasattr ().

a = SomeClass()
try:
    return a.fake_prop
except AttributeError:
    return default_value
Jordan Lewis
źródło
2
Dobrze hasattr może być zoptymalizowany. Np. Z pypy.
odinho
6
+1. Jest to nawet bezpieczniejsze niż używanie, hasattrgdy SomeClasszastąpienia, __getattr__ponieważ hasattrwychwytują wszystkie wyjątki w Pythonie 2.x, nie tylko AttributeErrortak, jak można się spodziewać. Zostało to naprawione w Pythonie 3.2 - proszę zobaczyć moją inną odpowiedź na proste obejście.
Martin Geisler,
24

Chciałbym zasugerować, aby tego uniknąć:

try:
    doStuff(a.property)
except AttributeError:
    otherStuff()

Wspomniał o tym użytkownik @jpalecek: Jeśli coś AttributeErrorwystąpi w środku doStuff(), jesteś zgubiony.

Może to podejście jest lepsze:

try:
    val = a.property
except AttributeError:
    otherStuff()
else:
    doStuff(val)
Maico
źródło
może też być po prostu: try: a.propertynie potrzeba lewej strony
Petruza
Następnie musisz powtórzyć właściwość a. W bloku else, co może się nie powieść.
Éric Araujo
13

W zależności od sytuacji możesz sprawdzić, isinstancejakiego rodzaju masz obiekt, a następnie użyć odpowiednich atrybutów. Wraz z wprowadzeniem abstrakcyjnych klas bazowych w Pythonie 2.6 / 3.0 to podejście stało się również znacznie bardziej wydajne (w zasadzie ABC pozwala na bardziej wyrafinowany sposób pisania kaczych znaków).

Jedna sytuacja, w której byłoby to przydatne, miałaby miejsce, gdyby dwa różne obiekty miały atrybut o tej samej nazwie, ale o innym znaczeniu. Używanie tylko hasattrmoże prowadzić do dziwnych błędów.

Dobrym przykładem jest rozróżnienie między iteratorami a iteratorami (patrz to pytanie). Te __iter__metody iteratora i iterable mieć taką samą nazwę, ale są semantycznie zupełnie inaczej! hasattrJest to więc bezużyteczne, ale isinstancewraz z ABC zapewnia czyste rozwiązanie.

Zgadzam się jednak, że w większości sytuacji hasattrpodejście (opisane w innych odpowiedziach) jest najbardziej odpowiednim rozwiązaniem.

nikow
źródło
13

Mam nadzieję, że oczekujesz hasattr (), ale staraj się unikać hasattr () i preferuj getattr (). getattr () jest szybszy niż hasattr ()

za pomocą hasattr ():

 if hasattr(a, 'property'):
     print a.property

to samo tutaj używam getattr, aby uzyskać właściwość, jeśli nie ma właściwości, nie zwraca żadnych

   property = getattr(a,"property",None)
    if property:
        print property
Janarthanan Ramu
źródło
11

EDYCJA : To podejście ma poważne ograniczenia. Powinien działać, jeśli obiekt jest iterowalny . Sprawdź komentarze poniżej.

Jeśli używasz Pythona w wersji 3.6 lub wyższej, tak jak ja, istnieje wygodna alternatywa, aby sprawdzić, czy obiekt ma określony atrybut:

if 'attr1' in obj1:
    print("attr1 = {}".format(obj1["attr1"]))

Nie jestem jednak pewien, które z nich jest obecnie najlepsze. za pomocą hasattr(), za pomocą getattr()lub za pomocą in. Komentarze są mile widziane.

nayak
źródło
6
insłowo kluczowe działa w celu sprawdzenia typów iterowalnych. Na przykład 'foo' in Nonezgłasza błąd TypeError: argument of type 'NoneType' is not iterable. Rozwiązaniem jest sprawdzenie, czy typ jest iterowalny przed użyciem in. Po skorygowaniu przypadków krawędzi, takich jak typ nie iterowalny, prawdopodobnie lepiej jest użyć, hasattr()ponieważ jest zaprojektowany do obsługi przypadków krawędzi.
Seth Difley,
3
Nie ma to nic wspólnego z dostępem do atrybutów. Nie mam pojęcia, jak to się zyskało. Jedynym podobieństwem, jakie ma do przypisania dostępu, jest to, że używasz go dictjako „lekkiego obiektu”, podobnego do projektowania obiektów JavaScript, ale większość normalnych klas nie obsługuje tego ogólnie (uzyskanie wariantu błędu wspomnianego przez @SethDifley) .
ShadowRanger
2
jeśli „zmienna” w klasie .__ dict__: ??
Ben
5

Oto bardzo intuicyjne podejście:

if 'property' in dir(a):
    a.property
Alec Alameddine
źródło
1

Możesz sprawdzić, czy objectzawiera atrybut, używając hasattrwbudowanej metody.

Na przykład, jeśli twój obiekt jest ai chcesz sprawdzić atrybutstuff

>>> class a:
...     stuff = "something"
... 
>>> hasattr(a,'stuff')
True
>>> hasattr(a,'other_stuff')
False

Sama sygnatura metody hasattr(object, name) -> booloznacza, że objectma atrybut, który jest przekazywany do drugiego argumentu, hasattrniż daje wartość logiczną Truelub Falsezależnie od obecności nameatrybutu w obiekcie.

Devang Padhiyar
źródło
1

Jest to bardzo proste, wystarczy użyć dir(obiektu)
Zwróci listę wszystkich dostępnych funkcji i atrybutów obiektu.

Sean Christianians
źródło
1

Inna możliwa opcja, ale zależy to od tego, co masz na myśli wcześniej :

undefined = object()

class Widget:

    def __init__(self):
        self.bar = 1

    def zoom(self):
        print("zoom!")

a = Widget()

bar = getattr(a, "bar", undefined)
if bar is not undefined:
    print("bar:%s" % (bar))

foo = getattr(a, "foo", undefined)
if foo is not undefined:
    print("foo:%s" % (foo))

zoom = getattr(a, "zoom", undefined)
if zoom is not undefined:
    zoom()

wynik:

bar:1
zoom!

Pozwala to nawet sprawdzić atrybuty Bez wartości.

Ale! Uważaj, aby przypadkowo nie utworzyć instancji i nie porównać undefinedwielu miejsc, ponieważ isw takim przypadku nigdy nie zadziała.

Aktualizacja:

z powodu tego, o czym ostrzegałem w powyższym akapicie, mając wiele niezdefiniowanych, które nigdy nie pasują, ostatnio nieznacznie zmodyfikowałem ten wzorzec:

undefined = NotImplemented

NotImplemented, nie należy mylić NotImplementedError, jest wbudowany: jest częściowo zgodny z intencją JS undefinedi możesz ponownie użyć jego definicji wszędzie i zawsze będzie pasować. Wadą jest to, że jest „prawdziwa” w logach i może wyglądać dziwnie w logach i śladach na stosie (ale szybko się z tego obchodzisz, gdy wiesz, że pojawia się tylko w tym kontekście).

JL Peyret
źródło
0

hasattr()to właściwa odpowiedź. Chcę dodać, że hasattr()można go również dobrze używać w połączeniu z aser (aby uniknąć niepotrzebnych ifinstrukcji i uczynić kod bardziej czytelnym):

assert hasattr(a, 'property'), 'object lacks property' 

Jak stwierdzono w innej odpowiedzi dotyczącej SO : Asertów należy używać do testowania warunków, które nigdy nie powinny się zdarzyć. Celem jest wczesne zawieszenie się w przypadku uszkodzonego stanu programu.

FMF
źródło
Jest to pomocne, ale nie zawsze tak jest. Być może chciałem sprawdzić, czy obiekt ma właściwość, a następnie dodać go po raz pierwszy, a może muszę uruchomić inny kod dla obiektów z tą właściwością. Jeśli kod nie może kontynuować, potwierdzenie może być w porządku. Ale znowu nie zawsze tak jest. Ważne jest użycie hasattrnie otaczającego kodu.
Lucas Gabriel Sánchez