目录
0. 请写下这一节课你学习到的内容:格式不限,回忆并复述是加强记忆的好方式!
2. 什么时候我们需要在类中明确写出 __init__ 方法?
0. 小李做事常常丢三落四的,写代码也一样,常常打开了文件又忘记关闭。你能不能写一个 FileObject 类,给文件对象进行包装,从而确认在删除对象时文件能自动关闭?
1. 按照以下要求,定义一个类实现摄氏度到华氏度的转换(转换公式:华氏度 = 摄氏度*1.8+32)
2. 定义一个类继承于 int 类型,并实现一个特殊功能:当传入的参数是字符串的时候,返回该字符串中所有字符的 ASCII 码的和(使用 ord() 获得一个字符的 ASCII 码值)。
0. 请写下这一节课你学习到的内容:格式不限,回忆并复述是加强记忆的好方式!
我们接下来几节课的主要内容是魔法方法,此前我们已经接触过Python中最常用的魔法方法 __init__。
构造与析构
魔法方法总是被双下划线包围,例如__init__
魔法方法是面向对象的Python的一切,如果你不知道魔法方法,说明你还没能意识到面向对象的Python的强大
魔法方法的“魔力”体现在它们总能够在适当的时候被自动调用
(1)__init__(self[ , ...])
它就相当于其它面向对象的编程语言的构造方法,也就是类在实例化对象时首先会调用的一个方法,在前面关于类的课程中,有人会问:“有时候在类定义时写__init__方法,有时候却没有,这是为什么呢?”
这是因为“需求”,因为我们有时候需要重写__init__方法
我们在这里定义一个矩形(Retangle)类:
>>> class Retangle:
def __init__(self, x, y):
self.x = x
self.y = y
def getPeri(self):
return(self.x + self.y) * 2
def getArea(self):
return self.x * self.y
>>> rect = Retangle(3, 4)
>>> rect.getPeri()
14
>>> rect.getArea()
12
需要注意的是,__init__方法的返回值一定是None,不能试图在__init__方法中返回一个什么东西,以在实例化的时候返回给变量。
>>> class A:
def __init__(self):
return "try return"
>>> a = A()
Traceback (most recent call last):
File "<pyshell#18>", line 1, in <module>
a = A()
TypeError: __init__() should return None, not 'str'
一般我们在需要对一个对象进行初始化操作的时候,我们才会重写__init__方法。
(2)__new__(cls[, ...])
其实__init__方法并不是实例化对象时第一个被调用的魔法方法,第一个被调用的应该是__new__(cls[, ...])方法,它跟其他的魔法方法不同,第一个参数不是self,而是class,它在init之前被调用,它后边有参数的话,会原封不动的传给__init__方法,new方法需要一个实例对象作为返回值,需要返回一个对象,通常是返回cls这个类的实例对象,也可以返回其它类的对象,需要说明的是,new方法平时是极少去重写的,一般用Python默认的方案即可,但是用一种情况我们需要重写new魔法方法,就是继承一个不可变类型的时候,又需要进行些改的时候,那么它的特性就显得尤为重要了。
>>> class CapStr(str):
def __new__(cls, string):
string = string.upper()
return str.__new__(cls, string)
>>> a = CapStr("I love fichc")
>>> a
'I LOVE FICHC'
这里继承的父类 str 是不可修改的,我们就不能在init中对其自身进行修改,所以我们就需要在new的时候进行一个替换,然用替换后的调用str的new方法做一个返回。如果这里的new没有做重写的话,就会自动调用父类str的new。
(3)__del__(self)
如果我们说init和new方法是对象的构造器的话,那么Python也提供了一个析构器,就是del方法,当对象需要被销毁的时候,这个方法就会自动的被调用,但要注意的是,并非我们写 del x ,就会调用 x.__del__(),del方法是当垃圾回收机制,我们知道Python有一个垃圾回收机制,当没有任何变量去引用这个对象时,垃圾回收机制就会自动将其销毁,这时候才会调用对象的del方法。
>>> class C:
def __init__(self):
print("我是__init__方法,我被调用了")
def __del__(self):
print("我是__del__方法,我被调用了")
>>> c1 = C()
我是__init__方法,我被调用了
>>> c2 = c1
>>> del c1
>>> del c2
我是__del__方法,我被调用了
实例化的时候就会调用__init__方法,然后有c1 和 c2 两个变量指向这个对象,但并不是 del c1 或者 del c2 的时候就会调用__del__方法,而是当指向该对象的变量都被del的时候,才会被自动调用,来销毁这个对象。
测试题
0. 是哪个特征让我们一眼就能认出这货是魔法方法?
答:魔法方法总是被双下划线包围,例如 __init__<
1. 类实例化对象所调用的第一个方法是什么?
答:__new__ 是在一个对象实例化的时候所调用的第一个方法。它跟其他魔法方法不同,它的第一个参数不是 self 而是这个类(cls),而其他的参数会直接传递给 __init__ 方法的。
2. 什么时候我们需要在类中明确写出 __init__ 方法?
答:当我们的实例对象需要有明确的初始化步骤的时候,你可以在 __init__ 方法中部署初始化的代码。
举个例子:
# 我们定义一个矩形类,需要长和宽两个参数,拥有计算周长和面积两个方法。
# 我们需要对象在初始化的时候拥有“长”和“宽”两个参数,因此我们需要重写__init__方法
# 因为我们说过,__init__方法是类在实例化成对象的时候首先会调用的一个方法,大家可以理解吗?
class Rectangle:
def __init__(self, x, y):
self.x = x
self.y = y
def getPeri(self):
return (self.x + self.y) * 2
def getArea(self):
return self.x * self.y
>>> rect = Rectangle(3, 4)
>>> rect.getPeri()
14
>>> rect.getArea()
12
3. 请问下边代码存在什么问题?i
class Test:
def __init__(self, x, y):
return x + y
答:编程中需要主要到 __init__ 方法的返回值一定是None,不能是其它!
4. 请问 __new__ 方法是负责什么任务?
答:__new__ 方法主要任务时返回一个实例对象,通常是参数 cls 这个类的实例化对象,当然你也可以返回其他对象。
5. __del__ 魔法方法什么时候会被自动调用?
答:如果说 __init__ 和 __new__ 方法是对象的构造器的话,那么 Python 也提供了一个析构器,叫做 __del__ 方法。当对象将要被销毁的时候,这个方法就会被调用。
但一定要注意的是,并非 del x 就相当于自动调用 x.__del__(),__del__ 方法是当垃圾回收机制回收这个对象的时候调用的。
动动手
0. 小李做事常常丢三落四的,写代码也一样,常常打开了文件又忘记关闭。你能不能写一个 FileObject 类,给文件对象进行包装,从而确认在删除对象时文件能自动关闭?
答:只要灵活搭配 __init__ 和 __del__ 魔法方法,即可做到收放自如。
代码清单:
class FileObject:
'''给文件对象进行包装从而确认在删除时文件流关闭'''
def __init__(self, filename='sample.txt'):
#读写模式打开一个文件
self.new_file = open(filename, 'r+')
def __del__(self):
self.new_file.close()
del self.new_file
1. 按照以下要求,定义一个类实现摄氏度到华氏度的转换(转换公式:华氏度 = 摄氏度*1.8+32)
要求:我们希望这个类尽量简练地实现功能,如下
>>> print(C2F(32))
89.6
答:为了尽量简练地实现功能,我们采取了“偷龙转凤”的小技巧。在类进行初始化之前,通过“掉包” arg 参数,让实例对象直接返回计算后的结果。
代码清单:
class C2F(float):
"摄氏度转换为华氏度"
def __new__(cls, arg=0.0):
return float.__new__(cls, arg * 1.8 + 32)
2. 定义一个类继承于 int 类型,并实现一个特殊功能:当传入的参数是字符串的时候,返回该字符串中所有字符的 ASCII 码的和(使用 ord() 获得一个字符的 ASCII 码值)。
实现如下:
>>> print(Nint(123))
123
>>> print(Nint(1.5))
1
>>> print(Nint('A'))
65
>>> print(Nint('FishC'))
461
代码清单:
注意:ord()的参数只能是单字符串。
class Nint(int):
def __new__(cls, arg=0):
if isinstance(arg, str):
total = 0
for each in arg:
total += ord(each)
arg = total
return int.__new__(cls, arg)
另有不明白的,可参阅->Python魔法方法详解