开发者学堂课程【Python 入门 2020年版:__init__&__del__&__str__&__repr__&__call__魔法介绍 】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/639/detail/10370
__init__&__del__&__str__&__repr__&__call__魔法介绍
内容介绍:
一、魔法方法简介
二、__init__& 方法
三、__del__方法
四、__str__方法和__repr__方法
五、__call__方法
一、魔法方法简介
魔法方法,也叫魔术方法,是类里的特殊的一些方法。
特点:
1. 不需要手动调用,会在合适的时机自动调用。而合适的时机是需要掌握的,它是不需要手动调用的但如果需要可以是手动调用的,一般不会手动调用,在后面的学习中会写到手动调用。
2.这些方法,都是使用__开始,使用__结束。
3.方法名都是系统规定好的,在合适的时机自动调用。就是这个名字得写好,写好才能在合适的时机自动调用,不可以瞎写
二、__init__& 方法
注意:
1._ init _() 方法在创建对象时,会默认被调用,不需要手动的调用这个方法。
2._ init _() 方法里的 self 参数,在创建对象时不需要传递参数,python 解释器会把创建好的对象引用直接赋值给 self
3.在类的内部,可以使用 self 来使用属性和调用方法;在类的外部,需要使用对象名来使用属性和调用方法法。
4.如果有多个对象,每个对象的属性是各自保存的,都有各自独立的地址。
5.方法是所有对象共享的,只占用一份内存空间,方法被调用时会通过 sell 来判断是哪个对象调用了实例方法。
新建一个 python 文件命名为 08- 魔法方法,书写代码。
在创建对象时,会自动调用这个__init__方法
以下为代码内容:
class Person(object):
def __init__(self,name,age):
print(‘__init__方法被调运了’)
self.name = name
self.age = age
p = Person(‘zhangsan’,18)
运行结果为:
结果可以看到这个函数被调用后得到的结果
三、__del__方法
有创建对象的就会存在删除对象的函数,就是__del__方法。
在对象被销毁时,会自动调用这个__def__方法
创建对象后,python 解释器默认调用_ init _()方法;
而当删除对象时,python 解释器也会默认调用一个方法,这个方法为_ de l_() 方法。
以下为代码内容:
class Person(object):
def __init__(self,name,age):
print(‘__init__方法被调运了’)
self.name = name
self.age = age
def __del__(self):
print(‘__del__方法被调运了’)
p = Person(‘zhangsan’,18)
运行结果为:
结果可以看到这两个函数都被调用
此时并没有删除对象,但依旧被销毁了。原因是,程序结束了,它申请的所有内存是都已经结束,它自己会终结自己。向代码中最后加入 time.sleep() 函数(注意导包)进行休息。此时代码整体修改为
import time
class Person(object):
def __init__(self,name,age):
print(‘__init__方法被调运了’)
self.name = name
self.age = age
def __del__(self):
print(‘__del__方法被调运了’)
p = Person(‘zhangsan’,18)
time.sleep(10)
运行结果为:
刚开始运行后结果为下图
等待十秒后,程序结束。会自动删掉新对象。
也可以选择手动销毁,在创建的对象后面,直接加入语句 delp。创建对象语句执行完后会立即销毁,会调用销毁函数完成。
代码整体为:
import time
class Person(object):
def __init__(self,name,age):
print(‘__init__方法被调运了’)
self.name = name
self.age = age
def __del__(self):
print(‘__del__方法被调运了’)
p = Person(‘zhangsan’,18)
del p
time.sleep(10)
运行结果为:
创建之后立即销毁,此时程序还未结束。
等待十秒完成 sleep 函数之后,程序结束。
__del__方法就是销毁对象时会调用,__init__方法用的较多
四、__str__方法和__repr__方法
(1) __str__方法返回对象的描述信息,使用 print() 函数打印对象时,其实调用的就是这个对象的__str__方法。
先打印 p,加入语句 print(p)。如果不作任何修改,直接打印一个对象,是对象的类型以及内存地址,准确的来说,是对象得模块名.类型。更加贴切得来讲是文件的__name__.类型 内存地址。
代码内容为:
class Person(object):
def __init__(self,name,age):
print(‘__init__方法被调运了’)
self.name = name
self.age = age
def __del__(self):
print(‘__del__方法被调运了’)
p = Person(‘zhangsan’,18)
print(p)
运行结果为:
会打印出 p 的类型是 Person 类,以及会打印出对应得内存地址。
__main__是这个 p 得模块名,直接运行就是__main__
在开发的时候,大多数打印这个对象并不是为了打印出它的文件类型和内存地址。
例如想打印 p,但要求是输出的是这个人的名字以及年龄,可以通过使用__str__或者__repr__方法来改变打印出来的结果。语句 print(p.name,p.age)
是可以的,但在这用另一种方式。
(2) 当打印一个对象的时候,会调用这个对象的__str__或者__repr__方法
代码内容为:
class Person(object):
def __init__(self,name,age):
print(‘__init__方法被调运了’)
self.name = name
self.age = age
def __del__(self):
print(‘__del__方法被调运了’)
def __repr__(self)
return ‘hello’
p = Person(‘zhangsan’,18)
print(p)
运行结果为:
发现要求打印的是 p,但打印出来的是 hello ,在这里是 p 自动调用了__repr__方法并打印出了函数返回值内容。
(3)除了调用__repe__,还可以是__str__方法,加入函数。
代码内容为:
class Person(object):
def __init__(self,name,age):
print(‘__init__方法被调运了’)
self.name = name
运行结果为:
此时,并没有运行__repr__函数,说明当两个函数都存在时,只运行__str__函数。
有一个内置函数名为 repr()
加入语句 print(repr({‘name’:’zhangsan’,’age’:18}))
这个语句本身是一个字典,加入 repr 语句中就会变为一串字符串“{‘name’:’zhangsan’,’age’:18}“。
repr 语句中放入的字典,它本质上就是找字典的下划线__repr__方法。
(4)现在如果想调用 repr 的方法可以按下面的讲解来完成。之前所说的是魔术方法属于自动调用方法,在这里可以手动调用方法,删掉刚刚的 repr 语句,加入 print(repr(p)) 语句
此时代码内容为:
class Person(object):
def __init__(self,name,age):
print(‘__init__方法被调运了’)
self.name = name
self.age = age
def __del__(self):
运行结果为:
结果中会调用 repr 方法得到 hello 结果。
大多数情况下,在 repr 语句中加入一个对象,就是代表找到这个对象的 repr 方法。
除了加入 print(repr(p))
的方式,还可以再加入 print(p.__repr__())
。
总之,魔法方法一般不直接调用,但是是可以直接调用的。在调用内置函数 repr 会触发对象的__repr__方法。一般情况下,选择__str__或者__repr__方法都可以。
在这个例子中要想打印出名字和年龄,就需要语句 return ‘姓名:{},年龄:{}’.format(self.name,self.age)
,继续选择打印 p。
整体代码变为:
class Person(object):
def __init__(self,name,age):
print(‘__init__方法被调运了’)
self.name = name
self.age = age
def __del__(self):
print(‘__del__方法被调运了’)
def __repr__(self)
return ‘姓名:{},年龄:{}’.format(self.name,self.age)
运行结果为:
可以书写__str__或者__repr__方法都可以。
(4)这两个方法的区别:
使用__str__和__repr__方法,都会修改一个对象转换成为字符串的结果。一般来说,__str__方法的结果更加在意可读性,而__repr__方法的结果更加在意正确性(例如: datetime 模块里的 datellime 类)
举例:加入语句
import datatime
datatime.datatime
点进后面的 datatime 进入发现,它是一个类,就可以创建它们,使用__init__来创建。
加入语句 x = datatime.datatime()
,通过点进了 datatime 中可以看到这个代码需要年、月、日、时、分、秒以及毫秒这些数据。在这里成 x = datatime.datatime(2020,2,24,26,17,45,200)
,并加入 print(x)
语句。
在 datatime 模块中,这个两种方法都写的有。print(x) 调用的时__str__方法,而 print(repr(x)) 调用的是__repr__方法。如果调用__str__方法它的可读性更好,更加方便。
但如果调用的是__repr__方法,它读出来的就更加精确。在写的过程中,两个方法选一个就好,选__str__方法或是__repr__方法都是可行的。也可以两个都写,在 datatime 中就是两个都写了。
五、__call__方法
p 是指一个对象,现在写成 p() ,小括号是当成函数来调用现在这个 p 就是个函数。
写 repr 可以直接调用,用手动调用,但直接打印是调用 str 方法
(1)直接加入代码 p()
源代码变为:
class Person(object):
def __init__(self,name,age):
print(‘__init__方法被调运了’)
self.age = age
运行结果如图:
可以看到会报错,它还不是函数,此时运行会崩掉。运行后的结果显示是不可以被调用的。
(2) 类似的如果把 p() 换成 a = ’hello’
和 a ()
语句,会显示有同样的错误。
代码更改后为:
class Person(object):
def __init__(self,name,age):
print(‘__init__方法被调运了’)
self.name = name
self.age = age
def __del__(self):
运行结果如下图:
会告知字符串是不可以被调用的
(3)函数是可以调用的,写一段函数,使 p 也可以被调用
增加以下语句:
def __call__(self,*args,**kwargs):
print(‘__call__方法被调用’)
p = Person(‘zhangsan’,18)
print(p)
p()
此时运行代码,结果如下图:
此时不会报错并且是可以调用到 call 方法
其实对象名 ()==> 调用这个对象的__call__方法,括号里还可以传参数,例如将代码修改为 p(1,2),此时在运行依旧不会报错。原因是写的 p.__call__(1,2) 方法,这个函数中写的都是可变参数。
将代码继续修改为 p(1,2,fn=lambda x,y:x+y)
,要想到处 lambda 数据需要以下步骤。
这个中的 1 和 2,有方法中可以知道,args 是一个元组,保存 (1,2),kwargs 是一个 字典,里面存的就是 {fn:lambda x,y:x+y}
(4) 加入语句
print(‘args={},kwargs={}’.format(args,kwargs))
(5)现在要想实现一个加法的操作,方法如下:
需要先拿到函数,利用语句 fn = kwargs[‘fn’]
在提取 args 当中的第零个、第一个,用语句 fn(args[0],args[1])
Fn
通过字典拿到函数,在传进去两个参数,参数从 args 中获取,最后再把获取到的值返回
class Person(object):
def __init__(self,name,age):
print(‘__init__方法被调运了’)
self.name = name
self.age = age
print(‘__del__方法被调运了’)
def __repr__(self)
return ‘hello’
def __str__(self)
return ‘姓名:{},年龄:{}’.format(self.name,self.age)
def __call__(self,*args,**kwargs):
print(‘args={},kwargs={}’.format(args,kwargs))
fn = kwargs[‘fn’]
return fn(args[0],args[1])
p = Person(‘zhangsan’,18)
print(p)
n = p(1,2,fn=lambda x,y:x+y)
print(n)
此时运行代码,结果如下图:
这些内容都是前面所提到过的,在这里进化了以下,变成了面向对象编程语法。
所传进去的 1、2 是放在了 args 的元组中,而传的关键字 lambda 会当成一个字典传给 kwargs,这个传进去是一个 fn,一个函数;调用的过程就是先通过字典拿到数据,在通过函数进行调用。两个参数都是从 args 中获取。
把对象当成一个函数调用这种写法是有的,但是不多,一般用在装饰器,以前的装饰器都是由函数作为。这代码中都是 fn 参数,它没有重名的问题。
为了更好的理解,将 fn 更改名字为 test,代码内容变为:
class Person(object):
def __init__(self,name,age):
print(‘__init__方法被调运了’)
self.name = name
self.age = age
def __del__(self):
print(‘__del__方法被调运了’)
def __repr__(self)
return ‘hello’
def __str__(self)
return ‘姓名:{},年龄:{}’.format(self.name,self.age)
def __call__(self,*args,**kwargs):
print(‘args={},kwargs={}’.format(args,kwargs))
test = kwargs[‘fn’]
return test(args[0],args[1])
p = Person(‘zhangsan’,18)
print(p)
n = p(1,2,fn=lambda x,y:x+y)
print(n)
这个是不具有重名的问题,当传过去的时候 fn 对应得是一个函数,它在这里面是一个字符串。
这个 fn 字典通过 key 获得 value 函数,其实也可以不需要加上函数名直接return kwargs[‘fn’](args[0],args[1])
。它就是一个拿到对应得函数进行调用得一个过程。
这些高级语言了解即可,把一个对象当作函数是可以调用的,可以传任意的参数,按照处理的不同方式来选择参数。逻辑是根据业务需求来写的,把一个对象当作函数是调用的语法关键是在于传参,以及之后的处理都是要根据业务逻辑来进行处理的。