Который является лучшим способом проверить на существование атрибута? [дубликат]

Этот вопрос уже имеет ответ здесь:

Который является лучшим способом проверить на существование атрибута?

Jarret Hardie предоставил этот ответ:

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

Я вижу, что это может также быть сделано этот путь:

if 'property' in a.__dict__:
    a.property

Один подход обычно используется больше, чем другие?

62
задан 23 May 2017 в 14:54

4 ответа

нет никакого "лучшего" пути, , потому что Вы просто никогда не проверяете, чтобы видеть, существует ли атрибут; это всегда - часть некоторой большей программы. Существует несколько корректных путей и один известный неправильный путь.

неправильным путем

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

Большую часть времени, Вы не хотите смешивать с [1 113]. Это - специальный атрибут для того, чтобы сделать специальные вещи и проверить, чтобы видеть, существует ли атрибут, является довольно приземленным.

путь 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's 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 техника также медленнее, чем [1 117], но Вы не называете ее достаточно часто для заботы, и разница не является очень большой. Наконец, hasattr не является атомарным, таким образом, это могло бросить AttributeError, если другой поток удаляет атрибут, но это - неправдоподобный сценарий, и необходимо будет быть очень осторожны относительно потоков так или иначе. Я не полагаю, что любое из этих трех различий стоит волнения по поводу.)

Используя [1 120] намного более просто, чем [1 121], пока все, что необходимо знать, существует ли атрибут. Большая проблема для меня - то, что техника LBYL выглядит "странной", с тех пор как Python программист я больше привык к чтению техники EAFP. При перезаписи вышеупомянутых примеров так, чтобы они использовали эти LBYL стиль, Вы получаете код, который является или неуклюжим, напрямую неправильным, или слишком трудным для записи.

# Seems rather fragile...
if re.match('^(:?0|-?[1-9][0-9]*) 

И 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, является более короткая версия [1 124].

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, builtins просто используют try/except техника (кроме записанного в C). Таким образом, они все ведут себя тот же путь, где он рассчитывает, и выбор правильного происходит из-за вопроса обстоятельств и стиля.

try/except код EAFP будет всегда протирать некоторых программистов неправильный путь, и hasattr/getattr, код LBYL будет раздражать других программистов. Они и корректны, и часто нет никакого действительно неопровержимого довода для выбора один или другой. (Все же другие программисты чувствуют отвращение, что Вы считали бы это нормальным, чтобы атрибут был не определен, и некоторые программисты испуганы, что даже возможно иметь неопределенный атрибут в Python.)

, 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, является более короткая версия [1 124].

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, builtins просто используют try/except техника (кроме записанного в C). Таким образом, они все ведут себя тот же путь, где он рассчитывает, и выбор правильного происходит из-за вопроса обстоятельств и стиля.

try/except код EAFP будет всегда протирать некоторых программистов неправильный путь, и hasattr/getattr, код LBYL будет раздражать других программистов. Они и корректны, и часто нет никакого действительно неопровержимого довода для выбора один или другой. (Все же другие программисты чувствуют отвращение, что Вы считали бы это нормальным, чтобы атрибут был не определен, и некоторые программисты испуганы, что даже возможно иметь неопределенный атрибут в Python.)

140
ответ дан 31 October 2019 в 13:28

hasattr() путь <глоток> * .

a.__dict__ ужасно, и это не работает во многих случаях. hasattr() на самом деле попытки получить атрибут и выгоды AttributeError внутренне, таким образом, это работает, даже если Вы определяете пользовательский __getattr__() метод.

, Чтобы постараться не запрашивать атрибут дважды третий аргумент в пользу getattr() мог использоваться:

not_exist = object()

# ...
attr = getattr(obj, 'attr', not_exist)
if attr is not_exist:
   do_something_else()
else:
   do_something(attr)

Вы могли просто использовать значение по умолчанию вместо not_exist сигнальная метка, если это является более соответствующим в Вашем случае.

мне не нравится try: do_something(x.attr) \n except AttributeError: .., это могло бы скрыться AttributeError внутренний do_something() функция.

<глоток> * , Прежде чем Python 3.1 hasattr() подавил все исключения (не только [1 112]), если это не желательно getattr(), должен использоваться.

11
ответ дан 31 October 2019 в 13:28

hasattr() Pythonic способ сделать это. Изучите это, любите его.

Другой возможный путь состоит в том, чтобы проверить, является ли имя переменной в locals() или globals():

if varName in locals() or in globals():
    do_something()
else:
    do_something_else()

я лично очень не хочу поймать исключения для проверки чего-то. Это ужасные стили. Это идентично проверке, если строка содержит только цифры тот путь:

s = "84984x"
try:
    int(s)
    do_something(s)
except ValueError:
    do_something_else(s)

Вместо того, чтобы мягко использовать s.isdigit(). Eww.

5
ответ дан 31 October 2019 в 13:28

Очень старый вопрос, но этому действительно нужен хороший ответ. Даже для короткой программы я сказал бы, используют пользовательскую функцию!

Вот пример. Это не идеально подходит для всего приложения, но это для моего для парсинга ответов от бесчисленных API и использования Django. Легко зафиксировать для общих собственных требований.

from django.core.exceptions import ObjectDoesNotExist
from functools import reduce

class MultipleObjectsReturned(Exception):
    pass

def get_attr(obj, attr, default, asString=False, silent=True):
    """
    Gets any attribute of obj.
    Recursively get attributes by separating attribute names with the .-character.        
    Calls the last attribute if it's a function.

    Usage: get_attr(obj, 'x.y.z', None)
    """
    try:
        attr = reduce(getattr, attr.split("."), obj)
        if hasattr(attr, '__call__'):
            attr = attr()
        if attr is None:
            return default
        if isinstance(attr, list):
            if len(attr) > 1:
                logger.debug("Found multiple attributes: " + str(attr))
                raise MultipleObjectsReturned("Expected a single attribute")
            else:
                return str(attr[0]) if asString else attr[0]
        else:
            return str(attr) if asString else attr
    except AttributeError:
        if not silent:
            raise
        return default
    except ObjectDoesNotExist:
        if not silent:
            raise
        return default
0
ответ дан 31 October 2019 в 13:28

Другие вопросы по тегам:

Похожие вопросы: