python面向对象之魔术方法
前言
相信很多使用python的小伙伴都有一个困惑,在看一些库的源码时,发现源码中有很多 __XX__
(双下划线开头,双下划线结尾)的方法。比如我们在定义类时,经常用到的初始化方法__init__
,在python中像__init __
这类双下划线开头和结尾的方法,我们把它统称为魔术方法(也有叫魔法方法和特殊方法的)。 今天就专门和大家一起来聊聊python中的魔术方法,首先我们来看看魔术方法有哪些特征。
-
魔术方法的特征:
-
魔术方法都是双下划线开头,双下划线结尾的方法
-
魔术方法都是python内部事先定义的,是对象相关行为的底层实现方法
-
魔术方法都是在特定的情况下自动化触发的,一般不会直接去调用。
-
接下来我们一起看看python中场景的一些魔术方法。
1、__new__
方法
相信大多的程序员,都听说过 new一个对象
这句话(如下图),
在很多的编程语言中创建对象都是使用的new来创建的,那么在咱们python中呢?其实也有一个new,它是一个魔术方法,接下来我们一起来看看。
-
问题:python创建一个对象的时候,调用的第一个方法是什么?
很多小伙伴会说是
__init__
,其实不然,正确答案是:__new__
方法 -
问题:那么这个
__new__
方法呢,它有什么作用?又在什么时候会调用呢? -
案例:
接下来我们一起来看看下面这案例段代码:
class Test(object): def __init__(self): print('-------init------方法') def __new__(cls, *args, **kwargs): print('------new方法-------') t = Test() print(t)
-
运行上面的代码,发现
__new__
执行了,__init__
没有执行,创建出来的对象变成了None
-
-
为什么会出现这样的情况呢?
原因是我们重写了父类object中的
__new__
方法,当我们没有自定义__new__
方法时,默认继承了object的__new__
方法,我们使用类创建对象时,底层会自动调用这个方法来完成对象的创建。那么我们自己定义了这个方法之后呢?方法中没没有创建对象,也没有返回对象,所以最终创建的对象打印为None,而__init__
方法是一个实例方法,是创建对象之后用来初始化对象的,但是new方法中并没有创建出来对象,所以__init__
方法也没有执行。__new__
一般情况下我们都不会自己去重定义,只有在有特定的需求时才会去用,比如要修改或者控制类创建对象行为时,如实现单例类等等。
2、上下文管理器
相信很多小伙伴都用过python中的with,也都指定可以同它来操作文件,文件会自动关闭。那么大家有没有思考过一个问题,为什么with 打开文件为何会自动关闭?
-
问题: with 打开文件为何会自动关闭?
其实with操作文件不需要关闭的原因是,因为with启动的文件操作的上下文管理器协议。
-
什么上下文管理器协议?
所谓的上下文管理器协议,是由两个魔术方法实现的。在python中只要任意一个类中实现了
__enter__
和__exit__
这两个方法,那么这个类就实现了上下文管理器协议,这个类的对象就可以使用with来进行操作。 -
object.__enter__
(self)使用with操作实现上下文管理器协议的对象时,则会自动调用这个对象的
__enter__
方法,with语句将该方法非返回赋值给到as
后面的变量。 -
object.__exit__
(self, exc_type, exc_val, exc_tb)exc_type : # 异常类型 exc_val : # 异常值 exc_tb : # 异常回溯追踪
当with中的代码执行完毕之后,会自动调用
__exit__
方法退出上下文管理器。如果该上下文退出时没有异常,三个参数都将为None
。如果提供了一个异常,并且该方法希望抑制该异常(即防止它被传播),它应该返回一个真值。否则,在退出此方法后,异常将被正常处理。注意__exit__()
方法不应该重新抛出传递进去的异常;这是调用者的责任。
案例
手动实现操作文件的上下文管理器
class OpenFile(object):
'''手动实现文件操作的上下文'''
def __init__(self,filename,method):
#初始化打开文件
self.file = open(filename,method)
def __enter__(self):
#启动上下文时,将打开的对象返回出去
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
#退出上下文时,将文件关闭
self.file.close()
with OpenFile('python.txt','w') as f:
f.write('hello')
3、__call__
方法
-
问题:python中万物皆对象,函数也是对象,为什么函数可以调用,而其他的对象不行?
-
需求:如果想让类创建出来的对象,可以像函数一样被调用可以实现吗?
-
我们只需要再类里面定义魔术方法
__call__
方法即可实现,__call__
中可以定义对象调用的逻辑
-
class Test(object):
def __call__(self):
print('触发了call方法')
t = Test()
t()
4、__str__
方法
-
问题思考:交互环境下print打印的内容和和直接输入变量,返回的内容不一样这是为什么?
-
使用print打印的时候触发的是
__str__
方法,
-
-
注意点:
-
重写`str,必须要记得写return。
-
return返回的必须是一个字符串对象。
-
-
代码演示:
class Test(object):
def __init__(self,name):
self.name = name
def __str__(self):
return '触发了str方法'
-
python的内置函数str
-
内置函数str转换一个对象时,触发对象对应
__str__
的方法。 -
内置函数format处理对象是,触发对象对应
__str__
的方法。
-
5、算术运算的实现
-
思考问题: python中不仅数值之间能相加,字符串和列表,元祖之间也能进行,这是怎么实现的?
-
同类型对象之间使用+号的时候,实际上是触发了
__add__
魔术方法。
-
小案例验证
class Test(object):
def __init__(self,name,age):
self.name = name
self.age = name
def __add__(self, other):
print('对象之间使用了+号')
return self.age+other.age
xiaoming = Test('小明',18)
laowang = Test('老王',48)
print(xiaoming+laowang)
-
问题二:数值之间能用-进行运算,字符型、列表元祖为什么不行?
其实也是魔术方法来实现的,python的int类中实现了
__sub__
方法,字符串,列表等数据类型没有,所有的算术运算底层都是调用相应的魔术方法来实现的。 -
其他算术运算符对应的魔术方法:
__add__(self, other) 定义加法的行为:+
__sub__(self, other) 定义减法的行为:-
__mul__(self, other) 定义乘法的行为:*
__truediv__(self, other) 定义真除法的行为:/
__floordiv__(self, other) 定义整数除法的行为://
__mod__(self, other) 定义取余算法的行为:%
关于python中的魔术方法就暂时给大家介绍到这里,更多的魔术方法,大家可以自行扩展学习:
欢迎来到testingpai.com!
注册 关于