如何稳健地检查Python属性是否存在?

6

给定以下类(具有错误的属性),那么检查bar属性存在的最佳可靠方式是什么?

class Foo(object):
    @property
    def bar(self):
        raise AttributeError('unforeseen attribute error!')

hasattrgetattr都失败了,只有dir起作用:

foo = Foo()

print hasattr(foo, 'bar')
# False

try:
    getattr(foo, 'bar')
    print True
except AttributeError as e:
    print False
# False    

print 'bar' in dir(foo)
# True

我能想到的最全面的解决方案是:

def robust_hasattr(obj, attr):
    return hasattr(obj, attr) or attr in dir(obj)

有更好的方法吗?


为什么需要检查它?假设它已经存在并处理错误,如果不存在,则使用tryexcept进行恢复,或者让其传播,如果无法恢复。 - jonrsharpe
3
好的,您需要将以下内容翻译为中文: "Well, your property does raise AttributeError. It is firmly trying to signal that the attribute is not there. What is your specific usecase that you must detect that this is a property?"您的属性确实会引发AttributeError。它坚定地试图表明该属性不存在。您必须检测到这是一个属性的特定用例是什么? - Martijn Pieters
还可以参考hasattr()和dir()中的'attribute'有什么区别?。使用dir()可能不够。 - Martijn Pieters
实际使用情况更为复杂 - 我正在对一个与硬件通信并可能存在意外错误(包括属性错误)的类进行子类化。在子类中,我只想检查属性名称是否存在(出于接口原因),而不一定要执行它们。 hasattr 的问题在于它会“失败”,从而隐藏了潜在复杂父类中的任何错误(不仅仅是 AttributeError),这使得调试实际错误变得困难。 - 101
2个回答

10
如果您有一个有缺陷的属性,请修复该缺陷。如果引发AttributeError是一个错误,那么使该属性不会这样做。引发该异常是表示您不应使用该属性的方法。
使用dir()可能是一种解决方法,但它并不是万无一失的,因为dir()是一种调试工具,既可以省略信息,也可以被object.__dir__挂钩覆盖(给您的代码另一个引入漏洞的途径)。然后,还有可能出现有缺陷的object.__getattr__挂钩有缺陷的object.__getattribute__挂钩,甚至是元类上的描述符,所有这些都不能通过使用dir()来检测到。

由于您特别寻找一个属性,因此请在对象的类上查找相同的属性:

hasattr(foo, 'bar') or isinstance(getattr(type(foo), 'bar', None), property)

对于您的特定情况,以上返回True:
>>> class Foo(object):
...     @property
...     def bar(self):
...         raise AttributeError('unforeseen attribute error!')
...
>>> foo = Foo()
>>> hasattr(foo, 'bar') or isinstance(getattr(type(foo), 'bar', None), property)
True

因为类上确实有这样一个属性对象。

谢谢,我没有想到要检查类本身。就像我在上面所说的,在我的用例中,我只想检查命名空间而不是执行代码,所以这是一个不错的解决方案。 - 101

3
根据Python规则,bar属性不存在。只有当尝试访问属性时不引发异常时,才认为对象具有该属性。
如果您想使用其他属性存在的概念,可以自己实现。例如,要检查实例字典或一个类字典中是否存在与bar相对应的条目:
for obj in (foo,) + type(foo).__mro__:
    if 'bar' in obj.__dict__:
        print "It's there."
        break
else:
    print "Didn't find it."

1
@MartijnPieters:因为我在考虑其他描述符类型,它们也可能会为该类引发AttributeError。这对于property来说并不是必要的。 - user2357112
1
@MartijnPieters:但是在元类上的描述符不会被考虑为实例属性访问,所以我们大多数情况下不需要担心它们。不过,我们可能需要处理__dict____mro__的自定义描述符。 - user2357112
有趣的方法 :) - 101
1
@Maggyero:如果你想检查一个对象是否是数据描述符,则应该跳过实例字典,即使存在实例字典也是如此。在实例字典中的__set____delete__被描述符协议忽略,就像大多数情况下Python内部调用魔术方法一样。 - user2357112
1
对于覆盖__getattribute__的其他类型,这个答案同样可能被认为不起作用。例如,如果您将其与super对象一起使用,它将不执行代理MRO搜索super对象执行的操作 - 此外,正如我们在2019年讨论过的那样,它假定对象具有实例字典。 - user2357112
显示剩余12条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接