浅谈python中的__init__、__new__和__call__方法


Posted in Python onJuly 18, 2017

前言

本文主要给大家介绍关于python中__init__、__new__和__call__方法的相关内容,分享出来供大家参考学习,下面话不多说,来一起看看详细的介绍:

任何事物都有一个从创建,被使用,再到消亡的过程,在程序语言面向对象编程模型中,对象也有相似的命运:创建、初始化、使用、垃圾回收,不同的阶段由不同的方法(角色)负责执行。

定义一个类时,大家用得最多的就是 __init__ 方法,而 __new__ 和 __call__ 使用得比较少,这篇文章试图帮助大家把这3个方法的正确使用方式和应用场景分别解释一下。

关于 Python 新式类和老式类在这篇文章不做过多讨论,因为老式类是 Python2 中的概念,现在基本没人再会去用老式类,新式类必须显示地继承 object,而 Python3 中,只有新式类,默认继承了 object,无需显示指定,本文代码都是基于 Python3 来讨论。

__init__方法

__init__方法负责对象的初始化,系统执行该方法前,其实该对象已经存在了,要不然初始化什么东西呢?先看例子:

# class A(object): python2 必须显示地继承object
class A:
 def __init__(self):
  print("__init__ ")
  super(A, self).__init__()

 def __new__(cls):
  print("__new__ ")
  return super(A, cls).__new__(cls)

 def __call__(self): # 可以定义任意参数
  print('__call__ ')

A()

输出

__new__
__init__

从输出结果来看, __new__方法先被调用,返回一个实例对象,接着 __init__ 被调用。 __call__方法并没有被调用,这个我们放到最后说,先来说说前面两个方法,稍微改写成:

def __init__(self):
 print("__init__ ")
 print(self)
 super(A, self).__init__()

def __new__(cls):
 print("__new__ ")
 self = super(A, cls).__new__(cls)
 print(self)
 return self

输出:

__new__ 
<__main__.A object at 0x1007a95f8>
__init__ 
<__main__.A object at 0x1007a95f8>

从输出结果来看,__new__ 方法的返回值就是类的实例对象,这个实例对象会传递给 __init__ 方法中定义的 self 参数,以便实例对象可以被正确地初始化。

如果 __new__ 方法不返回值(或者说返回 None)那么 __init__ 将不会得到调用,这个也说得通,因为实例对象都没创建出来,调用 init 也没什么意义,此外,Python 还规定,__init__ 只能返回 None 值,否则报错,这个留给大家去试。

__init__方法可以用来做一些初始化工作,比如给实例对象的状态进行初始化:

def __init__(self, a, b):
 self.a = a
 self.b = b
 super(A, self).__init__()

另外,__init__方法中除了self之外定义的参数,都将与__new__方法中除cls参数之外的参数是必须保持一致或者等效。

class B:
 def __init__(self, *args, **kwargs):
  print("init", args, kwargs)

 def __new__(cls, *args, **kwargs):
  print("new", args, kwargs)
  return super().__new__(cls)

B(1, 2, 3)

# 输出

new (1, 2, 3) {}
init (1, 2, 3) {}

__new__ 方法

一般我们不会去重写该方法,除非你确切知道怎么做,什么时候你会去关心它呢,它作为构造函数用于创建对象,是一个工厂函数,专用于生产实例对象。著名的设计模式之一,单例模式,就可以通过此方法来实现。在自己写框架级的代码时,可能你会用到它,我们也可以从开源代码中找到它的应用场景,例如微型 Web 框架 Bootle 就用到了。

class BaseController(object):
 _singleton = None
 def __new__(cls, *a, **k):
  if not cls._singleton:
   cls._singleton = object.__new__(cls, *a, **k)
  return cls._singleton

这段代码出自 https://github.com/bottlepy/bottle/blob/release-0.6/bottle.py

这就是通过 __new__ 方法是实现单例模式的的一种方式,如果实例对象存在了就直接返回该实例即可,如果还没有,那么就先创建一个实例,再返回。当然,实现单例模式的方法不只一种,Python之禅有说:

There should be one-- and preferably only one --obvious way to do it.

用一种方法,最好是只有一种方法来做一件事

__call__ 方法

关于 __call__ 方法,不得不先提到一个概念,就是可调用对象(callable),我们平时自定义的函数、内置函数和类都属于可调用对象,但凡是可以把一对括号()应用到某个对象身上都可称之为可调用对象,判断对象是否为可调用对象可以用函数 callable

如果在类中实现了 __call__ 方法,那么实例对象也将成为一个可调用对象,我们回到最开始的那个例子:

a = A()
print(callable(a)) # True

a是实例对象,同时还是可调用对象,那么我就可以像函数一样调用它。试试:

a() # __call__

很神奇不是,实例对象也可以像函数一样作为可调用对象来用,那么,这个特点在什么场景用得上呢?这个要结合类的特性来说,类可以记录数据(属性),而函数不行(闭包某种意义上也可行),利用这种特性可以实现基于类的装饰器,在类里面记录状态,比如,下面这个例子用于记录函数被调用的次数:

