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

目录

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'

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. 请问下边代码存在什么问题?

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

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

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

答:new 方法主要任务时返回一个实例对象,通常是参数 cls 这个类的实例化对象,当然你也可以返回其他对象。
__init__用于初始化
__new__用于生成

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

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

动动手

0. 小李做事常常丢三落四的,写代码也一样,常常打开了文件又忘记关闭。你能不能写一个 FileObject 类,给文件对象进行包装,从而确认在删除对象时文件能自动关闭?

答:只要灵活搭配 initdel 魔法方法,即可做到收放自如。

import easygui as g
class FileObject:
    
    def __init__(self,fileName):
        self.f=open(fileName)

    def __del__(self):
        self.f.close()
        del self 
f1=FileObject(g.fileopenbox())

1. 按照以下要求,定义一个类实现摄氏度到华氏度的转换(转换公式:华氏度 = 摄氏度*1.8+32)

要求:我们希望这个类尽量简练地实现功能,如下

>>> print(C2F(32))
89.6

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

class C2F(float):
        "摄氏度转换为华氏度"
        def __new__(cls, C=0.0):
                return float.__new__(cls, C * 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
class _int(int):
    def __new__(self,arg=''):
        if isinstance(arg,str):
            ans=0
            for i in arg:
                ans+=ord(i)
            return str.__new__(self,ans)
        elif isinstance(arg,int):
            ans=int(arg)
            return int.__new__(self,ans)
        
        else:
            try:
                raise TypeError
            except:
                print("参数类型错了")
        

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页