• 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 中的魔术方法就暂时给大家介绍到这里,更多的魔术方法,大家可以自行扩展学习:

    更多的魔术方法参考地址:https://www.cnblogs.com/nmb-musen/p/10861536.html

    =====请大家尊重原创,如要转载,请注明出处:转载自:https://www.cnblogs.com/tudou-22/,谢谢!!=====
  • =====有任何疑问,欢迎加微信 ningmengpyn (请备注:博客园)=====

  • 相关阅读:
    SQLServer如何批量替换某一列中的某个字符串
    能成为一名合格的Java架构师
    来看看Uber的司机支持服务签到及预约系统的架构设计思路
    什么是三层架构?你真的理解分层的意义吗?
    京东7Fresh新零售架构设计分析
    解密京东千亿商品系统核心架构
    因特尔黑科技:黑暗中快速成像系统
    分布式缓存架构设计
    各种排序算法汇总小结
    系统架构设计之-任务调度系统的设计
  • 原文地址:https://www.cnblogs.com/nmb-musen/p/14842010.html
  • Copyright © 2020-2023  润新知