Ответ 1
Нет никакого "лучшего" способа,, потому что вы никогда не проверяете, существует ли атрибут; он всегда является частью некоторой более крупной программы. Существует несколько правильных способов и один неправильный способ.
Неверный путь
if 'property' in a.__dict__:
a.property
Вот демонстрация, которая показывает, что этот метод не работает:
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
Вывод:
'prop' in a.__dict__ = False hasattr(a, 'prop') = True a.prop = 3
В большинстве случаев вы не хотите возиться с __dict__
. Это специальный атрибут для выполнения особых вещей, и проверка того, существует ли атрибут, является довольно мирской.
Способ EAFP
Общей идиомой в Python является "проще попросить прощения, чем разрешение", или краткое описание EAFP. Вы увидите много кода Python, который использует эту идиому, а не только для проверки существования атрибута.
# 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()
Обратите внимание, что это точно такая же идиома для открытия файла, который может не существовать.
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 open
Кроме того, для преобразования строк в целые числа.
try:
i = int(s)
except ValueError:
print "Not an integer! Please try again."
sys.exit(1)
Даже импортирование дополнительных модулей...
try:
import readline
except ImportError:
pass
Способ LBYL
Конечно, метод hasattr
тоже работает. Этот метод называется "смотреть, прежде чем прыгать", или LBYL для краткости.
# Cached attribute
if not hasattr(self, 'big_object'):
big_object = CreateBigObject()
self.big_object = CreateBigObject()
big_object.do_something()
(Встроенный встроенный hasattr
ведет себя странно в версиях Python до 3.2 в отношении исключений - он будет ловить исключения, которых он не должен, но это, вероятно, не имеет значения, поскольку такие исключения маловероятны. hasattr
метод также медленнее, чем try/except
, но вы не называете его достаточно часто, чтобы заботиться, а разница не очень велика. Наконец, hasattr
не является атомарным, поэтому он может бросать AttributeError
, если другой поток удаляет атрибут, но это очень надуманный сценарий, и в любом случае вам нужно быть очень осторожным в отношении потоков. Я не считаю, что какое-либо из этих трех различий стоит беспокоиться.)
Использование hasattr
намного проще, чем try/except
, если все, что вам нужно знать, - это атрибут существует. Большая проблема для меня в том, что техника LBYL выглядит "странно", так как в качестве программиста на Python я больше привык читать технику EAFP. Если вы переписываете приведенные выше примеры, чтобы использовать стиль LBYL
, вы получаете код, который является либо неуклюжим, абсолютно неправильным, либо слишком сложным для записи.
# 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)
И LBYL иногда совершенно неверен:
if os.path.isfile('some_file'):
# At this point, some other program could
# delete some_file...
f = open('some_file', 'r')
Если вы хотите написать функцию LBYL для импорта дополнительных модулей, будьте моим гостем... похоже, что функция будет полным монстром.
Способ getattr
Если вам просто нужно значение по умолчанию, getattr
является более короткой версией try/except
.
x = getattr(self, 'x', default_value)
Если значение по умолчанию стоит дорого, тогда вы получите что-то вроде этого:
x = getattr(self, 'attr', None)
if x is None:
x = CreateDefaultValue()
self.attr = x
Или, если None
- возможное значение,
sentinel = object()
x = getattr(self, 'attr', sentinel)
if x is sentinel:
x = CreateDefaultValue()
self.attr = x
Заключение
Внутренне встроенные встроенные функции getattr
и hasattr
используют технику try/except
(кроме записи на C). Таким образом, все они ведут себя так же, как и считают, и выбор правильного объясняется обстоятельствами и стилем.
Код EAFP try/except
всегда будет неправильно протирать некоторых программистов, а код hasattr/getattr
LBYL будет раздражать других программистов. Они оба правильны, и часто нет поистине убедительных оснований для выбора того или другого. (Тем не менее, другим программистам отвратительно, что вы считаете нормальным для атрибута undefined, а некоторые программисты в ужасе, что даже возможно иметь атрибут undefined в Python.)