Почему 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