Python魔法方法(在合适的时间,python自动调用如下方法魔法方法)
此前我们已经解除了Python中最常用的魔法方法:__init__
什么时候调用__init__? 答:我们想在对象实例化的时候就赋某些值就重写它呗!就是看需求,有需要就重写,没需要就不重写呗,不重写就是啥也没有呗!嗯~对的!
*__init__(self[,...])
__init__是不可以设置返回值的,他只能默认的返回None,如果试图给它设置返回值,会出现TypeError的异常
*__new__(cls[,...])
实际上实例化一个类的时候第一个调用的不是__init__方法,而是这个__new__方法
·他和其他实例化方法不同,他的第一个参数是类和self一样就写cls,如果后面有参数,后面的参数会原封不动的传给__init__方法
·__new__返回一个实例对象,可以是这个类的实例对象也可以是其他类的实例对象
·一般情况下我们极少的会去重写这个__new__方法
·但是在继承了不可改变的父类的时候,new方法就显得尤为重要了
因为init只能返回None,因此我们设置这个new去掉用不可改变的父类中的方法,如下我想把所有的字符串都变成大写显示
我发现这样写也行[但是上方那样还写,只修改了传入的string,而其他的str方法继续用,而下方的可能就没了把,不太清楚,如果用还是按照上方那样用]
实际上通过__init__方法也可以实现,只不过不能直接返回,而是通过函数调用出来,但是他不调用方法,实例化返回的还是aaa,而new实例化返回的就是我们所设定好的全部大写了!!,可见还是有很大区别的。
*__del__(self)
当对象【内存】被销毁的时候,这个方法会被自动的调用
他是当垃圾回收机制回收的时候,他才会调用这个对象的del方法,因此下图等号不成了(并不是我们调用del方法,他就会调用__del__方法)
工厂函数
~~~算数运算~~~~【个人觉得这东西知道怎么用即可,随着学习的深入可能以后会用到】
当你调用加法是会自动调用__add__方法,调用减法的时候会自动调用__sub__方法...
但是注意了,下图这样会报错【报错的原因是无线递归,因为self是实例化对象,other是执行方法是另一个参数对象,而self是由这个类实例化的,因此当执行到(return self + other)的时候,会调用__add__方法,如此一直调用,停不下来!】
解决方法:将self转化成int,不当成这个类的对象,则不会调用该类的__add__方法了
什么是反运算呢?
如a+b,而a没有__add__方法,则会执行b的__radd__方法:
如下图:数字1没有__add__方法,因此会执行a对象中的的__radd__方法 #如果俩个都是数字没有,个人理解是默认是int,但是这个int优先级比Nint低,为什么呢?因为Nint继承自int,而int内置方法没有写出来,优先级比较低,直接写出来的radd方法优先级高。 先这样写看看以后怎么说!!!
下图中在(1+a)中调用radd方法的self指的就是a对象,而other才是1
增量赋值运算
简单定制
*__str__(self)
#print这个实例化对象走的就是__str__()
*__repr__(self)
#没有重新定于__new__方法,实例化对象直接返回的就是__repr__()
#调用new是最强的,有它在实例化后返回的一定是new返回的,如果没有它,则是repr返回的 #Nxj自己测试的结果 【但是new还是很少用的嘛,还是需要多看多练加强记忆别搞混】
localtime()返回的的是如下的元组结构:
其实写的有问题,就是比如说开始时间是是16:25:50 结束时间是16:26:04 ,那么返回的是个负数
解决思路应该是如果是负数说明最起码最前面的第一项一定大【16:26:50~~17:22:05】【如果不全部化成秒的话】,如果发现这个是负数,就用60+上这个负数返回,相应的需要把end的数组这个的前一项数值-1 ,对于分钟也是这样,而对于小时就是+12 了,一直到年,【一直到年还是恐怖的,一般不会的-_-||】
扩展【纠正】一下以前写的,以前写的是没错,但是针对于self.这样来写属性的话,属性名和方法名相同的话,属性会覆盖方法,而以前写得的是谁在下面谁优先级高恢复高以前的,这个写的也没错,但是谁在下面谁覆盖上面针对的属性是不带self的
就是!:如果一个类中含有self.name1=属性 与def name1(): 无论谁在前在后,self.name1属性是会覆盖name1的方法的
而如果是name 与 def name1(): 谁在前谁被覆盖!这个应该没问题
针对的主要是self内!!!
报的异常:
在此提醒:类中的属性不能被变更为方法!!!切记切记!!这个属性指的是self下的属性!!也需要切记切记!!!
小甲鱼这个写法可比我简单啊 这个思路虽然跟我很像,但是我感觉这样写好巧啊,省了好多的代码啊 好清晰!!
下面这个和我上面一样没考虑时间为负值哦
方法前加一个下划线变成伪私有,属性前加俩个下划线变成伪私有
下方这个是我搞出的最终版
import time as t class Mytimer:#self所有指的都是我实例化出来的!(那个对象)!,无论在哪个方法内的self都一样 #相加 def __add__(self,other): self.prompt="二者总共持续了" result=[] for i in range(0,6): result.append(self.arr[i]+other.arr[i]) if result[i]:#如果不是0 self.prompt+=str(result[i])+str(self.unit[i]) return self.prompt #事先定义防止出错 def __init__(self): self.prompt="未调用stop()方法" self.begin=0 self.end=0 self.unit=['年','月','天','小时','分钟','秒'] self.arr=[0,0,0,0,0,0]#这个是为了解决__add__方法如果运行方法,防止报错,没运行让他为就好 #print出来显示 def __str__(self): return self.prompt #实例化对象返回出来 __repr__=__str__ #计时开始 def start(self): print("计时开始...") self.begin=t.localtime() #这里不能写start否则类的属性会使得start方法失效 #计时结束 def stop(self): #print(self) if not self.begin:#没有!self.begin这样写的哦 print("请先调用start()方法") else: self.end=t.localtime() self._lasttime() print("计时结束") #持续时间 def _lasttime(self):#方法前面加上一个下划线,编程伪私有 #时间返回的是一个元组形式,索引前六个分别是年月日时分秒 self.arr=[]#按照年月日时分秒差值6单位个添加进去【持续时间】 self.prompt="总共持续了:" for i in range(6): self.arr.append(self.end[i]-self.begin[i]) #if self.arr[i]:#如果不是0 #self.prompt+=str(self.arr[i])+str(self.unit[i]) #解决出现减出负号的问题 arrlist=[5,4,3,2,1,0] arrlist1=['这个肯定不用',12,30,12,60,60]#30那个有的月份是31 2月也可能反正很多 for i in arrlist: if self.arr[i]<0: self.arr[i]+=arrlist1[i] self.arr[i-1]-=1 for i in range(0,6): if self.arr[i]:#如果不是0 self.prompt+=str(self.arr[i])+str(self.unit[i]) print(self.prompt) #执行完归零方便下次调用 self.begin=0 self.end=0
·~~~属性访问~~~
下放图,setattr这 写return也是一样的
无论属性存不存在,获取属性第一个走的方法就是__getattribute__(),他必须有renturn,否则当该属性不存在的时候也不会执行__getattr__()方法
下图这样写会进入死循环
解决方案:
或者这样写也可以: .__dict__是把所有属性以字典的形式返回出来
~~~描述符(property的原理)~~~
·就是当我把这个特殊的类实例化赋给了另一个类的属性的时候,之后实例化那个类,操作那个属性的时候就操作我这个特殊类的__get__等方法
self:指的是我这个特殊的类的实例,就是另一个类中赋予的那个属性
instance:指的是另一个类的实例化
owner:指的是指的是另一个类
下图中不是__del__而是__delete__ 【__del__是被垃圾回收机制回收该块实例化占的内存的时候调用】
下图中的self指的是该类的实例化fah,而不是那个类
协议
对于列表、元组、字典等等都是容器,那么接下来谈一谈容器的协议
#用数组表示 #牢记可变参数*啊 class List: def __init__(self,*arg): self.count=[0 for x in arg]#把前面0换成x这就是把arg每个参数都赋值给列表 self.values=[] for i in arg: self.values.append(i) def __len__(self): return len(self.values) def __getitem__(self,key): self.count[key]+=1 return self.values[key] def __delitem__(self,key): del self.count[key] del self.values[key] #用字典表示数量 class CountList: def __init__(self,*args): self.values=[x for x in args] #他使用的是字典 self.count={}.fromkeys(range(len(self.values)),0) #这种形式{0: 0, 1: 0, 2: 0, 3: 0, 4: 0} def __len__(self): return len(self.values) def __getitem__(self,key): self.count[key]+=1 return self.values[key] def __delitem__(self,key): del self.count[key] del self.values[key] self.countTemp={}.fromkeys(range(len(self.values)),0) for i in range(len(self.values)): if i<key: self.countTemp[i]=self.count[i] else: self.countTemp[i]=self.count[i+1] self.count=self.countTemp
迭代器~~!!
迭代:序列(列表 元组 字符串 字典)
iter()生成迭代序列
next()开始寻找下一个元素
如果迭代全部完成next再次执行会抛出StopIteration的异常
我们可以通过while写出for循环的原理
那么关于迭代器的魔法方法有俩个,就是对应的前俩个BIF的实现
本身就是迭代,因此在__iter__写的就是self