进击の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解释器来执行

所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的


*有点东西* *继续深究*