第41~43讲:魔法方法——构造和析构、算术运算

一 相关知识

1 python中函数和方法

  • 函数:类外部定义的,跟类没有直接关系的;形式: def func(*argv):
  • 方法:class内部定义的函数(对象的方法也可以认为是属性);分为两种:
    • ① python自动产生的(魔法方法):一般形式为 __func__(),python会在对应的时机自动调用该函数;
    • ② 人为自定义的方法:一般和普通函数没有区别,只是定义在了class中而已
  • 方法与函数的区别:
    • 方法可认为是函数的特殊情况;
    • ① 方法定义在class内部
    • ② 方法的第一个参数应为 cls(类方法) 或者 self(实例方法)

2 什么是魔法方法

  • python对象自带的一些被双下划线包围的方法,我们称之为魔法方法。
  • python中的魔法方法像是java中的重载,即可以理解为对类中的内置方法的重载,注意这里不是重写。
  • 如果python对象重载了这些方法中的某一个,这些方法就会在特殊情况下被python调用,可以在重写过程中定义自己想要的行为,这一切都是自动触发的。
  • 魔法方法是python的内置方法,不需要主动调用,存在的目的是为了给python解释器进行调用。

3 常见的魔法方法

参见博客:https://www.cnblogs.com/Jimmy1988/p/6801795.html

也可参考:https://fishc.com.cn/forum.php?mod=viewthread&tid=48793&extra=page%3D1%26filter%3Dtypeid%26typeid%3D403

Python 魔法方法汇总
方法 描述 备注
1、基本方法
__new__(cls[,*argv])  1. __new__ 是在一个对象实例化的时候所调用的第一个方法
2. 它的第一个参数是这个类,其他的参数是用来直接传递给 __init__ 方法
3. __new__ 决定是否要使用该 __init__ 方法,因为 __new__ 可以调用其他类的构造方法或者直接返回别的实例对象来作为本类的实例,如果 __new__ 没有返回实例对象,则 __init__ 不会被调用
4. __new__ 主要是用于继承一个不可变的类型比如一个 tuple 或者 string(不可改变就不能在__init__方法内对类自身进行修改,所以可以通过__new__方法完成对该类自身的一个替换)

 cls:代表一个类的名称

self:代表一个实例对象的名称

 __init__(self[,*argv]) 构造器,当一个实例对象被定义时调用 类似于C++的构造函数
 __del__(self) 析构器,当删除一个实例对象时调用(所有对该实例的引用都被删除之后才会启动垃圾回收机制) 类似于C++的析构函数
 __call__(self[,*argv])  允许一个类像函数一样被调用   class_x(a,b)实际调用的是class_x.__call__(a,b) 
 __len__(self) 获得实例对象的长度  与调用函数 len(obj)一样的结果 
 __repr__(self) 将实例对象转化为字符串的形式

如 ls=[1,2,3], 则repr(ls)为 '[1,2,3]',与函数repr(obj)功能相同

 __str__(self)  将实例对象转化为字符串的形式

与repr()的区别在于:str(obj)的字符串是打印出来让人看的,更加亲民,而repr(obj)是给解释器看的;

若 a = xxx(列表、字典、元祖或集合等)

eval(repr(a)) == a  成立

eval(str(a)) == a    不一定成立

__int__(self) 定义当被 int() 调用时的行为  
__float__(self)  定义当被 float() 调用时的行为  
__round__(self[, n]) 当被round()调用时的行为 round(digit[, n]) 将digit数字保留n位精度
 __hash__(self) 定义能被 hash() 调用的行为   
 __bytes__(self)  定义被 bytes() 调用的行为  
 __bool__(self) 定义被 bool() 调用的行为 返回True(1) 或 False(0) 
 __format__(self, form) 定义被 format()调用的行为   
2、运算符方法
 __add__(self, other) 加法:+   
__sub__(self, other)  减法:-  
__mul__(self,other)  乘法:*   
__truediv(self, other) 除法:/ 注意是 truediv 
__floordiv(self, other) 整数除法://  floor()即为向下取整的意思
__mod__(self, other)  求余:%  
__pow__(self, other[, mod]) 乘方:** 

 pow(x,y[,z]),

