魔术方法
在Python中,所有以“__”双下划线包起来的方法,都统称为“Magic Method”,中文称『魔术方法』
魔术方法就是一个类/对象中的方法,和普通方法唯一的不同时,普通方法需要调用!而魔术方法是在特定时刻自动触发。
1. __new__
实例化魔术方法 类方法
触发时机: 在实例化对象时触发
参数:至少一个cls 接收当前类
返回值:必须返回一个对象实例
作用:实例化对象
注意:实例化对象是Object类底层实现,其他类继承了Object的 __new__ 才能够实现实例化对象。
没事别碰这个魔术方法,先触发__new__才会触发__init__
2 .__init__
初始化魔术方法
成员
触发时机:初始化对象时触发(不是实例化触发,但是和实例化在一个操作中)
参数:至少有一个self,接收对象
返回值:无
作用:初始化对象的
pytorch 中,在自定义数据集时候需要重写该函数用来定义数据集路径等。
当我们创建一个实例的时候,实际上是先调用的__new__函数创建实例,然后再调用__init__对实例进行的初始化。
Python当中的类的构造函数是 __new__ 。__init__并不是构造函数,它只是初始化方法。也就是说在调用__init__之前,我们的实例就已经被创建好了,__init__只是为这个实例赋上了一些值。
在使用Python面向对象的时候,一般都不会重构 __new__,而是使用Python提供的默认构造函数
参考:Python面试常见问题,__init__是构造函数吗? - 云+社区 - 腾讯云
3. __del__
析构魔术方法 构析函数
触发时机:当对象没有用(没有任何变量引用)的时候被触发
参数:一个self
返回值:无
作用:在销毁对象时回收资源
注意:del不一定会触发当前方法,只有当前对象没有任何变量引用时才会触发
4 .__len__
触发时机:使用 len(对象) 的时候触发
参数:一个参数self
返回值:必须是一个整型
作用:可以设置为检测对象成员个数,但是也可以进行其他任意操作
注意:返回值必须必须是整数,否则语法报错,另外该要求是格式要求。
pytorch 中,在自定义数据集时,需要重写该函数,用来返回 数据的个数。
5. __call__
调用对象的魔术方法
触发时机:将对象当作函数调用时触发,方式: 对象(参数)
参数:至少一个self接收对象,其余根据调用时参数决定
返回值:根据情况而定
作用:可以将复杂的步骤进行合并操作,减少调用的步骤,方便使用
注意:无
允许一个类的实例像函数一样被调用。实质上说,这意味着 x() 与 x.__call__() 是相同的。注意 __call__ 的参数可变。这意味着你可以定义 __call__ 为其他你想要的函数,无论有多少个参数。
__call__ 在那些类的实例经常改变状态的时候会非常有效。调用这个实例是一种改变这个对象状态的直接和优雅的做法。
在pytorch 中:在Model继承的父类nn.Module里应该有一个__call__()直接自动调用了forward函数,而在Model这个nn.Module的子类里我们又重写了forward函数,所以我们间接地通过父类nn.Module里的__call__()调用了子类Model里的forward函数。
6. 容器类型相关魔术方法
__len__(self) 定义当被 len() 调用时的行为(返回容器中元素的个数)
__getitem__(self, key) 定义获取容器中指定元素的行为,相当于 self[key]
__setitem__(self, key, value) 定义设置容器中指定元素的行为,相当于 self[key] = value
__delitem__(self, key) 定义删除容器中指定元素的行为,相当于 del self[key]
__iter__(self) 定义当迭代容器中的元素的行为
__reversed__(self) 定义当被 reversed() 调用时的行为
__contains__(self, item) 定义当使用成员测试运算符(in 或 not in)时的行为
当实例对象(假定为p)做 p[key] 运算时,会调用类中的方法 __getitem__ 。
在pytorch中,自定义数据集时,需要重写该方法,获取一条数据。该方法需要一个 index 索引。
__iter__(self)
一文彻底搞懂Python可迭代(Iterable)、迭代器(Iterator)和生成器(Generator)的概念 - 云+社区 - 腾讯云
一个对象(在Python里面一切都是对象)只要实现了__iter__()
方法,那么用isinstance()
函数检查就是Iterable
对象;
常见的可迭代对象
在Python
中有哪些常见的可迭代对象呢?
- 集合或序列类型(如
list
、tuple
、set
、dict
、str
) - 文件对象
- 在类中定义了
__iter__()
方法的对象,可以被认为是Iterable
对象,但自定义的可迭代对象要能在for
循环中正确使用,就需要保证__iter__()
实现必须是正确的(即可以通过内置iter()
函数转成Iterator
对象。iter()
函数是能够将一个可迭代对象转成迭代器对象,然后在for
中使用) - 在类中实现了如果只实现
__getitem__()
的对象可以通过iter()
函数转化成迭代器但其本身不是可迭代对象。所以当一个对象能够在for
循环中运行,但不一定是Iterable
对象。
在定义一个可迭代对象时,我们要非常注意__iter__()
方法的内部实现逻辑,一般情况下,是通过一些已知的可迭代对象(例如,上文提到的集合、序列、文件等或其他正确定义的可迭代对象)来辅助我们来实现
在for
中使用的对象,不一定是可迭代对象。
一个对象实现了__iter__()
和__next__()
方法,那么它就是一个迭代器对象。
一个迭代器(Iterator
)对象不仅可以在for
循环中使用,还可以通过内置函数next()
函数进行调用。
如果一个对象定义了 __iter__
和 __next__
两个方法,它就是一个迭代器。对于迭代器来说,__iter__
返回的是它自身 self,__next__
则是返回迭代器中的下一个值,如果没有值了则抛出一个 StopIteration
的异常。
如果一个对象定义了 __iter__
和方法,返回一个迭代器对象,那么它就是一个可迭代的对象。
迭代器(Iterator)和可迭代(Iterable)这两个的差别:
- 一个迭代器一定是可迭代对象,因为它一定有
__iter__
方法。反过来则不成立。(事实上,Iterator 就是 Iterable 的子类) - 迭代器的
__iter__
方法返回的是自身,并不产生新实例。而可迭代对象的__iter__
方法通常会生成一个新的迭代器对象。
for 循环的实现理解:
- 首先 for 循环会调用可迭代对象的
__iter__
方法,获取相应的迭代器 - 每次循环,将迭代器的
__next__
方法的返回值赋值给循环变量 - 直到捕获迭代器抛出的
StopIteration
异常,循环结束
参考: