第041讲:魔法方法:构造和析构

版权声明:转载请标明出处 https://blog.csdn.net/qq_41556318/article/details/84638121

目录

0. 请写下这一节课你学习到的内容:格式不限,回忆并复述是加强记忆的好方式!

构造与析构

测试题

0. 是哪个特征让我们一眼就能认出这货是魔法方法?

1. 类实例化对象所调用的第一个方法是什么?

2. 什么时候我们需要在类中明确写出 __init__ 方法?

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

4. 请问 __new__ 方法是负责什么任务?

5. __del__ 魔法方法什么时候会被自动调用?

动动手

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魔法方法详解

猜你喜欢

转载自blog.csdn.net/qq_41556318/article/details/84638121