若无Z,则为 return x**y

若有Z,则为 return x**y%z

__divmod__(self, other) divmode()  返回值为元祖  (商值,余数) 
__lshift__(self, other)  左移:<<  
__rshift__(self, other) 右移:>>   
__and__(self, other) 按位与:& 注意以下均为按位操作,非逻辑操作 
__or__(self, other) 按位或:|   
__xor__(self, other) 按位异或:^  
 3、反运算符方法  
 __radd__(self, other) 加法,如a+b,当a不支持__add__()操作时,调用此函数;  即在运算符的基础上加上 'r' 即可,以下雷同 
 __rsub__(self, other) other - self   
 …………    
 4、增量赋值运算符方法  
 __iadd__(self, other) 赋值加法:+=    即在赋值运算符之前加 'i' ,以下雷同 
__isub__(self, other)  赋值减法:-=  self = self - other 
…………     
5、一元操作符方法  
__pos__(self) 定义正号:+x  
__neg__(self) 定义负号:-x  
__abs__(self) 取绝对值  
__invert__(self) 按位求反:~x  
6、比较操作符方法  
__gt__(self, other) 大于:>  
__ge__(self, other) 大于等于:>=  
__lt__(self, other) 小于:<  
__le__(self, other) 小于等于:<=  
__eq__(self, other) 相等:==  
__ne__(self, other) 不等:!=  
7、属性操作  
__getattr__(self, name) 当用户访问一个不存在的属性时调用 注意 object/super() (所有类的基类) 是无该方法的
__getattribute(self, name) 访问存在的属性时调用 先调用此函数,如找不到该属性,再去调用上面的属性
__setattr__(self, name, value) 设置属性时调用  
__delattr__(self, name) 删除一个属性时调用  
property(fget=None, fset=None, fdel=None, doc=None) 是一个类,主要功能是为了方便类内部的函数调用
 1 class C(object):
 2     def getx(self): return self._x
 3     def setx(self, value): self._x = value
 4     def delx(self): del self._x
 5     x = property(getx, setx, delx, "I'm the 'x' property.")
 6 
 7     
 8 >>> c=C()
 9 >>> c.x=10
10 >>> c.x
11 10
举例
__get__(self, instance, owner) 描述符被访问时调用 想详细了解,请点击这里
 __set__(self, instance, value) 描述符被改变时调用  
  __delelte__(self, instance, value) 删除描述符时调用  
8、容器类型操作  
__len__(self)    求容器的大小(注意与capacity的区别) 可变和非尅便容器均具备 __len__ 和 __getitem__
__getitem__(self, key) 获取容器中指定元素的行为  
__setitem__(self, key, value) 设置容器中指定元素的行为  只有可变容器拥有 __setitem__ 和 __delitem__
__delitem__(self, key) 删除容器中指定元素的行为  
__iter__(self) 定义迭代器中元素的行为  
__reversed__(self) 当调用reversed()函数时  
__contains__(self, item) 成员运算符in/ not in的行为  
   

 PS: ①.以上所有的魔法方法,君采用__xx__形式(__为双 "_",双下划线)

   ②.以上魔法方法为Python解释器自动调用,当然也可以手动调用

   ③.魔法方法Python解释器自动给出默认的,因此除非需要改变其内部功能,其它时刻刻使用默认魔法方法

   ④.魔法方法是针对class而言的,脱离了”类“谈magic_method是没有意义的

   ⑤.*argv为可变的参数列表,类似C语言的va(variable argument),注意与指针的区别,python中暂时忘掉指针,因为python的内存机制都是解释器自动完成的

 

二 课后题

第41讲:

测试题部分:

0. 是哪个特征让我们一眼就能认出这货是魔法方法?
答:魔法方法总是被双下划线包围,例如 __init__

1. 类实例化对象所调用的第一个方法是什么?
答:__new__ 是在一个对象实例化的时候所调用的第一个方法。它跟其他魔法方法不同,它的第一个参数不是 self 而是这个类(cls),而其他的参数会直接传递给 __init__ 方法的。Sj

2. 什么时候我们需要在类中明确写出 __init__ 方法?
答:当我们的实例对象需要有明确的初始化步骤的时候,你可以在 __init__ 方法中部署初始化的代码。
举个例子:2iB78JYf

 1 # 我们定义一个矩形类,需要长和宽两个参数,拥有计算周长和面积两个方法。
 2 # 我们需要对象在初始化的时候拥有“长”和“宽”两个参数,因此我们需要重写__init__方法
 3 # 因为我们说过,__init__方法是类在实例化成对象的时候首先会调用的一个方法,大家可以理解吗?
 4 class Rectangle:
 5         def __init__(self, x, y):
 6                 self.x = x
 7                 self.y = y
 8         def getPeri(self):
 9                 return (self.x + self.y) * 2
10         def getArea(self):
11                 return self.x * self.y
12 >>> rect = Rectangle(3, 4)
13 >>> rect.getPeri()
14 14
15 >>> rect.getArea()
16 12

 

3. 请问下边代码存在什么问题?

1 class Test:
2         def __init__(self, x, y):
3                 return x + y

答:编程中需要主要到 __init__ 方法的返回值一定是None,不能是其它!

4. 请问 __new__ 方法是负责什么任务?
答:__new__ 方法主要任务时返回一个实例对象,通常是参数 cls 这个类的实例化对象,当然你也可以返回其他对象。

5. __del__ 魔法方法什么时候会被自动调用?
答:如果说 __init__ 和 __new__ 方法是对象的构造器的话,那么 Python 也提供了一个析构器,叫做 __del__ 方法。当对象将要被销毁的时候,这个方法就会被调用。}XV
但一定要注意的是,并非 del x 就相当于自动调用 x.__del__(),__del__ 方法是当垃圾回收机制回收这个对象的时候调用的。

 

动动手部分:

0. 小李做事常常丢三落四的,写代码也一样,常常打开了文件又忘记关闭。你能不能写一个 FileObject 类,给文件对象进行包装,从而确认在删除对象时文件能自动关闭?
答:只要灵活搭配 __init__ 和 __del__ 魔法方法,即可做到收放自如。
代码清单:`S6F;=p

1 class FileObject:
2     '''给文件对象进行包装从而确认在删除时文件流关闭'''
3     def __init__(self, filename='sample.txt'):
4         #读写模式打开一个文件
5         self.new_file = open(filename, 'r+')
6     def __del__(self):
7         self.new_file.close()
8         del self.new_file

 

1. 按照以下要求,定义一个类实现摄氏度到华氏度的转换(转换公式:华氏度 = 摄氏度*1.8+32)zcZ"1 8U
要求:我们希望这个类尽量简练地实现功能,如下

1 >>> print(C2F(32))
2 89.6

答:为了尽量简练地实现功能,我们采取了“偷龙转凤”的小技巧。在类进行初始化之前,通过“掉包” arg 参数,让实例对象直接返回计算后的结果。

代码清单:

1 class C2F(float):
2     "摄氏度转换为华氏度"
3     def __new__(cls, arg=0.0):
4         return float.__new__(cls, arg * 1.8 + 32)

 

2. 定义一个类继承于 int 类型,并实现一个特殊功能:当传入的参数是字符串的时候,返回该字符串中所有字符的 ASCII 码的和(使用 ord() 获得一个字符的 ASCII 码值)
实现如下:BIa?bi

1 >>> print(Nint(123))
2 123
3 >>> print(Nint(1.5))
4 1
5 >>> print(Nint('A'))
6 65
7 >>> print(Nint('FishC'))
8 461

代码清单:

1 class Nint(int):
2     def __new__(cls, arg=0):
3         if isinstance(arg, str):
4             total = 0
5             for each in arg:
6                 total += ord(each)
7             arg = total
8         return int.__new__(cls, arg)

 

posted @ 2020-08-26 21:55  洛兰123  阅读(250)  评论(0编辑  收藏  举报