为什么hasattr会执行@属性装饰器代码块

Why does hasattr execute the @property decorator code block

在 Python 中,当我在 @property 装饰器上调用 hasattr 时,hasattr 函数实际上运行 @property 代码块。

例如一个 class:

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 电话的情况下解决这个问题?

hasattr() 通过实际 检索属性 来工作;如果抛出异常 hasattr() returns False。这是因为这是了解属性是否存在的唯一可靠方法,因为有很多动态方法可以将属性注入 Python 对象(__getattr____getattribute__property对象、元 classes 等)。

来自hasattr() documentation:

This is implemented by calling getattr(object, name) and seeing whether it raises an exception or not.

如果您不想在执行此操作时调用 属性,则不要使用 hasattr。使用 vars()(returns 实例字典)或 dir()(也为您提供 class 上的名称列表)。

hasattr基本上是这样实现的(C中除外):

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

所以在真正的 "easier to ask for forgiveness than permission" (EAFP) 中,要找出对象是否具有给定的属性,Python 只是尝试获取该属性,并将失败转换为 return False 的值。由于是真正获取成功案例中的属性,hasattr()可以触发property等描述符的代码。

要在不触发描述符的情况下检查属性,您可以编写自己的 hasattr 遍历对象的方法解析顺序并检查名称是否在每个 class 的 __dict__(或 __slots__)。由于这不是属性访问,因此不会触发属性。

方便的是,Python 已经有一种方法可以遍历方法解析顺序并从实例的 classes 中收集属性名称: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)

请注意,如果我们在 dir() 中找不到所需的名称,我们将回退到使用 hasattr(),因为 dir() 不会找到动态属性(即,其中 __getattr__ 被覆盖)。当然,这些代码仍然会被触发,所以如果您不在乎找不到它们,您可以省略 or 子句。

总的来说,这是一种浪费,因为当我们只对某个特定的属性是否存在感兴趣时,它会获取所有相关的属性名称,但它会在紧要关头完成。我什至不确定自己做循环而不是调用 dir() 平均来说会更快,因为它将在 Python 而不是 C.

将变量设置为 class 变量,然后对其调用 hasattr 就成功了。

if not hasattr(google_plus_user, GooglePlusUser.call):
  self.response.out.write('Unknown call')
  return