Python的程序结构[1] -> 方法/Method[2] -> 魔术方法 __init__ / __del__ / __new__
魔术方法 / Magic Method
魔法方法就是可以给你的类增加魔力的特殊方法(实质应称为特殊方法,魔术方法在JavaScript中有所体现,对象具有不透明特性,而且无法在自定义对象中模拟这些行为),如果你的对象实现(重载)了这些方法中的某一个,那么这个方法就会在特殊的情况下被 Python 所调用,你可以定义自己想要的行为,而这一切都是自动发生的。它们经常是两个双下划线包围来命名的(比如 __init__,__lt__),Python的魔法方法是非常强大的。下面介绍几种常用的魔术方法
__init__ 方法
__init__ 构造器/构造方法,当一个实例被创建的时候初始化的方法。但是它并不是实例化调用的第一个方法,__new__才是实例化对象调用的第一个方法。
class Foo:
def __init__(self, param):
print('Init method get parameter: %s' % param)
f = Foo('PARAM')
上面代码中的 Foo 类定义了一个 __init__ 构造方法,这个方法接收一个参数,但在使用时,这个方法可以不被显式的调用(类继承的时候有时会进行父类构造方法的显式调用),而是通过第 4 行的实例化方式来进行调用,运行上面的代码可以得到结果如下,
Init method get parameter: PARAM
从结果可以看出,在一个类进行实例化生成实例的时候,__init__ 构造方法会被自动调用。
__del__ 方法
__del__ 析构器/析构方法,与 __init__ 相反,当实例被销毁的时候调用的方法,也是 del() 函数会调用的方法。
class Foo():
def __init__(self):
print('__init__ method called') def __del__(self):
print('__del__ method called') f = Foo()
del(f)
上面的代码定义了一个带有构造方法可析构方法的 Foo 类,运行代码可以得到如下结果,
__init__ method called
__del__ method called
从结果可以看出,构造和析构两个方法都被调用了,构造方法在第 8 行被调用,而析构方法则是在第 9 行被 del 函数所调用。
__new__ 方法
__new__ 方法是实例化调用的第一个方法,在 __init__ 方法之前调用,它只取下 cls参数,并把其他参数传给 __init__。__new__ 很少使用,但是也有它适合的场景,尤其是当类继承自一个像元组或者字符串这样不经常改变的类型的时候。
__new__ 方法使用时注意以下几点:
1. __new__ 是在一个对象实例化的时候所调用的第一个方法;
2. 它的第一个参数是这个类,其他的参数是用来直接传递给 __init__ 方法;
3. __new__ 决定是否要使用自身的 __init__ 方法,因为 __new__ 可以调用其他类的构造方法或者直接返回别的实例对象来作为本类的实例,如果 __new__ 没有返回实例对象,则 __init__ 不会被调用,只有 __new__ 返回了实例对象,__init__ 才会被调用执行;
4. __new__ 主要是用于继承一个不可变的类型比如一个 tuple 或者 string;
5. __new__ return的是一个构建的实例。
__new__ 实现单例模式
class Person:
def __init__(self, name, age):
self.name = name
self.age = age def __new__(cls, *args, **kwargs):
print(id(cls), id(Person)) # Id is same
# If instance of class has exist, return the instance without generate a new one
if not hasattr(cls, 'instance'):
cls.instance = super(Person, cls).__new__(cls) # Generate a instance
return cls.instance a = Person('p1', 20)
print(a.name, a.age) # 'p1', 20
b = Person('p2', 21)
print(a.name, a.age) # 'p2', 21
print(b.name, b.age) # 'p2', 21
print(a is b) # True
上面的代码利用 __new__ 方法实现了一种单例模式,
首先是定义了一个类以及构造方法,然后定义了这个类的 __new__ 方法,注意这里的 __new__ 方法接收的第一个参数为cls,也就是当前的类,可以通过第 7 行输出两者的 id 进行比较,最终可以发现两者 id 相同。
在第 8-10 行中,利用一个判断语句判断当前类中是否含有 instance 属性,若有则直接返回,若没有则利用 super 通过 MRO 查找到 Person 的 MRO 搜索顺序中的下一个类(本例中是 object)来调用其 __new__ 方法,并传入当前 cls 作为参数。这样便实现了一个简单的单例模式。
然后是第 11 行,返回一个实例,这个返回的实例将会作为 __init__ 方法的第一个参数传给 self。
最后对单例模式进行验证,在 12-13 行中生成一个实例 a,并查看内部属性,发现其结果与传入值相同,在 15-17 行再生成一个实例 b,同时对 a 和 b 进行查看发现, a 的属性由于 b 的实例化也被改变了,最后通过 18 行查看发现,a 和 b 是同一个实例。
也就是说,无论 Person 实例化多少次,都只会产生一个实例对象传给 __init__ 方法,只不过每次的初始化函数参数不同,从而改变了实例的属性值。
相关阅读
1. super
2. MRO 搜索顺序
参考链接
http://www.cnblogs.com/styier/p/6111370.html
Python的程序结构[1] -> 方法/Method[2] -> 魔术方法 __init__ / __del__ / __new__的更多相关文章
- Python的程序结构[1] -> 方法/Method[3] -> 魔术方法 __getattr__ 与代理模式
__getattr__ 方法 __getattr__ 方法当对象调用内部属性(包括方法等)且未找到对应属性的时候会调用的特殊方法.利用这一特性,可是对函数实现一个代理模式. __getattr__方法 ...
- Python的程序结构[1] -> 方法/Method[4] -> 魔术方法 __call__ / __str__ / __repr__
__call__ 方法 __call__ 是当对象被调用时会调用的方法,允许一个对象(类的实例等)像函数一样被调用,也可以传入参数. 1 class Foo(): 2 def __init__(sel ...
- Python的程序结构[1] -> 方法/Method[0] -> 类实例方法、私有方法和抽象方法
类实例方法.私有方法和抽象方法 Python中最常用的就是类实例方法,类似于属性中的类实例属性,同时,也存在与私有属性类似方法,即私有方法,下面介绍这两种常见的方法,以及一种特殊意义的类实例方法 -- ...
- C++/Php/Python/Shell 程序按行读取文件或者控制台方法总结。
C++/Php/Python/Shell 程序按行读取文件或者控制台方法总结. 一.总结 C++/Php/Python/Shell 程序按行读取文件或者控制台(php读取标准输入:$fp = fope ...
- Python的程序结构[1] -> 方法/Method[1] -> 静态方法、类方法和属性方法
静态方法.类方法和属性方法 在 Python 中有三种常用的方法装饰器,可以使普通的类实例方法变成带有特殊功能的方法,分别是静态方法.类方法和属性方法. 静态方法 / Static Method 在 ...
- Python的程序结构[4] -> 函数/Function[0] -> 函数与方法的区别
函数与方法的区别 / Distinction of Function and Method 关于函数与方法的区别,可根据两者的定义看出, 函数function -- A series of state ...
- Python的程序结构[7] -> 生成器/Generator -> 生成器浅析
生成器 / Generator 目录 关于生成器 生成器与迭代器 生成器的建立 通过迭代生成器获取值 生成器的 close 方法 生成器的 send 方法 生成器的 throw 方法 空生成器的检测方 ...
- Python的程序结构[2] -> 类/Class[1] -> 基类与继承
基类与继承 / Base Class and Inheritance Class 面向对象的特性使得 Python 中不可避免地需要使用到类和类的继承,类的继承可以使得代码很好的被重用.下面以一些代码 ...
- 零基础小白Python入门必看:面向对象之典型魔术方法
随机推荐
- Windows Server 2008 R2 集群(OpenService “RemoteRegistry” 失败)笔记
OpenService “RemoteRegistry” 失败. 我在创建验证域控服务器[系统]类别中 看到错误日志 我在域控服务器去看,在 computers 里面 是有这台 计算机,但是为什么不行 ...
- 恢复误删除表黑科技之relay log大法(续)
Preface I've stuck twice in my previous experiments in backing up dropped tables.I am still ...
- JNDI和JDBC的区别和联系及其使用方法
一.JNDI 和JDBC的区别和联系 两者都是API,是连接数据库的标准.并不是什么产品或方法. 二.JDBC 全称:Java Database Connectivity 以一种统一的方式来对各种各样 ...
- ES6常用片段
promise: --在return里面: methods:{ getSellData(){ return axios.get('/api/seller').then((res=>{ retur ...
- 剑指offer-从尾到头打印链表03
class Solution: # 返回从尾部到头部的列表值序列,例如[1,2,3] def printListFromTailToHead(self, listNode): # write code ...
- linux c编程(一)
1 常用系统环境配置 2 使用g++编译连接,使用gdb调试 3 使用makefile组织目标文件的依赖关系 4 使用git 1 常用系统环境配置 输入法 Download setup file fo ...
- 第八章 Internet控制报文协议
Internet控制报文协议 首先,我们必须先清楚,IP协议本身没有为终端系统提供直接的方法来发现那些发往目的地址失败的IP数据包,并且IP没有提供直接的方式来获取诊断信息,那么我们的故事来了. In ...
- 基于HTTP协议的轻量级开源简单队列服务:HTTPSQS[转]
HTTPSQS(HTTP Simple Queue Service)是一款基于 HTTP GET/POST 协议的轻量级开源简单消息队列服务,使用 Tokyo Cabinet 的 B+Tree Key ...
- DP石子合并问题
转自:http://www.hnyzsz.net/Article/ShowArticle.asp?ArticleID=735 [石子合并] 在一个圆形操场的四周摆放着n 堆石子.现要将石子有次序 ...
- BZOJ4824 [Cqoi2017]老C的键盘 【树形dp】
题目链接 BZOJ4824 题解 观察出题目中的关系实际上是完全二叉树的父子关系 我们设\(f[i][j]\)为以\(i\)为根的节点在其子树中排名为\(j\)的方案数 转移时,枚举左右子树分别有几个 ...