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

mac2026-04-25  7

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

一、构造与析构

1.构造函数

通俗的讲解Python中的__new__()方法 - SJ2050的博客 - 博客 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.自定义运算

可以自定义方法对对象进行算术运算 主要方法如下: 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.一元运算符和类型转换

如下表,也可自定义

三、输出

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> <class '__main__.TestDesc'> 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的实例对象,其实就是towner: 即谁拥有这些东西,当然是 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] = value4__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

最新回复(0)