首页 > 技术文章 > Python魔法方法(在合适的时间,python自动调用如下方法魔法方法)

ningxinjie 2019-05-31 11:06 原文

此前我们已经解除了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  

 

 

 

推荐阅读