【问题标题】:What's the difference between hasattr() and 'attribute' in dir()?dir() 中的 hasattr() 和 'attribute' 有什么区别?
【发布时间】:2013-07-18 12:34:11
【问题描述】:

例如:

>>> s = 'string'
>>> hasattr(s, 'join')
True
>>> 'join' in dir(s)
True

Python documentation 表示 hasattr 是通过调用 getattr 来实现的,并查看它是否引发异常。但是,这会导致很大的开销,因为获得的值会被丢弃,并且可能会引发异常。

问题是,如果调用'attribute' in dir(obj) 意味着同样的事情,它是否更快、更安全,还是可能在特定场合失败?

【问题讨论】:

    标签: python


    【解决方案1】:

    这不完全一样。 dir() 是一种诊断工具,省略 getattr()hasattr() 会找到的属性。

    来自dir() documentation

    默认的dir() 机制对不同类型的对象表现不同,因为它试图生成最相关的,而不是完整的信息:

    • 如果对象是模块对象,则列表包含模块属性的名称。
    • 如果对象是类型或类对象,则列表包含其属性的名称,并递归地包含其基类的属性。
    • 否则,该列表包含对象的属性名称、其类属性的名称,以及其类的基类属性的递归名称。

    注意:因为提供dir()主要是为了方便在交互式提示中使用,它试图提供一个有趣的 一组名称,而不是试图提供严格或一致的名称 定义的名称集,其详细行为可能会随着 发布。例如,metaclass 属性不在结果中 当参数是一个类时列出。

    强调我的。

    这意味着hasattr() 会找到提供的元类 属性,但dir() 不会,并且发现的内容可能因Python 版本的不同而不同,因为该函数的定义是为了提供调试方便,不完整。

    具体元类场景的演示,hasattr()找到元类定义的属性:

    >>> class Meta(type):
    ...     foo = 'bar'
    ... 
    >>> class Foo(metaclass=Meta):
    ...     pass
    ... 
    >>> hasattr(Foo, 'foo')
    True
    >>> 'foo' in dir(Foo)
    False
    

    最后但并非最不重要的一点:

    如果对象有一个名为__dir__()的方法,这个方法将被调用并且必须返回属性列表。

    这意味着如果已实现 .__dir__() 方法,hasattr()dir() 在“找到”哪些属性方面的差异会更大。

    只要坚持hasattr()。一方面,它更快,因为测试属性很便宜,因为这只是针对一个或多个字典的成员资格测试。另一方面,枚举所有字典键并跨实例、类和基类合并它们的 CPU 成本要高得多。

    【讨论】:

    • 使用hasattr 的一个缺点是它会触发属性执行,这可能会很昂贵。在诸如 Django OneToOneField 关系之类的情况下,这甚至会触发数据库查询。我不认为有其他选择,是吗?
    • @AugustoMen:您可以使用特定于应用程序的 方法来测试存在的属性。例如,我确信 Django 有一种枚举字段的方法。
    【解决方案2】:

    hasattr 快 100 倍以上 :)

    In [137]: s ='string'
    
    In [138]: %timeit hasattr(s, 'join')
    10000000 loops, best of 3: 157 ns per loop
    
    In [139]: %timeit 'join' in dir(s)
    100000 loops, best of 3: 19.3 us per loop
    

    【讨论】:

      【解决方案3】:

      dir() 不调用getattr() 或类似的东西,它取决于类来“描述”自己:

      >>> class Foo(object):
      ...     def __dir__(self):
      ...         return ['apples', 'bananas', 'mangoes']
      ...     def __getattr__(self, attr):
      ...         return {'a': 1}[attr]
      ...     
      >>> foo = Foo()
      >>> hasattr(foo, 'a')
      True
      >>> hasattr(foo, 'apples')
      False
      >>> 'a' in dir(foo)
      False
      >>> 'apples' in dir(foo)
      True
      

      您只应在查找文档时使用dir()

      【讨论】:

        【解决方案4】:

        hasattr() 基本上是

        try:
            s.attribute
            return True
        except AttributeError:
            return False
        

        而“目录中的属性”更像:

        for attr in dir(s):
            if attribute == attr:
                return True
        return False
        

        因此,预计 hasattr 会更快一些。

        无论如何,如果允许我偏离轨道,那么我会建议这样做。 如果你想做类似的事情:

        if hasattr(s, 'attributeName'):
            s.attributeName()
        else:
            do_stuff()
        

        那么建议这样做:

        try:
            s.attributeName()
        except AttributeError:
            do_stuff()
        

        为什么?

        1. 为了避免额外的 try-except 块/for 循环的开销。
        2. 在 python 中,请求宽恕比请求许可更容易。

        【讨论】:

          猜你喜欢
          • 2015-01-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-12-29
          • 2015-04-11
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多