今天在跟一个朋友讨论到关于Python中的反射操作时,引出一个很有趣的问题,就是delattr究竟能不能用来删除一个对象的方法?
何以产生这样一个疑问呢,我们来看这样一段案例代码:
def func():
pass
class MyClass:
pass
mc = MyClass()
setattr(mc, 'myfunc', func)
print(hasattr(mc, 'myfunc')) # 打印结果为True
delattr(mc, 'myfunc')
print(hasattr(mc, 'myfunc')) # 打印结果为False
运行上面的代码,第一个print打印出来是True,第二个print打印出来是False。我们通过setattr方法为mc这个对象动态添加了一个叫myfunc的方法,然后又通过delattr方法动态删除了这个方法,这看起来似乎没什么问题,一切正常。
然后,我们再来看看下面这段代码:
class MyClass:
def myfunc():
pass
mc = MyClass()
print(hasattr(mc, 'myfunc')) # 打印结果为True
delattr(mc, 'myfunc')
print(hasattr(mc, 'myfunc')) # 打印结果为False
运行这段代码,发现报错了。
![watermark,image_bG9nby9jc2RuXzEucG5nP3gtb3NzLXByb2Nlc3M9aW1hZ2UvcmVzaXplLGhfNjA=,g_se,x_0,y_0,t_100](https://img-blog.csdnimg.cn/img_convert/165ccde90d9fc297c56b583504aa7145.png?x-oss-process=image/watermark,image_bG9nby9jc2RuXzEucG5nP3gtb3NzLXByb2Nlc3M9aW1hZ2UvcmVzaXplLGhfNjA=,g_se,x_0,y_0,t_100)
提示属性错误,意思就是mc这个对象中没有myfunc这个属性。myfunc是mc这个对象的一个方法,这个报错没毛病。
但问题来了,为什么前面我们可以用delattr删除一个方法,而现在我们却不行了?
难道是因为只有动态添加的方法才能被删除,对象自身的方法就不行吗?这看起来有点奇怪,如果能删除方法的话应该都能删除才对。我们再来尝试下删除对象自身的属性,假设刚刚这个结论是正确的话,那么我们使用delattr来删除对象自身的属性应该也会失败。我们用代码来实践一下。
class MyClass:
def __init__(self):
self.name = 'test'
def myfunc(self):
pass
mc = MyClass()
print(hasattr(mc, 'name')) # 打印结果为True
delattr(mc, 'name')
print(hasattr(mc, 'name')) # 打印结果为False
运行上面这段代码,操作成功了,没有任何的报错。这说明一个问题,delattr并不仅仅只能操作动态添加的属性,也能操作对象本身的属性。
那么问题出在哪里呢?
其实仔细一看我们就会明白,真正的问题出在,不管是setattr和delattr,其实都只能针对对象的属性进行操作。
前面我们看到的通过delattr能够删除通过setattr动态添加的方法,其实也是一个假象。真相是通过setattr添加的一个方法并不是真的给这个对象添加了一个方法,而是添加了一个属性,setattr方法的第二个参数就是这个属性的名字,然后这个属性的值是一个指向外部函数的引用地址,所以当我们调用这个对象的属性时,实际上是间接调用了这个函数,看起来就像是这个对象添加了一个方法一样,但本质上仍然是添加的一个属性。
至此,前面的疑问全部解决了,大家记住,不管是setattr和delattr,其实都只能针对对象的属性进行操作,它们对对象的方法是无法直接操作的。
注:本文为蜗牛学院资深讲师卿淳俊老师原创,首发链接https://mp.weixin.qq.com/s/SPtSec5TXloz1W36kUFB6Q,切勿擅自盗用,如需转载请私聊我处获得授权并注明出处。