Python类,所有用双下划线__包裹的方法都称为魔术方法。魔术方法可以在类或对象的某些事件发出后自动执行,使类具有神奇的力量,如常见的构造方法__new__、初始化方法__init__、析构方法__del__。今天就来说说__new__的妙用,主要分享以下几点:__new__和__init__的区别应用1:改变内置不可变类型应用2:实现单例应用3:客户端缓存应用4:不同解密方式应用针对不同的文件5:Metaclasses__new__和__init__的区别1.调用的时机不同:new是实际创建一个实例方法,init用于实例初始化,new运行在init之前。2.返回值不同,new返回的是一个类的实例,而init不返回任何信息。3.new是类的方法,init是对象的方法。示例代码:classA:def__new__(cls,*args,**kwargs):print("new",cls,args,kwargs)returnsuper().__new__(cls)def__init__(self,*args,**kwargs):print("init",self,args,kwargs)defhow_object_construction_works():x=A(1,2,3,x=4)print(x)print("====================")x=A.__new__(A,1,2,3,x=4)ifisinstance(x,A):type(x).__init__(x,1,2,3,x=4)print(x)if__name__=="__main__":how_object_construction_works()上面的代码定义了一个类A,调用A(1,2,3,x=4)时,先执行new,再执行init,即相当于:x=A.__new__(A,1,2,3,x=4)ifisinstance(x,A):type(x).__init__(x,1,2,3,x=4)结果代码如下:new(1,2,3){'x':4}init<__main__.Aobjectat0x7fccaec97610>(1,2,3){'x':4}<__main__.Aobjectat0x7fccaec97610>====================new(1,2,3){'x':4}init<__main__.Aobjectat0x7fccaec97310>(1,2,3){'x':4}<__main__.Aobjectat0x7fccaec97310>new的主要作用是让程序员自定义类的创建行为。以下是它的主要应用场景:应用一:改变内置的不可变类型。我们知道,Tuple是一个不可变类型,但是我们继承了tuple,然后我们可以在new中修改它的tuple的元素,因为在new返回之前,元组不是元组,在init函数中是无法实现的。比如实现一个大写的元组,代码如下:code会报错,初始化时不能修改#def__init__(self,iterable):#print(f'init{iterable}')#fori,arginenumerate(iterable):#self[i]=arg.upper()if__name__=='__main__':print("UPPERCASETUPLEEXAMPLE")print(UppercaseTuple(["hello","world"]))#UPPERCASETUPLEEXAMPLE#('HELLO','WORLD')应用二:实现一个单例类Singleton:_instance=Nonedef__new__(cls,*args,**kwargs):ifcls._instanceisNone:cls._instance=super().__new__(cls,*args,**kwargs)returncls._instanceif__name__=="__main__":print("SINGLETONEXAMPLE")x=Singleton()y=Singleton()print(f"{xisy=}")#SINGLETONEXAMPLE#xisy=TrueApplication3:客户端缓存,当客户端创建成本比较高的时候,比如读取文件或者数据库,以下方法可以是我们编辑。同一个客户端属于同一个实例,节省了创建对象的开销。这本质上就是多实例模式。classClient:_loaded={}_db_file="file.db"def__new__(cls,client_id):if(client:=cls._loaded.get(client_id))isnotNone:print(f"returningexistingclient{client_id}fromcache")returnclientclient=super().__new__(cls)cls._loaded[client_id]=clientclient._init_from_file(client_id,cls._db_file)returnclientdef_init_from_file(self,client_id,file):#lookupclientinfileandreadpropertiesprint(f"readingclient{client_id}datafromfile,db,etc.")name=...email=...self.name=nameself.email=emailself.id=client_idif__name__=='__main__':print("CLIENTCACHEEXAMPLE")x=Client(0)y=Client(0)print(f"{xisy=}")z=Client(1)#CLIENTCACHEEXAMPLE#readingclient0datafromfile、db等#returningexistingclient0fromcache#xisy=True#readingclient1datafromfile、db等。应用4:首先在脚本所在目录创建不同文件的不同解密方法三个文件:plaintext_hello.txt、rot13_hello.txt、otp_hello.txt,程序会根据不同的文件选择不同的解密算法importcodecsimportitertoolsclassEncryptedFile:_registry={}#'rot13'->ROT13Textdef__init_subclass__(cls,prefix,**kwargs):super().__init_subclass__(**kwargs)cls._registry[prefix]=clsdef__new__(cls,path:str,key=None):prefix,sep,suffix=path.partition(":///")ifsep:file=suffixelse:file=prefixprefix="file"subclass=cls._registry[prefix]obj=object.__new__(subclass)obj.file=fileobj.key=keyreturnobjdefread(self)->str:raiseNotImplementedErrorclassPlaintext(EncryptedFile,prefix="file"):defread(self):withopen(self.file,"r")asf:returnf.read()classROT13Text(EncryptedFile,prefix="rot13"):defread(自我):withopen(self.file,"r")asf:text=f.read()returncodecs.decode(text,"rot_13")classOneTimePadXorText(EncryptedFile,prefix="otp"):def__init__(self,path,key):ifisinstance(self.key,str):self.key=self.key.encode()defxor_bytes_with_key(self,b:bytes)->bytes:returnbytes(b1^b2forb1,b2inzip(b,itertools.cycle(self.key))))defread(self):withopen(self.file,"rb")asf:btext=f.read()text=self.xor_bytes_with_key(btext).decode()returntextif__name__=="__main__":print("ENCRYPTEDFILEEXAMPLE")print(EncryptedFile("plaintext_hello.txt").read())print(EncryptedFile("rot13:///rot13_hello.txt").read())print(EncryptedFile("otp:///otp_hello.txt",key="1234").read())#ENCRYPTEDFILEEXAMPLE#plaintext_hello.txt#ebg13_uryyb.gkg#^FCkYW_X^GLE应用5:Metaclassesmetaclass可以像装饰器一样进行自定义修改,继承其子类。上一篇Python黑魔法之Metaclass请联系Python七公众号。