Почему hasattr выполняет блок кода декоратора @property

В Python, когда я вызываю hasattr на @property декоратор hasattr функция на самом деле запускает @property кодовый блок.

Например

Класс:

class GooglePlusUser(object):

    def __init__(self, master):
        self.master = master

    def get_user_id(self):
        return self.master.google_plus_service.people().get(userId='me').execute()['id']


    @property
    def profile(self):
        # this runs with hasattr
        return self.master.google_plus_service.people().get(userId='me').execute()

Выполнение потокового кода вызывает свойство профиля и фактически вызывает:

#Check if the call is an attribute
if not hasattr(google_plus_user, call):
    self.response.out.write('Unknown call')
    return

Зачем? Как я могу решить эту проблему без вызова API?

3 ответа

Решение

hasattr() работает путем фактического получения атрибута; если выдается исключение hasattr() возвращается False, Это потому, что это единственный надежный способ узнать, существует ли атрибут, поскольку существует очень много динамических способов внедрения атрибутов в объекты Python (__getattr__, __getattribute__, property объекты, мета классы и т. д.).

От hasattr() документация:

Это осуществляется путем вызова getattr(object, name) и видеть, вызывает ли это исключение или нет.

Если вы не хотите, чтобы при этом вызывалось свойство, не используйте hasattr, использование vars() (который возвращает словарь экземпляра) или dir() (который также дает список имен в классе).

hasattr в основном реализовано так (кроме C):

def hasattr(obj, attrname):
    try:
        getattr(obj, attname)
    except AttributeError:
        return False
    return True

Таким образом, истинно, "проще просить прощения, чем разрешения" (EAFP), чтобы выяснить, имеет ли объект заданный атрибут, Python просто пытается получить атрибут и преобразует сбой в возвращаемое значение False, Так как это действительно получает атрибут в случае успеха, hasattr() может вызвать код для property и другие дескрипторы.

Чтобы проверить атрибут без запуска дескрипторов, вы можете написать свой собственный hasattr который пересекает порядок разрешения методов объекта и проверяет, есть ли имя в каждом классе __dict__ (или же __slots__). Поскольку это не доступ к атрибутам, он не будет вызывать свойства.

Удобно, что в Python уже есть способ обойти порядок разрешения методов и собрать имена атрибутов из классов экземпляра: dir(), Таким образом, простой способ написать такой метод:

# gingerly test whether an attribute exists, avoiding triggering descriptor code
def gentle_hasattr(obj, name):
    return name in dir(obj) or hasattr(obj, name)

Обратите внимание, что мы вернемся к использованию hasattr() если мы не можем найти нужное имя в dir(), так как dir() не найдет динамические атрибуты (т.е. где __getattr__ отменяется). Конечно, код для них будет по-прежнему активирован, поэтому, если вам все равно, что вы их не найдете, вы можете опустить or пункт.

В целом, это расточительно, поскольку он получает все соответствующие имена атрибутов, когда нас интересует только то, существует ли конкретный атрибут, но это будет сделано в крайнем случае. Я даже не уверен, что делать цикл самостоятельно, а не звонить dir() будет в среднем быстрее, так как это будет в Python, а не в C.

Создание переменной как переменной класса и последующий вызов hasattr помогли мне.

if not hasattr(google_plus_user, GooglePlusUser.call):
  self.response.out.write('Unknown call')
  return
Другие вопросы по тегам