Python3中的__new__方法以及继承不可变类型类的问题
最近在学到Python中的__new__方法时被弄懵逼了,一开始实在是很难理解,有很多地方想不通(本人强迫症)。最近自己慢慢思索得出了能说服自己的理解:
说__new__方法之前要先提到__init__方法,__init__方法是普遍认为的Python类的构造方法,在我们对类进行实例化的时候,Python解释器会调用__init__方法对我们在init方法中定义的属性进行初始化,比如:
class demo(): def __init__(self,arg,kwarg): #定义属性并初始化 self.arg = arg self.kwarg = kwarg def Output(self): print(self.arg) print(self.kwarg) a = demo("NMSL","WSND") #实例化 a.Output() #调用类中的Output方法
但是其实在Python中,__init__并不是真正的构造函数,准确的说,__new__加__init__才是真正的构造函数,下面详细说一下__new__方法。
当我们在对类进行实例化的时候,Python解释器会从__new__方法的返回值中获取到实例对象的信息,在上面代码的例子中,我们把类demo进行实例化,对象为a
在这个过程中,类demo是怎么确定它的实例对象是a呢,就是__new__方法返回了这个值,这个值就是a,然后Python解释器就知道了demo这个类的对象为a,其实这里的返回值最后被Python解释器告诉__init__方法,init方法中的关键字self其实就是这个a,因为self代表的是类的对象。
__new__方法还有另一个用处就是用来实现单例设计模式以及继承不可变类的时候方便我们定制类:
class newfloat(float): def __new__(cls,value): return super().__new__(cls,round(value,4)) i = newfloat(3.14529) print(i)
这里的思路是用来接收__new__方法的返回值再打印出来。
老实说我在很多文章下面看到过关于__new__方法的应用,都是拿这个举例子,但是我觉得根本没有必要这样,我觉得可以这样:
class demo(int): def __init__(self,arg): self.arg = arg self.arg = round(self.arg,4) print(self.arg) a = demo(3.1415926)
这样的写法同样能实现功能。
单例设计模式是为了解决一个类有多个对象的时候,多个对象引用同一个内存地址,以减少内存占用的问题。
实现思路:
重写父类的__new__方法,使每次返回的内存地址引用都为同一个。
class demo(object): ins = None def __new__(cls): if cls.ins == None: cls.ins = super().__new__(cls) return cls.ins
a = demo()
b = demo()
print(a)
print(b)
这里的思路是:定义一个类属性ins为空值,再在重写__new__方法时候用if语句进行判断,如果ins为空则赋给它__new__方法的返回值,当用a去实例化demo时,
实际上是Python解释器为这次实例化开辟了一片内存空间,a就是引用了这个内存地址。
然后我们再用b去实例化demo,由于ins的值已经被改写,所以返回的是同样的之前a对象的内存地址,这样就成功实现了单例设计模式。
这里必须好好解释一下__new__方法的参数,在定义new方法时,在参数栏里不能写self,而是写cls,self代表的是类的实例,而cls则代表的是类本身。
继承不可变类型,我们知道Python中的不可变类型有int,str和tuple,但是Python中有这么一个思想:万物皆对象。也就是说,int,str和tuple这中数据类型其实也是一种对象,拿字符串举例,我们可以利用count之类的方法直接对字符串进行操作,但是这其实是Python内置类str中定义的方法,我们可以用dir()方法查看类中定义有哪些方法,但是在重写new方法时最多只能多传一个参数,这是为什么?
下面时我自己的解释,我们平时用内置方法比如count()时,一次只能对一个字符串进行操作,理所应当,每次当我们重写str类中定义的方法时,只能传入一个值。
其他注意事项:
__init__方法不能有返回值,而__new__方法却必须有返回值。
如有错误,请指正,感激不尽。