Python基础刻意练习——Day15&16:魔法方法(含自定义链表结构)

Day1:变量、运算符与数据类型
Day2:条件与循环
Day3&4:列表与元组
Day5:字符串与序列
Day6&7:函数与Lambda表达式
Day8:字典与集合
Day9&10:文件与文件系统
Day11:异常处理
Day12:else 与 with 语句
Day13&14:类与对象
Day15&16:魔法方法——>本文
Day17:模块

一、构造与析构

1.构造函数

通俗的讲解Python中的__new__()方法 - SJ2050的博客 - CSDN博客
new(cls[,…])方法是先于__init__方法执行的类构造方法,而__new__和__init__ 配合才是python中真正的类构造器
可以看出,__new__方法的第一个参数是这个类,而其余的参数会在调用成功后再全部传递给__init__方法初始化
同时应注意到,__init__方法传入类的实例化对象(self),而__new__方法返回的值就是一个实例化对象,如果__new__方法返回None,则__init__方法不会被执行
注意,返回值只能调用父类中的__new__方法,而不能调用毫无关系的类的__new__方法
绝大多数情况下,我们都不需要自己重写__new__方法,但在当继承一个不可变的类型(例如str类,int类等)时,它的特性就尤显重要了。

class CapStr(str):
    def __new__(cls,string):
        string = string.upper()
        return super().__new__(cls,string)
 
a = CapStr("I love China!")
print(a)
#I LOVE CHINA!

2.析构

方法__del__(self)用于析构对象
需要注意的是,del A(马上删除)并不等于A.del()
当没有变量引用这个类 的时候系统自动调用__del__()方法

class A():
    def __init__(self,Num):
        self.num=Num
    def printnum(self):
        print(self.num)
    def __del__(self):
        print('__del__被调用')

a1=A(5)
a2=a1
print('删除a1')
del a1
print('删除a2')
del a2
'''
删除a1
删除a2	
__del__被调用	在a2被删除之后才调用,与删除顺序无关
'''

二、算术运算

1.自定义运算

