一、介绍 首先,你一定用过魔术方法,也一定见过魔术方法。以下划线开头的方法,比如:newinitlenadd 这些被统称为魔术方法。 二、举例详解 给整数和字符串做加法:print(12)print(helloworld)输出:3输出:helloworld 我们写个表示城市的类,它有两个属性:城市名和人口。 然后我们给两个城市做加法,发现不能相加:classCity:definit(self,cityname,pop):self。citynamecitynameself。poppopcity1City(深圳市,2000)city2City(长沙市,4000)city3city1city2print(city3)输出:TypeError:unsupportedoperandtype(s)for:CityandCity 报错是说City不支持号,如何让它支持呢?需要给类加上魔术方法add就可以相加了。 我们给City添加一个add的方法,城市相加,人口相加,创建一个新的城市:classCity:definit(self,cityname,pop):self。citynamecitynameself。poppopdefadd(self,city):newcitynameself。citynamecity。citynamenewpopself。popcity。popreturnCity(newcityname,newpop)city1City(深圳市,2000)city2City(长沙市,4000)city3city1city2print(city3。cityname)print(city3。pop)print(city3)输出:深圳市长沙市输出:6000输出:main。Cityobjectat0x0000020937A5B7C8 这说明add有一定的魔力,当我们用到加号时,python就回去寻找这个方法,如果这个对象没有这个方法就会报错。 python中,所有的运算符都是通过魔术方法来实现的。 如果我们在City类有以下方法,就可以做加减乘除了:add(self,other)定义加法的行为:sub(self,other)定义减法的行为:mul(self,other)定义乘法的行为:truep(self,other)定义真除法的行为: 我们再来打印int和str查看他们的方法,int有加减乘除,str只有addmul,它只能做加法和乘法:print(dir(int))输出:〔abs,add,and,bool,ceil,class,delattr,dir,pmod,doc,eq,float,floor,floorp,format,ge,getattribute,getnewargs,gt,hash,index,init,initsubclass,int,invert,le,lshift,lt,mod,mul,ne,neg,new,or,pos,pow,radd,rand,rpmod,reduce,reduceex,repr,rfloorp,rlshift,rmod,rmul,ror,round,rpow,rrshift,rshift,rsub,rtruep,rxor,setattr,sizeof,str,sub,subclasshook,truep,trunc,xor,bitlength,conjugate,denominator,frombytes,imag,numerator,real,tobytes〕print(dir(str))输出:〔add,class,contains,delattr,dir,doc,eq,format,ge,getattribute,getitem,getnewargs,gt,hash,init,initsubclass,iter,le,len,lt,mod,mul,ne,new,reduce,reduceex,repr,rmod,rmul,setattr,sizeof,str,subclasshook,capitalize,casefold,center,count,encode,endswith,expandtabs,find,format,formatmap,index,isalnum,isalpha,isascii,isdecimal,isdigit,isidentifier,islower,isnumeric,isprintable,isspace,istitle,isupper,join,ljust,lower,lstrip,maketrans,partition,replace,rfind,rindex,rjust,rpartition,rsplit,rstrip,split,splitlines,startswith,strip,swapcase,title,translate,upper,zfill〕 三、魔术方法的定义魔术方法对python是至关重要的,python是运行在魔术方法的轮子之上的,没有魔术方法就没有python。形如xxx的方法自己随意定义的xxx方法是没有用的python运算符都对应着一个魔术方法,所以魔术方法都是内定的,具有特殊的含义for循环,被循环的对象之所以被循环,是因为他们有iter和nextlen(),del都有对应的魔术方法列表获取元素也有对应的魔术方法 列表为什么能获取元素,getitem,可以再任何一个类里加上这个方法,然后也就可以用〔〕方括号来获取元素:cities〔珠海市,厦门市,青岛市〕print(cities〔0〕)输出:珠海市print(dir(list))输出:〔add,class,contains,delattr,delitem,dir,doc,eq,format,ge,getattribute,getitem,gt,hash,iadd,imul,init,initsubclass,iter,le,len,lt,mul,ne,new,reduce,reduceex,repr,reversed,rmul,setattr,setitem,sizeof,str,subclasshook,append,clear,copy,count,extend,index,insert,pop,remove,reverse,sort〕 我们使用最多的方法一定是new和init,在新建方法的时候,都会调用到这两个方法:city1City(深圳市,2000)city2City(长沙市,4000) 四、魔术方法的好处python的一个点,可以非常灵活地实现运算符通过实现魔术方法可以让自定义的类支持各种操作 五、魔术属性 不止有魔术方法,还有魔术属性,形如yyy,通常是python自动设置的属性,我们可以使用这些属性,比如:print(file)打印出当前文件所在地目录和文件名print(name)判断当前模块是被导入进来的,还是被直接调用的输出:C:UsersDesktopmagicmethod。py输出:main(或者输出文件名) 六、查看所有魔术方法和魔术属性importgcgc管理内存的print(。join(sorted({attrnameforitemingc。getobjects()forattrnameindir(item)ifattrname。startswith()})))从内存中打印所有对象,并打印出每个对象中的魔术方法输出:aboutabsabstractmethodsaddaenteraexitaiterallandanextannotationsauthorawaitbasebasesbasicsizeboolbreakpointhookbuildclassbuiltinscachedcallcallbackcauseclassclosurecodecomplexconcatcontainscontextcopycreatecodecdebugdefaultsdeldelattrdeletedelitemdictdictoffsetdirdisplayhookdocentereqexcepthookexitfileflagsfloatfloorpformatfspathfuncgegetgetattrgetattributegetitemgetnewargsgetstateglobalsgthashiaddiandiconcatifloorpilshiftimatmulimodimportimulindexinitinitsubclassinstancecheckintinteractivehookinvinvertioripowirshiftisabstractmethodisubitemsizeiteritruepixorkwdefaultslelenloaderlshiftltmapgb18030extmapgb2312mapgbcommonmapgbkextmatmulmissingmodmodulemromulnamenenegnewnextnotobjclassorpackagepathpospowpreparequalnameraddrandreducereduceexreprreversedrmodrmulrorrshiftrsubrxorselfsetsetattrsetitemsetstatesizeofslotsspecstderrstdinstdoutstrsubsubclasschecksubclassessubclasshooksuppresscontexttextsignaturetracebacktruepweakrefweakrefoffsetwrappedxor 七、常见的魔术方法初始化和构造构造方法new(cls,other)对象实例化时自动触发(在init之前触发)初始化方法init(self,other)对象实例化之后立即触发析构方法del(self)当该类对象被销毁时 一元运算符pos(self)定义正号的行为:xneg(self)定义负号的行为:xabs(self)定义当被abs()调用时的行为invert(self)定义按位求反的行为:xround(self)调用round()方法时被调用floor(self)调用math。floor()方法时被调用ceil(self)调用math。ceil()方法时被调用trunc(self)调用math。trunc()方法时被调用 增量赋值iadd(self,other)定义赋值加法的行为:isub(self,other)定义赋值减法的行为:imul(self,other)定义赋值乘法的行为:itruep(self,other)定义赋值真除法的行为:ifloorp(self,other)定义赋值整数除法的行为:imod(self,other)定义赋值取模算法的行为:ipow(self,other〔,modulo〕)定义赋值幂运算的行为:ilshift(self,other)定义赋值按位左移位的行为:irshift(self,other)定义赋值按位右移位的行为:iand(self,other)定义赋值按位与操作的行为:ixor(self,other)定义赋值按位异或操作的行为:ior(self,other)定义赋值按位或操作的行为: 类型转换int(self)定义当被int()调用时的行为(需要返回恰当的值)float(self)定义当被float()调用时的行为(需要返回恰当的值)complex(self)定义当被complex()调用时的行为(需要返回恰当的值)oct(self)定义当被oct()调用时的行为(需要返回恰当的值)hex(self)定义当被hex()调用时的行为(需要返回恰当的值)trunc(self)定义当被trunc()调用时的行为(需要返回恰当的值)index(self)当对象是被应用在切片表达式中时,实现整形强制转换 字符串str(self)调用str()方法时被调用repr调用repr()方法时被调用unicode调用unicode()方法时被调用format调用string。format()方法时被调用hash调用hash()方法时被调用nonzero调用nonzero()方法时被调用dir调用dir()方法时被调用sizeof调用sys。getsizeof()方法时被调用 属性getattr(self,name)定义当用户试图获取一个不存在的属性时的行为setattr(self,name,value)定义对属性的赋值行为,无论属性是否存在delattr(self,name)定义删除属性的行为 操作符add(self,other)定义加法的行为:sub(self,other)定义减法的行为:mul(self,other)定义乘法的行为:truep(self,other)定义真除法的行为:floorp(self,other)定义整数除法的行为:mod(self,other)定义取模算法的行为:pow(self,other〔,modulo〕)定义当被power()调用或运算时的行为lt(self,other):定义小于号的行为:xy调用x。lt(y)le(self,other):定义小于等于号的行为:xy调用x。le(y)eq(self,other):定义等于号的行为:xy调用x。eq(y)ne(self,other):定义不等号的行为:x!y调用x。ne(y)gt(self,other):定义大于号的行为:xy调用x。gt(y)ge(self,other):定义大于等于号的行为:xy调用x。ge(y)lshift(self,other)定义按位左移位的行为:rshift(self,other)定义按位右移位的行为:and(self,other)定义按位与操作的行为:xor(self,other)定义按位异或操作的行为:or(self,other)定义按位或操作的行为: 八、魔术方法的操作实例new常用操作2new调用其他类classOtherClass1:print(其他类)a其他类的值otherobjOtherClass1()3new实现单例模式classOtherClass2:definit(self):print(触发了OtherClass2的初始化)otherobj1OtherClass2()otherobj2OtherClass2()print(otherobj1的id:,id(otherobj1))print(otherobj2的id:,id(otherobj2))装饰器单例模式defclassonecase(cls):空字典储存类和类实例(key:value)instace{}definner(args,kwargs):如果类不在字典中实例化对象储存,否者用字典中的对象ifclsnotininstace:instace〔cls〕cls(args,kwargs)returninstace〔cls〕else:returninstace〔cls〕returninnerclassonecaseclassCity(object):instanceNonedefinit(self,cityname):print(触发了初始化方法)self。citynamecitynamedefnew(cls,args,kwargs):print(触发了构造方法)returnsuper()。new(cls)1重写new方法,调用父类的new方法创建对象,并进行返回returnobject。new(cls)returnotherobj2返回其他类的对象3如果instance为None实例化对象,否则用第一次实例的对象ifnotcls。instance:print(instance为:,cls。instance)cls。instanceobject。new(cls)returncls。instanceelse:print(instance为:,cls。instance)returncls。instancedefrunonecase(self):print(单例装饰器runonecase:,self。cityname)city1City(深圳市)print(city1。cityname)city2City()print(city2。a)city3City(深圳市)city4City(深圳市)print(city3的id:,id(city3))print(city3的id:,id(city4))所以city3创建的属性,city4一样有city3。pop1234print(city4的pop为:,city4。pop)单例装饰器city4。runonecase() str和repr常用操作使用format函数、str函数、print打印对象时会优先触发str方法没定义str方法的情况下,会再去找repr方法,如果都没有,那么就会去找父类的str方法。如果父类没有str方法,会找父类的repr方法。如果继承object的话,object里面肯定有str方法的。除非是继承一个自己定义的类。 repr在交互环境下和内置函数处理对象时,它只会触发repr方法,如果repr方法不存在,会找父类的repr方法。repr方法是str方法的备胎。但是repr方法不能拿str方法做备胎。父类是第二轮备胎。print打印是由str方法决定的,交互环境下是由repr方法决定的。 什么使用str方法,什么时候用repr方法?str和repr方法都是返回字符串。在str方法里面返回的是字符串,直观的描述信息。str方法返回内容是给用户看的,返回是直接的描述信息。repr返回的东西是给程序员看的。程序员能看到是哪个类出来的对象,知道最开始的样子。 如果我们想让print打印出来好看,可以定义str的方法:classCity:definit(self,cityname,pop):self。citynamecitynameself。poppopdefadd(self,city):newcitynameself。citynamecity。citynamenewpopself。popcity。popreturnCity(newcityname,newpop)打印就会调用这个方法,如果str方法不存在,会去触发repr方法,打印出repr方法的内容:defstr(self):print(触发了str方法)returnf{self。cityname}:{self。pop}defrepr(self):print(repr被触发)报错:TypeError:reprreturnednonstring(typeNoneType)repr返回的不是个字符串,是个None。必须返回字符串。returnmyClass。object{}。format(self。cityname)假设没写这2个方法的时候,返回的是个对象city1City(深圳市,2000)city2City(长沙市,4000)city3city1city2print(city3)str(city3)format(city3)resrepr(city1)print(res) call常用操作call函数能被调用defcalldemo():print(fun)calldemo()字符串不能被调用a100a()TypeError:strobjectisnotcallable想让类像函数一样能都被调用,加上call方法classCallDemo2:defcall(self,args,kwargs):print(触发了类的call方法)callCallDemo2()call()