class Counter:
 def __init__(self, func):
  self.func = func
  self.count = 0

 def __call__(self, *args, **kwargs):
  self.count += 1
  return self.func(*args, **kwargs)

@Counter
def foo():
 pass

for i in range(10):
 foo()

print(foo.count) # 10

在 Bottle 中也有 call 方法 的使用案例,另外,stackoverflow 也有一些关于 call 的实践例子,推荐看看,如果你的项目中,需要更加抽象化、框架代码,那么这些高级特性往往能发挥出它作用。

总结

以上就是这篇文章的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Python 相关文章推荐
在Python的web框架中配置app的教程
Apr 30 Python
为Python的web框架编写MVC配置来使其运行的教程
Apr 30 Python
Python使用arrow库优雅地处理时间数据详解
Oct 10 Python
Python通过调用mysql存储过程实现更新数据功能示例
Apr 03 Python
Python中pillow知识点学习
Apr 30 Python
PyQt5内嵌浏览器注入JavaScript脚本实现自动化操作的代码实例
Feb 13 Python
python之array赋值技巧分享
Nov 28 Python
Python编写一个验证码图片数据标注GUI程序附源码
Dec 09 Python
Python文件操作函数用法实例详解
Dec 24 Python
Python爬取YY评级分数并保存数据实现过程解析
Jun 01 Python
Python QTimer实现多线程及QSS应用过程解析
Jul 11 Python
Django生成数据库及添加用户报错解决方案
Oct 09 Python
Pycharm编辑器技巧之自动导入模块详解
Jul 18 #Python
python对DICOM图像的读取方法详解
Jul 17 #Python
Python实现excel转sqlite的方法
Jul 17 #Python
PyChar学习教程之自定义文件与代码模板详解
Jul 17 #Python
Python实现将sqlite数据库导出转成Excel(xls)表的方法
Jul 17 #Python
Python下实现的RSA加密/解密及签名/验证功能示例
Jul 17 #Python
Python 的类、继承和多态详解
Jul 16 #Python
You might like
用PHP的ob_start() 控制您的浏览器cache
2009/08/03 PHP
克隆一个新项目的快捷方式
2013/04/10 PHP
JS模拟面向对象全解(一、类型及传递)
2011/07/13 Javascript
Javascript设置对象的ReadOnly属性(示例代码)
2013/12/25 Javascript
JS中自定义定时器让它在某一时刻执行
2014/09/02 Javascript
jQuery中closest和parents的区别分析
2015/05/07 Javascript
基于JavaScript实现移除(删除)数组中指定元素
2016/01/04 Javascript
jQuery的文档处理程序详解
2016/05/10 Javascript
js匿名函数作为函数参数详解
2016/06/01 Javascript
jquery 判断selection range 是否在容器中的简单实例
2016/08/02 Javascript
JS实现重新加载当前页面
2016/11/29 Javascript
微信小程序 HTTPS报错整理常见问题及解决方案
2016/12/14 Javascript
js实现交通灯效果
2017/01/13 Javascript
JavaScript对象引用与赋值实例详解
2017/03/15 Javascript
详解vue-cli3多环境打包配置
2019/03/28 Javascript
js实现带有动画的返回顶部
2020/08/09 Javascript
原生js实现滑块区间组件
2021/01/20 Javascript
[03:03]2014DOTA2国际邀请赛 EG战队专访
2014/07/12 DOTA
[57:22]完美世界DOTA2联赛PWL S2 FTD vs PXG 第二场 11.27
2020/12/01 DOTA
给Python中的MySQLdb模块添加超时功能的教程
2015/05/05 Python
Python实现字符串格式化的方法小结
2017/02/20 Python
Python多进程multiprocessing用法实例分析
2017/08/18 Python
python数字图像处理之高级滤波代码详解
2017/11/23 Python
Python语言实现百度语音识别API的使用实例
2017/12/13 Python
Python实现使用卷积提取图片轮廓功能示例
2018/05/12 Python
浅谈keras中的后端backend及其相关函数(K.prod,K.cast)
2020/06/29 Python
浅谈移动端网页图片预加载方案
2018/11/05 HTML / CSS
世界顶级足球门票网站:Live Football Tickets
2017/10/14 全球购物
Smallable意大利家庭概念店:设计师童装及家居装饰
2018/01/08 全球购物
美国婚礼装饰和活动用品批发供应商:Event Decor Direct
2018/10/12 全球购物
GWT (Google Web Toolkit)有哪些主要的原件组成?
2015/06/08 面试题
法人授权委托书公证范本
2014/09/14 职场文书
教师评职称工作总结2015
2015/04/20 职场文书
医者仁心观后感
2015/06/17 职场文书
如何用python反转图片,视频
2021/04/24 Python
MySQL 原理优化之Group By的优化技巧
2022/08/14 MySQL