Nie ma "najlepszego" sposobu,, ponieważ nigdy nie sprawdzasz, czy atrybut istnieje; zawsze jest częścią większego programu. Istnieje kilka poprawnych sposobów i jeden zauważalny niepoprawny sposób.
niewłaściwy sposób
if 'property' in a.__dict__:
a.property
Oto demonstracja, która pokazuje tę technikę niepowodzeniem:
class A(object):
@property
def prop(self):
return 3
a = A()
print "'prop' in a.__dict__ =", 'prop' in a.__dict__
print "hasattr(a, 'prop') =", hasattr(a, 'prop')
print "a.prop =", a.prop
wyjściowa:
'prop' in a.__dict__ = False
hasattr(a, 'prop') = True
a.prop = 3
Większość czasu, nie ma bałagan z __dict__
. Jest to specjalny atrybut do robienia specjalnych rzeczy, a sprawdzenie, czy dany atrybut istnieje, jest dość przyziemne.
EAFP sposób
Wspólne idiom w Pythonie jest „łatwiej prosić o przebaczenie niż pozwolenie”, lub EAFP za krótki. Zobaczysz wiele kodu Pythona, który używa tego idiomu, a nie tylko do sprawdzania istnienia atrybutów.
# Cached attribute
try:
big_object = self.big_object
# or getattr(self, 'big_object')
except AttributeError:
# Creating the Big Object takes five days
# and three hundred pounds of over-ripe melons.
big_object = CreateBigObject()
self.big_object = big_object
big_object.do_something()
Należy zauważyć, że jest to dokładnie ten sam idiom dla otwierania pliku, który może nie istnieć.
try:
f = open('some_file', 'r')
except IOError as ex:
if ex.errno != errno.ENOENT:
raise
# it doesn't exist
else:
# it does and it's open
Także do konwersji łańcuchów na liczby całkowite.
try:
i = int(s)
except ValueError:
print "Not an integer! Please try again."
sys.exit(1)
Nawet importowanie opcjonalnych modułów ...
try:
import readline
except ImportError:
pass
The LBYL sposób
Sposób hasattr
, oczywiście, działa też. Technika ta nosi nazwę "wygląd przed skokiem" lub LBYL w skrócie.
# Cached attribute
if not hasattr(self, 'big_object'):
big_object = CreateBigObject()
self.big_object = CreateBigObject()
big_object.do_something()
(hasattr
wbudowane faktycznie zachowuje się dziwnie w wersjach Pythona przed 3.2 w odniesieniu do wyjątków - to złapać wyjątki, że nie powinien on. - ale to chyba bez znaczenia, gdyż takie wyjątki są mało prawdopodobne Technika hasattr
jest również wolniejsza niż try/except
, ale nie nazywa się tego wystarczająco często, aby się tym przejmować, a różnica nie jest zbyt duża. Wreszcie, hasattr
nie jest atomowa, więc mógłby rzucić AttributeError
, jeśli inny wątek usuwa atrybut, ale jest to scenariusz jest bardzo naciągany, a poza tym musisz uważać na wątki. Nie uważam żadnej z tych trzech różnic za warte martwienia się.)
Używanie hasattr
jest znacznie prostsze niż try/except
, o ile tylko musisz wiedzieć, czy atrybut istnieje. Dla mnie dużym problemem jest to, że technika LBYL wygląda "dziwnie", ponieważ jako programista w Pythonie jestem bardziej przyzwyczajony do czytania techniki EAFP. Jeśli przerobisz powyższe przykłady, aby używać stylu LBYL
, otrzymasz kod, który jest albo niezgrabny, wręcz niepoprawny, albo zbyt trudny do napisania.
# Seems rather fragile...
if re.match('^(:?0|-?[1-9][0-9]*)$', s):
i = int(s)
else:
print "Not an integer! Please try again."
sys.exit(1)
I LBYL jest czasami wręcz błędna:
if os.path.isfile('some_file'):
# At this point, some other program could
# delete some_file...
f = open('some_file', 'r')
Jeśli chcesz napisać funkcję LBYL importowania opcjonalne moduły, Be My Guest ... to brzmi jak funkcja byłaby całkowita potwór .
getattr sposób
Jeśli wystarczy wartość domyślną, getattr
jest krótsza wersja try/except
.
x = getattr(self, 'x', default_value)
Jeśli wartość domyślna jest drogie skonstruować Pokochasz więc skończyć z czymś takim:
x = getattr(self, 'attr', None)
if x is None:
x = CreateDefaultValue()
self.attr = x
Lub jeśli None
jest możliwa wartość,
sentinel = object()
x = getattr(self, 'attr', sentinel)
if x is sentinel:
x = CreateDefaultValue()
self.attr = x
Wnioski
Wewnętrznie: wbudowane getattr
i hasattr
po prostu użyj techniki try/except
(z wyjątkiem napisanej w C). Tak więc wszyscy zachowują się w ten sam sposób, w którym to się liczy, a wybranie właściwego jest kwestią okoliczności i stylu.
Kod EAFP try/except
będzie zawsze pocierał niektórych programistów w niewłaściwy sposób, a kod LBYL hasattr/getattr
będzie irytować innych programistów. Oba są poprawne i często nie ma naprawdę nieodpartego powodu, aby wybrać jedną lub drugą. (Jeszcze inni programiści są zdegustowani, które można uznać za normalne atrybutem jako niezdefiniowany, a niektórzy programiści są przerażeni, że to w ogóle możliwe, aby mieć niezdefiniowany atrybut w Pythonie.)
Twoja druga opcja jest źle, i tak [drugi odpowiedź] (http://stackoverflow.com/a/610923/1132524) z linku, który podałeś odpowiedź na swoje pytanie. –