可以自定义方法对对象进行算术运算
主要方法如下:
Python基础刻意练习——Day15&16:魔法方法(含自定义链表结构)_第1张图片
p.s. divmod(a,b)的结果是元组(a//b,a%b)

class new_int(int):
    def __add__(self,other):			#试将加法定义为减法
        return int.__sub__(self,other)		
        
a=new_int(3)
b=new_int(5)
print(a+b)
#-2

注意,这里不能定义类似

class new_int(int):
    def __add__(self,other):			
        return self+other

进行返回,因为我们将self(a)和other(b)都定义成了new_int类型
对于这个类型的两个数据之间,“+”自动再次调用__add__(self,other)方法,造成无尽递归

2.反运算

反运算的方法就是在上述提到的运算方法名前加r
如,__rsub__代表+的反运算,即用右减去左,3 - 5 = 2
比如出现在两个不同类型对象的运算中时(往往其中一个对象的种类继承于另一个对象),因为左边的数找不到用于这两种类型的对应运算法,所以调用了右边的数的反运算方法

class new_int(int):
    def __radd__(self,other):
        return int.__sub__(self,other)

a=new_int(3)
print(1+a)	
#2	因为int中没有int+new_int的方法,所以调用了new_int中的__radd__()方法

3.增量赋值方法

增量赋值方法就是在上述提到的运算方法名前加i
如,__isub__代表+的增量赋值运算,即“+=”,a=1,a+=3——>a=4
也可自定义

4.一元运算符和类型转换

如下表,也可自定义
Python基础刻意练习——Day15&16:魔法方法(含自定义链表结构)_第2张图片
Python基础刻意练习——Day15&16:魔法方法(含自定义链表结构)_第3张图片

三、输出

1__str__(self)

__str__相当于str(obj),用在被打印的内容是字符串的时候

>>> class A():
...     def __str__(self):
...         return 'class A'
... 
>>> a=A()
>>> a
<__main__.A instance at 0x105ab8b90>
>>> print(a)
class A

2__repr_(self)

__str__相当于print(str(obj)),用在直接将内容转化为字符串打印的时候

>>> class B():
...     def __repr__(self):
...         return 'class B'
... 
>>> b=B()
>>> b
class B

四、属性访问

1.getattr,setattr,hasattr,dir,type

原本用法详见Day13&14:类与对象

  • 1__getattr__(self,name)
    定义当用户试图获取一个不存在的属性时的行为
  • 2__getattribute__(self,name)
    定义当该类的属性被访问时的行为
  • 3__setattr__(self,name)
    定义当一个属性被设置时的行为
  • 4__delattr__(self,name)
    定义当一个属性被删除时的行为
class A():
    def __getattr__(self, name):
        print('getattr')
    def __getattribute__(self, name):
        print('getattribute')
        return super().__getattribute__(name)

myA=A()
print(myA.a)
#myA=A()
#print(myA.a)

五、描述符(类)

描述符就是将某种特殊类型的类的实例指派给另一个类的属性。

  • 1__get__(self, instance, owner)
    用于访问属性,它返回属性的值。
  • 2__set__(self, instance, value)
    将在属性分配操作中调用,不返回任何内容。
  • 3__delete__(self, instance)
    控制删除操作,不返回任何内容。
class Desc(object):
    def __get__(self, instance, owner):
        print("__get__...",self,instance,owner)

    def __set__(self, instance, value):
        print('__set__...',self,instance,value)   
        
    def __delete__(self, instance):
        return ('__delete__...',self,instance)

class TestDesc(object):
    x = Desc()

t = TestDesc()
t.x				#希望调用__get__()
#__get__... <__main__.Desc object at 0x1092c3438> <__main__.TestDesc object at 0x1092c33c8> 
t.x				#希望调用__set__()
#__set__... <__main__.Desc object at 0x10e63e4e0> <__main__.TestDesc object at 0x10e63e470> hello
t.x				#希望调用__delete__()
#__delete__... <__main__.Desc object at 0x10e63e4e0> <__main__.TestDesc object at 0x10e63e470>

可以看出,这几个传入的参数分别为:

  • self: Desc的实例对象
  • instance: TestDesc的实例对象,其实就是t
  • owner: 即谁拥有这些东西,当然是 TestDesc这个类,它是最高统治者,其他的一些都是包含在它的内部或者由它生出来的

六、定制序列

协议(Protocols)与其它编程语言中的接口很相似,它规定你哪些方法必须要定义。
然而,在 Python 中的协议就显得不那么正式。事实上,在 Python 中,协议更像是一种指南。

  • 容器类型的协议
    如果需定制的容器是不可变的话,你只需要定义__len__()和__getitem()方法。
    如果需定制的容器是可变的话,除了__len
    ()和__getitem__()方法,还需要定义__setitem__()和__delitem()__两个方法。
方法名 具体内容
1__len__(self) 定义当被len()调用时的行为(返回容器中元素的个数)
2__getitem__(self, key) 定义获取容器中元素的行为,相当于self[key]
3__setitem__(self, key, value) 定义设置容器中指定元素的行为,相当于self[key] = value
4__delitem__(self, key) 定义删除容器中指定元素的行为,相当于del self[key]
5__iter__(self) 定义当迭代容器中元素的行为
6__contains__(self,item) 定义当使用成员测试运算符时的行为

练习:自定义一个链表结构

class Node(object):			#链表节点
    def __init__(self,val,p=None):
        self.data = val
        self.next = p

class LinkList(object):
    def __init__(self):
        self.head = None

    def __getitem__(self, key): 
        if self.is_empty():
            print ('linklist is empty.')
            return
        elif key <0  or key > self.getlength():
            print ('the given key is error')
            return
        else:
            return self.getitem(key)

    def __setitem__(self, key, value):
        if self.is_empty():
            print ('linklist is empty.')
            return
        elif key <0  or key > self.getlength():
            print ('the given key is error')
            return
        else:
            self.delete(key)
            return self.insert(key)

    def initlist(self,data):	#集体初始化
        self.head = Node(data[0])
        p = self.head
        for i in data[1:]:
            node = Node(i)
            p.next = node
            p = p.next

    def getlength(self):		#获得长度
        p =  self.head
        length = 0
        while p!=None:
            length+=1
            p = p.next
        return length

    def is_empty(self):			#是否为空
        if self.head==None:
            return True
        else:
            return False

    def clear(self):			#清空
        self.head = None

    def append(self,item):		#增加节点
        q = Node(item)
        if self.head ==None:
            self.head = q
        else:
            p = self.head
            while p.next!=None:
                p = p.next
            p.next = q

    def insert(self,index,item):
        if index >self.getlength():
            print ('OutOfRange.')
            return
        if index ==0:
            q = Node(item,self.head)
            self.head = q
        else:
            p = self.head
            post  = self.head
            j = 0
            while p.next!=None and j<index:
                post = p
                p = p.next
                j+=1
            if index ==j:
                q = Node(item,p)
                post.next = q
                q.next = p

    def delete(self,index):
        if (self.head==None or index<0 or index >self.getlength()):
            print ('Wrong.')
            return
        if index ==0:
            q=self.head.next
            self.head = q
        p = self.head
        post  = self.head
        j = 0
        while p!=None and p.next!=None and j<index:
            post = p
            p = p.next
            j+=1
            if index ==j:
                post.next = p.next
                break

    def index(self,value):
        if self.is_empty():
            print ('Linklist is empty.')
            return
        p = self.head
        i = 0
        while p.next!=None and not p.data ==value:
            p = p.next
            i+=1
        if p.data == value:
            return i
        else:
            return -1

七、迭代器

1.迭代器介绍

迭代是是访问集合元素的一种方式。
迭代器是一个可以记住遍历的位置的对象。
迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
迭代器有两个基本的方法:iter() 和 next()
字符串,列表或元组对象都可用于创建迭代器

>>> list=[1,2,3,4]
>>> it = iter(list)    # 创建迭代器对象
>>> print (next(it))   # 输出迭代器的下一个元素
1
>>> print (next(it))
2

StopIteration 异常用于标识迭代的完成,当被迭代的容器内所有元素都被迭代完了后,继续调用next()会爆出StopIteration异常

list=[1,2,3,4]
it = iter(list) 
while True:
    try:
        print (next(it))
    except StopIteration:
        print('stop')
        break
'''
1
2
3
4
stop
'''

2.创建迭代器

next() 方法中我们可以设置在完成指定循环次数后触发 StopIteration 异常来结束迭代。

class Fibs():
    def __init__(self, n=10):
        self.a = 0
        self.b = 1
        self.n = n

    def __iter__(self):
        return self

    def __next__(self):
        self.a, self.b = self.b, self.a + self.b
        if self.a > self.n:
            raise StopIteration
        return self.a

fibs = Fibs(100)
for each in fibs:
    print(each)
'''
1
1
2
3
5
8
13
21
34
55
89
'''

扩展:生成器

在 Python 中,使用了 yield 的函数被称为生成器(generator)。
跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。
在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。
调用一个生成器函数,返回的是一个迭代器对象。
同样迭代Fibonacci数列:

def fibonacci(n): 
    a, b = 0, 1
    while True:
    	if a>n:	return
        a, b = b, a + b
        yield a
        
fibs = Fibs(100)
for each in fibs:
    print(each,end=' ')
#1 1 2 3 5 8 13 21 34 55 89

参考视频:
[小甲鱼]零基础入门学习Python-bilibili

你可能感兴趣的:(python基础)