进击のpython
类的双下划线方法
双下划线方法是类的特殊方法,是由双下划线加方法名加双下划线进行定义的
而这样的写法就表示,它其实是有特殊意义的
(没有特殊意义我提他干撒,不是神经病嘛)
其实啊,双下划线方法更多是python的源码开发者使用的
他们在写源码的时候,会采用这种双下划线方法
但是我建议在开发的时候尽量少使用这种方法
那尽量少使用,为什么还要说呢?
这是因为,学一下这种方法,可以帮助我们更好地理解源码
放心兄弟
你要是没读过一两个源码,那算你口技好
源码可是必须要翻过去的山┗|`O′|┛ 嗷~~
来吧,既然是了解,就不会很细
-
大概了解的方法
len方法
这个方法很熟悉吧
其实我们在计算什么字典啊列表之类的长度的时候
表面上写的是len()
实际上是在调用--len--方法
正常我们在执行这个函数的时候是这样的
l = [1, 2, 3, 4, 5] print(l.__len__()) print(len(l))
结果喜闻乐见,两个5是吧,
但是我要是将他的方法进行修改
class Demo(object): def __len__(self, obj): print("我是len方法") return 1 l = Demo() print(l.__len__([1, 2, 3, 4, 5]))
(为什么要写return呢? 是因为要求的,不写就会报错)
你打印的结果,还是5嘛?
是不是就不是了
所以说,这个len()在执行的时候,实际上是在调用双下划线len方法
hash方法
其实和楼上的len方法相同
这个方法就是调用了内部的--hash--方法
仿照上面的写一个!
item系列
这个系列,我想说一个大家都很熟悉的东西
字典
你有想过字典是怎么赋值调用的吗?
其实用的都是双下划线方法
话不多说直接上代码
class Dic: # def __init__(self, name): # self.name = name def __getitem__(self, item): print("获取KEY", item) print(self.__dict__[item]) def __setitem__(self, key, value): print("设置一个key...", key) self.__dict__[key] = value def __delitem__(self, key): print('执行del时,我执行') self.__dict__.pop(key) def __delattr__(self, item): print('删除的时候我执行') self.__dict__.pop(item) b = Dic() b[1] = 1 b[2] = 2 del b[2] b[3] = 3 b[3] print(b.__dict__)
执行结果如下:
设置一个key... 1 设置一个key... 2 执行del时,我执行 设置一个key... 3 获取KEY 3 3 {1: 1, 3: 3}
所以说,字典的相关操作,实际上是在调用相应的双下划线方法
-
重点掌握的方法
str&repr
这两个方法应该知道吧
(要是不知道就回去看博客吧凑弟弟)
那我们在使用它的时候实际上调用的就是对应的双下划线方法
举个例子
class R: def __init__(self, item): self.item = item pass s = R("ponny") print(str(s)) print(repr(s))
可以看到打印出来的其实都是相对应的内存地址
<__main__.R object at 0x0548FF50> <__main__.R object at 0x0548FF50>
现在我们自己这两个相对应的双下划线方法
看看是不是对这两个函数有影响
class R: def __init__(self, item): self.item = item pass def __str__(self): print(f"我是{self.item}的str方法!") return "A" def __repr__(self): print(f"我是{self.item}的repr方法!") return "B" s = R("ponny") print(str(s)) print(repr(s))
当我们执行上面的语句的时候
应该得到的是这样的结果
我是ponny的str方法! A 我是ponny的repr方法! B
说明什么?
说明这两个方法的使用确实是调用python内部同名的双下划线方法
而我们在局部空间自己定义了这个方法
根据加载顺序,先加载的就是我们写好的
所以说,也可以说明这两个方法其实是调用对应的双下划线方法
其实还有一点想要说的
当我把str方法注释掉再执行
会发生什么现象呢?
class R: def __init__(self, item): self.item = item pass # def __str__(self): # print(f"我是{self.item}的str方法!") # return "A" def __repr__(self): print(f"我是{self.item}的repr方法!") return "B" s = R("ponny") str(s) print(str(s))
结果是这样的:
我是ponny的repr方法! 我是ponny的repr方法! B
说明什么?
当我们解释器中要是没有str双下划线方法的时候
在使用str方法时,就会使用repr的双下划线方法
然后其实还有一点就是
其实你可能也注意到了
这两个方法的返回值必须是字符串,否则会报错
(要是感兴趣你可以自己去上网查一下相关资料)
del 析构方法
析构方法,当对象在内存中被释放时,自动触发执行
怎么理解呢?
我现在搞一个这样的代码
class R: def __init__(self, item): self.item = item pass def __del__(self): print("我是del...") pass
当我对这个类进行实例化会发生什么呢?
s = R("ponny")
执行结果如下:
我是del...
欸?
我不是没有执行这个方法嘛?
怎么还会执行啊??
我们先把这个疑问放在这
我们在这实例化的后面
加上一个打印语句
看看会打印什么??
print("我是后面的语句")
结果你可能大概想到(猜到)了
我是后面的语句 我是del...
所以说为什么会是这个执行顺序呢?
前面我们说了,del是个析构方法
而析构方法就是对象在内存中被释放了就会自动执行
当py文件的所有东西都执行完了的时候
在内存中储存的对象就会自动释放
(这是python的垃圾回收机制导致的)
从而就会触发del方法
而del方法的本质就是双下划线的del方法
所以,就在最后执行了那个打印语句
但是呢
此方法一般无须定义,因为Python是一门高级语言
程序员在使用时无需关心内存的分配和释放
因为此工作都是交给Python解释器来执行
所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的