• 【Python】第十课 魔法方法


    10.1 构造和析构

    1._init_(self[……])

    该方法之前已经运用过了,在类中用于创建对象时初始化数据,给全局变量进行快速赋值的优点。

    1. # 定义一个矩形类,长和宽作为全局变量,计算周长和面积
    2. class Rectangle:
    3. # 构造方法,用于创建对象的时候,初始化长和宽的值
    4. def __init__(self,x,y):
    5. self.x=x
    6. self.y=y
    7. # 用于计算长方形的周长
    8. def getPeri(self):
    9. return (self.x+self.y)*2
    10. # 用于计算长方形的面积
    11. def getArea(self):
    12. return self.x * self.y
    13. # 运行
    14. rect=Rectangle(10,20)
    15. print("长方形的周长为:",rect.getPeri())#长方形的周长为: 60
    16. print("长方形的面积为:",rect.getArea())#长方形的面积为: 200

    这里需要注意构造方法是没有返回值的,它的返回值一定是None,所以一般在需要进行初始化的时候才重写该方法,但在python中这个方法并不是创建对象的时候第一个被调用的魔法方法。

    2._new_(cls[……])

    该方法才是创建一个对象的时候第一个被执行的方法,它和其他方法不一样,它的第一个参数不再是self,而是cls,该方法是一定需要有返回值,返回值为一个对象。一般情况下该方法很少会被重写,但当继承一个不可变的类型的时候,就需要该方法来完成功能。

    1. # 定义一个类继承至字符串类,作为改变字符串字母为大写的功能
    2. class Capstr(str):
    3. def __new__(cls, string):
    4. # 将该字符串转换为大写
    5. string=string.upper()
    6. # 将转换后的字符串提交给字符串类
    7. return str.__new__(cls,string)
    8. # 执行代码
    9. a=Capstr("how do you do?")
    10. print(a)#HOW DO YOU DO?

    3._del_(self)

    该方法是在对象被销毁的时候,自动调用执行。

    1. class C:
    2. def __init__(self):
    3. print("我是构造方法,我用于初始化数据")
    4. def __del__(self):
    5. print("我是销毁的方法,当该类的对象被销毁时执行")
    6. # 运行
    7. c=C()#我是构造方法,我用于初始化数据
    8. c1=C()#我是构造方法,我用于初始化数据
    9. del c#我是销毁的方法,当该类的对象被销毁时执行
    10. del c1#我是销毁的方法,当该类的对象被销毁时执行

    10.2 算术运算

    1. # 算术运算
    2. #查看以下几个是内建函数还是类
    3. print(type(len))
    4. #
    5. print(type(int))
    6. #
    7. print(type(dir))
    8. #
    9. print(type(list))
    10. #

    通过上面的输出可以发现len和dir是内建函数,而int和list其实是类,那么说明之前说讲的类型之间的转换其实是创建了一个这个类的对象,并把值传入给构造方法,例如:

    1. # 类型转换
    2. # 创建了一个int类的对象,把字符串120作为参数传入给init构造方法,得到对象a
    3. a=int("120")
    4. # # 创建了一个int类的对象,把浮点数3.14作为参数传入给init构造方法,得到对象b
    5. b=int(3.14)
    6. # 将两个对象做加法运算,得到结果为123
    7. print(a+b)

    讲到这里你会发现其实python中对象无处不在。int(),float(),str(),list(),tuple(),dict()等等都是表示创建对象。

    以下为算术运算相关的魔法方法

    魔法方法含义
    __add__(self,other)定义加法的行为:+
    __sub__(self,other)定义减法的行为:-
    __mul__(self,other)定义乘法的行为:*
    __truediv__(self,other)定义真除法的行为:/
    __floordiv__(self,other)定义整数除法的行为://
    __mod__(self,other)定义取模算法的行为:%
    __divmod__(self,other)定义当被divmod()调用时的行为
    __pow__(self,other[,modulo])定义当被pow()调用或**运算时的行为
    __lshift__(self,other)定义按位左移位的行为<<
    __rshift__(self,other)定义按位右移位的行为:>>
    __and__(self,other)定义按位与操作的行为:&
    __xor__(self,other)定义按位异或的行为: ^
    __or__(self,other)定义按位或操作的行为: |

    举例,定义一个int的子类,将加法变减法,减法变加法

    1. # 加减互换
    2. class New_int(int):
    3. # 在该类对象做加法的时候执行该方法,其内部实际做的是减法
    4. def __add__(self, other):
    5. return int.__sub__(self,other)
    6. # 在该类对象做减法的时候执行该方法,其内部实际做的是加法
    7. def __sub__(self, other):
    8. return int.__add__(self,other)
    9. # 运行
    10. # 将字符串3转换成整型
    11. x=New_int("3")
    12. #将字符串5转换成整型
    13. y=New_int("5")
    14. # 对象x调用加法魔法方法把y作为参数传入
    15. print(x + y)# 可理解为x.__add__(y) 因此 3.add(5)实际是 3-5=-2
    16. print(x - y)# 可理解为x.__sub__(y) 因此 3.sub(5)实际是 3+5=8
    17. print(y + x)# 可理解为y.__add__(x) 因此 5.add(3)实际是 5-3=2
    18. print(y - x)# 可理解为y.__sub__(x) 因此 5.sub(3)实际是 5+3=8

    课堂练习:大家可以练习一下乘法,除法互换,练一练。

    以下为反运算的常用魔法方法

    魔法方法含义
    __radd__(self,other)定义加法的行为:+
    __rsub__(self,other)定义减法的行为:-
    __rmul__(self,other)定义乘法的行为:*
    __rtruediv__(self,other)定义真除法的行为:/
    __rfloordiv__(self,other)定义整数除法的行为://
    __rmod__(self,other)定义取模算法的行为:%
    __rdivmod__(self,other)定义当被divmod()调用时的行为
    __rpow__(self,other[,modulo])定义当被pow()调用或**运算时的行为
    __rlshift__(self,other)定义按位左移位的行为<<
    __rrshift__(self,other)定义按位右移位的行为:>>
    __rand__(self,other)定义按位与操作的行为:&
    __rxor__(self,other)定义按位异或的行为: ^
    __ror__(self,other)定义按位或操作的行为: |

    那什么是反运算,我们在做a+b运算的时候,实际是a对象调用add方法去和b对象做运算,那么如果前者不是对象是纯数字,例如3+b,这种情况,前者不是对象,也就不存在add方法,而后者是有add方法的,那么python就会调用后者的radd方法进行执行运算,这种称为反运算,可以理解为后项驱动运算。

    1. # 反运算
    2. class Nint(int):
    3. def __radd__(self, other):
    4. return int.__sub__(self,other)
    5. # 运行
    6. z=Nint(10)
    7. print(4+z)#前者不是对象,不具备add方法,则后者调用radd方法执行
    8. # 结果应该是 10-4=6

    Python还支持一元操作符的魔方方法:__neg__()表示正号行为,__pos__()表示负号行为,__abs__()表示绝对值行为,__invert__()表示按位取反行为。

    10.3 属性访问

    在上一章中,我们讲过一些内置函数操作对象的全局变量

    1. class A:
    2. def __init__(self):
    3. self.x="XXX"
    4. # 运行
    5. a=A()
    6. print(a.x)
    7. print(getattr(a,"x"))
    8. print(getattr(a,"y","不存在该变量"))
    9. # 动态创建全局变量
    10. setattr(a,"y","YYY")
    11. print(getattr(a,"y","不存在该变量"))
    12. delattr(a,"y")

    以上这些方法也对应着魔法方法的触发

    魔法方法含义
    __getattr__(self,name)定义当用户试图获取一个不存在的属性时的行为
    __getattribute__(self,name)定义当该类的属性被访问时的行为
    __setattr__(self,name,value)定义当一个属性被设置时的行为
    __delattr__(self,name)定义当一个属性被删除时的行为
    1. class E:
    2. def __init__(self):
    3. self.x=100
    4. def __getattribute__(self, item):
    5. print("该类的属性正在被访问……")
    6. return super().__getattribute__(item)
    7. def __getattr__(self, item):
    8. print("该类的属性正在被要求获取输出数据")
    9. def __setattr__(self, key, value):
    10. print("正在给该类的属性进行赋值")
    11. return super().__setattr__(key,value)
    12. def __delattr__(self, item):
    13. print("正在删除该类的属性变量")
    14. return super().__delattr__(item)
    15. e=E()#创建对象时,会执行init方法,该方法中给x变量赋值,这时候会触发setattr这个魔法方法
    16. print(e.x)#通过对象调用x这个全局变量,触发getattribute这个魔法方法,变量被访问
    17. #运行结果
    18. #正在给该类的属性进行赋值
    19. #该类的属性正在被访问……
    20. #100
    21. print(getattr(e,"x"))#通过getattr方法获得值,触发getattribute这个魔法方法
    22. #该类的属性正在被访问……
    23. #100
    24. print(getattr(e,"y","不存在"))#通过getattr方法获得不存在的变量的值,
    25. #该类的属性正在被访问……
    26. #该类的属性正在被要求获取输出数据
    27. #不存在
    28. setattr(e,"y",200)
    29. #正在给该类的属性进行赋值
    30. print(getattr(e,"y","不存在"))
    31. #该类的属性正在被访问……
    32. #200
    33. delattr(e,"y")
    34. #正在删除该类的属性变量

    10.4 描述符(property的原理)

    python提供了一下三个魔法方法,用来监听get,set,del方法的调用。

    魔法方法含义
    __get__(self,instance,owner)用于访问属性,他返回属性的值
    __set__(self,instance,value)将在属性分配操作中调用,不返回任何内容
    __del__(self,instance)控制删除操作,不返回任何内容
    1. class My:
    2. def __get__(self, instance, owner):
    3. print("getting……")
    4. def __set__(self, instance, value):
    5. print("setting……")
    6. def __delete__(self, instance):
    7. print("deling……")
    8. class Test:
    9. #将My类的对象作为测试类的全局变量
    10. x=My()
    11. # 运行测试
    12. t=Test()
    13. #给My类进行赋值,被set魔法方法监听触发执行
    14. t.x="test" #setting……
    15. print(t.x)#获得my类的值,被get魔法方法监听触发执行
    16. del t.x#删除my类对象,被delete魔法方法监听触发执行

     接下来我们自定义Property函数:

    1. # 自定义Property函数
    2. class MyProperty:
    3. def __init__(self,fget=None,fset=None,fdel=None):
    4. #将getX方法赋予fget变量
    5. self.fget=fget
    6. #将setX方法赋予fset变量
    7. self.fset=fset
    8. #将delX方法赋予fdel变量
    9. self.fdel=fdel
    10. #也就是说这里的三个全局变量其实是三个方法
    11. #那么当把该类对象当做属性来操作时,就会触发以下三个方法,则在方法中调用C类的三个方法,间接操作C类的私有变量
    12. def __get__(self, instance, owner):
    13. return self.fget(instance)
    14. def __set__(self, instance, value):
    15. self.fset(instance,value)
    16. def __delete__(self, instance):
    17. self.fdel(instance)
    18. class C:
    19. # 定义构造方法
    20. def __init__(self):
    21. # 初始化一个私有变量
    22. self._x=None
    23. # 定义获得变量值的方法
    24. def getX(self):
    25. return self._x
    26. # 定义给变量赋值的方法
    27. def setX(self,value):
    28. self._x=value
    29. # 定义删除变量的方法
    30. def delX(self):
    31. del self._x
    32. # 将以上三个方法作为参数传入MyProperty类中作为参数
    33. x=MyProperty(getX,setX,delX)
    34. # 运行测试
    35. c=C()
    36. # x变量就是MyProperty类的对象
    37. # 把hello赋值给x,也就是赋值给MyProperty类,则会触发set方法,则set方法中把接收到的hello又赋值给了C类中的私有变量_x
    38. c.x="hello"
    39. # 获得x变量,其实是获得MyProperty类的对象,则会触发get魔法方法,即调用getX方法,把_x的值输出
    40. print(c.x)
    41. #删除x变量,其实是删除MyProperty类的对象,则会触发delete魔法方法,即调用delX方法,把_x的变量删除
    42. del c.x
    43. # 测试x是否还存在
    44. print(c.x)
    45. # 通过输出结果可以看出,确实是把_x这个私有变量删除了
    46. # AttributeError: 'C' object has no attribute '_x'

    10.5 自定义序列容器

    在python中,序列类型(类表,元组,字符串)或者映射类型(字典)都是属于容器,本节我们来自定义容器。

    以下是自定义容器的相关魔法方法

    魔法方法含义
    __len__(self)定义当len()函数被调用的时候,触发该魔法方法
    __getitem__(self,key)当根据索引值或者key获得容器中的数据时触发
    __setitem__(self,key,value)当根据索引值或者key赋值时触发
    __delitem__(self,key)删除指定索引值或者key的数据时触发
    __iter__(self)将容器做迭代的时候触发
    __reversed__(self)当reversed()函数被执行时触发
    __contains__(self,item)当使用in或者not in判断容器中是否存在某个元素时触发

    案例:编写一个自定义列表,要求记录列表中每个元素被访问的次数

    1. # 自定义列表,要求记录列表中每个元素被访问的次数
    2. class CountList:
    3. # 定义构造方法,将元组数据进行保存至列表格式
    4. def __init__(self,*args):
    5. # 将传入进来的元组做循环获得里面的每一个元素存储至列表中
    6. self.value=[x for x in args]
    7. # 根据value列表的长度个数来填充初始化一个字典,每一个元素都用0填充
    8. self.count={}.fromkeys(range(len(self.value)),0)
    9. # 重写获得长度的魔法方法,将列表的长度进行返回
    10. def __len__(self):
    11. return len(self.value)
    12. #重写通过索引值获得某个元素时,触发该魔法方法
    13. def __getitem__(self, item):
    14. # 根据当前索引值得知哪个元素被访问,则把该元素对应在count字典中的元素中加一,记录该元素被访问了一次
    15. self.count[item]+=1
    16. # 并根据索引值从列表中将对应的元素返回
    17. return self.value[item]
    18. # 运行测试
    19. cl1=CountList(1,3,5,7,9)
    20. cl2=CountList(2,4,6,8,10)
    21. # 输出索引值为1的元素,同时记录被访问次数
    22. print(cl1[1])#3
    23. print(cl2[3])#8
    24. print(cl1[1]+cl2[3])#11
    25. # 以上cl1列表索引值为1的元素被访问了两次,一次输出,一次加法
    26. # 以上cl2列表索引值为3的元素被访问了两次,一次输出,一次加法
    27. print("输出列表中各元素的被访问次数:",cl1.count)
    28. #输出列表中各元素的被访问次数: {0: 0, 1: 2, 2: 0, 3: 0, 4: 0}
    29. print("输出列表中各元素的被访问次数:",cl2.count)
    30. #输出列表中各元素的被访问次数: {0: 0, 1: 0, 2: 0, 3: 2, 4: 0}

    10.6 迭代器

    迭代的意思类似于循环,每一次重复的过程被称为一次迭代的过程,而每一次爹地啊得到的结果会被用来作为下一次迭代的初始值。例如:

    1. for i in range(10):
    2. print(i,end=" ")
    3. #运行结果:0 1 2 3 4 5 6 7 8 9

    列表,元组,字符串,字典和集合都是带有迭代器的

    1. links={"百度":"www.baidu.com","京东":"www.jd.com","淘宝":"www.taobao,com"}
    2. for key in links:
    3. print("%s -> %s" % (key,links[key]))
    4. #运行结果:
    5. 百度 -> www.baidu.com
    6. 京东 -> www.jd.com
    7. 淘宝 -> www.taobao,com

    接下来介绍两个内建函数:iter()和next()

    iter():任何容器对象调用它可以获得迭代对象

    next():被调用时获得下一个元素值

    当元素不存在的时候,会产生StopIteration异常

    例如:

    1. numbers=(1,11,12,23,24,34,45,56)
    2. it=iter(numbers)
    3. print(next(it))#1
    4. print(next(it))#11
    5. print(next(it))#12
    6. print(next(it))#23
    7. print(next(it))#24

    因此,以上案例可以简化一下

    1. numbers=(1,11,12,23,24,34,45,56)
    2. it=iter(numbers)
    3. while True:
    4. try:
    5. n=next(it)
    6. except StopIteration:
    7. break
    8. print(n)

    以上代码中,建立while循环执行next方法获得元组中的迭代元素,直到产生StopIteration异常,表示迭代结束,则通过捕获异常来停止while循环,并每一次循环都输出迭代的元素。

    那么以上两个内建函数有两个对应的魔法方法:

    __iter()__和__next__()

    例如:

    1. class Filb:
    2. def __init__(self):
    3. self.x=0
    4. self.y=1
    5. def __iter__(self):
    6. return self
    7. def __next__(self):
    8. #每一次执行该方法,将x和y进行运算,最后返回x变量的值
    9. self.x,self.y=self.y,self.x+self.y
    10. return self.x
    11. # 运行
    12. # 调用执行构造方法,初始化x和y变量
    13. f=Filb()
    14. # 循环Filb类,则自动会调用iter魔法方法,返回当前类对象
    15. for i in f:
    16. # 输出i,其实等价于执行了next()内建函数,因此每一次循环都会触发next魔法方法
    17. # 将计算后的x变量的值赋值给i,再判断i是否小于20,计算结果超过20则停止迭代循环
    18. if i<20:
    19. print(i,end="")
    20. else:
    21. break
    22. 运行结果:
    23. 1 1 2 3 5 8 13

    如果想让运行测试的代码更简便,我们将封装的类进行升级一下,什么时候停止循环的判断和判断条件封装起来

    1. class Filb:
    2. def __init__(self,total):
    3. self.total=total
    4. self.x=0
    5. self.y=1
    6. def __iter__(self):
    7. return self
    8. def __next__(self):
    9. # 如果不超出则进行计算,并返回x的值
    10. self.x, self.y = self.y, self.x + self.y
    11. #判断每一次迭代后,x的值是否会超出上线值
    12. # 如果超出上线,则抛出异常
    13. if self.x > self.total:
    14. raise StopIteration
    15. return self.x
    16. # 运行
    17. f=Filb(20)
    18. for i in f:
    19. print(i,end=" ")

    10.7 生成器

    生成器是迭代器的一个特殊的用法,比迭代器更简洁,运用更方便。需要使用yield关键字

    1. # 生成器的运用
    2. def myGen():
    3. print("生成器被执行!!!")
    4. yield 1
    5. yield 2
    6. # 该方法就是迭代对象
    7. m=myGen()
    8. # 直接使用next方法获得迭代对象中的元素
    9. print(next(m))
    10. print(next(m))
    11. # 使用循环执行迭代器
    12. for i in myGen():
    13. print(i)

    使用生成器完成斐波那契数列的实现

    1. def fbnq():
    2. a=0
    3. b=1
    4. while True:
    5. a,b=b,a+b
    6. yield a
    7. # 执行该方法
    8. for x in fbnq():
    9. if x>100:
    10. break
    11. print(x)

    使用类来封装以上方法,进行简化测试的过程

    1. # 定义斐波那契数列类
    2. class FbnqC:
    3. def __init__(self,sum):
    4. self.sum=sum
    5. # 生成器迭代
    6. def fbnq(self):
    7. a = 0
    8. b = 1
    9. while True:
    10. a, b = b, a + b
    11. yield a
    12. #运行测试
    13. def test(self):
    14. for x in fbnq():
    15. if x > self.sum:
    16. break
    17. print(x)
    18. f=FbnqC(1000)
    19. f.test()

    1.列表推导式:求出100以内能被2整除,但不能被3整除的所有数:

    1. # 求出100以内能被2整除,但不能被3整除的所有数
    2. a=[ i for i in range(100) if i % 2==0 and i %3!=0]
    3. print(a)

    简化一下

    1. # 求出100以内能被2整除,但不能被3整除的所有数
    2. a=[ i for i in range(100) if not(i % 2) and i %3]
    3. print(a)

    2.字典推导式:输出10以内,偶数输出True,奇数输出False

    1. # 输出10以内,偶数输出True,奇数输出False
    2. b={i:i%2==0 for i in range(10) }
    3. print(b)

    3.集合推导式:去除重复

    1. c={ i for i in [1,1,2,3,3,4,5,5,5,6,6,7,7,8] }
    2. print(c)

  • 相关阅读:
    壳聚糖修饰的雷公藤多苷聚乳酸纳米粒(LMWC-TG-PLA-NPs)齐岳规格信息
    Linux-MySQL数据库之高级SQL 语句二
    AsynchronousSocketChannel http server 接收浏览器请求 java代码 并获取浏览器请求路径
    基于RxJava框架开发的新闻APP设计与实现
    MariaDB MaxScale实现mysql8读写分离
    Nacos服务注册
    从零开始写 Docker(七)---实现 mydocker commit 打包容器成镜像
    LVS负载均衡集群
    第十六章 品质保证:发布覆盖率测试报告
    leetcode:762. 二进制表示中质数个计算置位
  • 原文地址:https://blog.csdn.net/u010321564/article/details/126623907