有人能解释一下Python中的hasattr/delattr在方法属性上的作用吗?

3 投票
2 回答
5174 浏览
提问于 2025-04-16 23:01

我遇到了一个奇怪的问题,可能是我对函数属性的理解有些偏差,或者这可能是个bug。我正在使用

$ python
Python 2.7.1+ (r271:86832, Apr 11 2011, 18:13:53) 
[GCC 4.5.2] on linux2

首先,我定义了一个类,并给它的一个方法添加了一个属性。

>>> class Foo(object):
...    def bar(self):
...        pass
...    bar.xyzzy = 'magic'
...

现在我可以看到这个属性存在于 Foo.bar 上。

>>> hasattr(Foo.bar, 'xyzzy')
True

而且我可以把它取出来。

>>> Foo.bar.xyzzy
'magic'

>>> getattr(Foo.bar, 'xyzzy')
'magic'

但是我却无法删除它。

>>> del Foo.bar.xyzzy
Traceback (most recent call last):
   ...
AttributeError: 'instancemethod' object has no attribute 'xyzzy'

>>> delattr(Foo.bar, 'xyzzy')
Traceback (most recent call last):
   ...
AttributeError: 'instancemethod' object has no attribute 'xyzzy'

有人知道为什么吗?我猜这可能和 Foo.bar 被报告为“实例方法”有关,而不是如果你只是问 Foo.bar 是什么时的“未绑定方法”。但我不确定这是否是一个预期的副作用,还是我理解错了什么,或者这真的是个bug。

2 个回答

5

你的问题其实不是出在函数属性上,而是实例方法和普通函数之间的区别。你可以试试:

class A(object):
    def foo(self):
        pass
    foo.bar = 'magic'

del A.__dict__['foo'].bar

这样就可以正常工作了。原因在于这里你实际上获取的是函数对象 foo,而不是实例方法 foo

在Python 3中,这种区别就不存在了,你所尝试的在实例方法上也能正常工作。想了解更多细节,可以查看这个邮件列表的讨论

6

你应该这样做:

del Foo.bar.im_func.xyzzy

im_func 是用来访问真正的方法 Foo.bar 的,instancemethod 是一种装饰器,可以自动处理传递 self 的问题……。


根据我对你问题的理解,你之前成功地使用 getattr 来访问 Foo.barxyzzy 属性,但发现 delattr 的表现不一样。原因在于,内部 getattr 会调用类和对象的所有父类的 __getattribute__(和 __getattr__),直到找到这个属性或者抛出 AttributeErrorhasattr 的工作原理也是类似的,等同于:

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

另一方面,delattr 只有在传入的属性是对象命名空间的一部分时才会成功(也就是说,不是它的类或父类的属性),这是出于安全考虑(你肯定不想尝试删除一个对象的属性,对吧 :-)。

撰写回答