今天这篇是Python专题的第17篇文章,我们来聊聊Python当中一个新的默认函数__new__。

上一篇当中我们讲了如何使用type函数来动态创建Python当中的类,除了type可以完成这一点之外,还有另外一种用法叫做metaclass。原本这一篇应该是继续元类的内容,讲解metaclass的使用。但是metaclass当中用到了一个新的默认函数__new__,关于这个函数大家可能会比较陌生,所以在我们研究metaclass之前,我们先来看看__new__这个函数的用法。

真假构造函数

如果你去面试Python工程师的岗位,面试官问你,请问Python当中的类的构造函数是什么?

你不假思索,当然是__init__啦!如果你这么回答,很有可能你就和offer无缘了。因为在Python当中__init__并不是构造函数,__new__才是。是不是有点蒙,多西得(日语:为什么)?我们不是一直将__init__方法当做构造函数来用的吗?怎么又冒出来一个__new__,如果__new__才是构造函数,那么为什么我们创建类的时候从来不用它呢?

别着急,我们慢慢来看。首先我们回顾一下__init__的用法,我们随便写一段代码:

class Student:

def __init__(self, name, gender):

self.name = name

self.gender = gender

我们一直都是这么用的,对不对,毫无问题。但是我们换一个问题,我们在Python当中怎么实现单例(Singleton)的设计模式呢?怎么样实现工厂呢?

从这个问题出发,你会发现只使用__init__函数是不可能完成的,因为__init__并不是构造函数,它只是初始化方法。也就是说在调用__init__之前,我们的实例就已经被创建好了,__init__只是为这个实例赋上了一些值。如果我们把创建实例的过程比喻成做一个蛋糕,__init__方法并不是烘焙蛋糕的,只是点缀蛋糕的。那么显然,在点缀之前必须先烘焙出一个蛋糕来才行,那么这个烘焙蛋糕的函数就是__new__。

__new__函数

我们来看下__new__这个函数的定义,我们在使用Python面向对象的时候,一般都不会重构这个函数,而是使用Python提供的默认构造函数,Python默认构造函数的逻辑大概是这样的:

def __new__(cls, *args, **kwargs):

return super().__new__(cls, *args, **kwargs)

从代码可以看得出来,函数当中基本上什么也没做,就原封不动地调用了父类的构造函数。这里隐藏着Python当中类的创建逻辑,是根据继承关系一级一级创建的。根据逻辑关系,我们可以知道,当我们创建一个实例的时候,实际上是先调用的__new__函数创建实例,然后再调用__init__对实例进行的初始化。我们可以简单做个实验:

class Test:

def __new__(cls):

print('__new__')

return object().__new__(cls)

def __init__(self):

print('__init__')

当我们创建Test这个类的时候,通过输出的顺序就可以知道Python内部的调用顺序。

从结果上来看,和我们的推测完全一样。

单例模式

那么我们重载__new__函数可以做什么呢?一般都是用来完成__init__无法完成的事情,比如前面说的单例模式,通过__new__函数就可以实现。我们来简单实现一下:

class SingletonObject:

def __new__(cls, *args, **kwargs):

if not hasattr(SingletonObject, "_instance"):

SingletonObject._instance = object.__new__(cls)

return SingletonObject._instance

def __init__(self):

pass

当然,如果是在并发场景当中使用,还需要加上线程锁防止并发问题,但逻辑是一样的。

除了可以实现一些功能之外,还可以控制实例的创建。因为Python当中是先调用的__new__再调用的__init__,所以如果当调用__new__的时候返回了None,那么最后得到的结果也是None。通过这个特性,我们可以控制类的创建。比如设置条件,只有在满足条件的时候才能正确创建实例,否则会返回一个None。

比如我们想要创建一个类,它是一个int,但是不能为0值,我们就可以利用__new__的这个特性来实现:

class NonZero(int):

def __new__(cls, value):

return super().__new__(cls, value) if value != 0 else None

那么当我们用0值来创建它的时候就会得到一个None,而不是一个实例。

工厂模式

理解了__new__函数的特性之后,我们就可以灵活运用了。我们可以用它来实现许多其他的设计模式,比如大名鼎鼎经常使用的工厂模式。

所谓的工厂模式是指通过一个接口,根据参数的取值来创建不同的实例。创建过程的逻辑对外封闭,用户不必关系实现的逻辑。就好比一个工厂可以生产多种零件,用户并不关心生产的过程,只需要告知需要零件的种类。也因此称为工厂模式。

比如说我们来创建一系列游戏的类:

class Last_of_us:

def play(self):

print('the Last Of Us is really funny')

class Uncharted:

def play(self):

print('the Uncharted is really funny')

class PSGame:

def play(self):

print('PS has many games')

然后这个时候我们希望可以通过一个接口根据参数的不同返回不同的游戏,如果不通过__new__,这段逻辑就只能写成函数而不能通过面向对象来实现。通过重载__new__我们就可以很方便地用参数来获取不同类的实例:

class GameFactory:

games = {'last_of_us': Last_Of_us, 'uncharted': Uncharted}

def __new__(cls, name):

if name in cls.games:

return cls.games[name]()

else:

return PSGame()

uncharted = GameFactory('uncharted')

last_of_us = GameFactory('last_of_us')

总结

相信看到这里,关于__new__这个函数的用法应该都能理解了。一般情况下我们是用不到这个函数的,只会在一些特殊的场景下使用。虽然如此,我们学会它并不只是用来实现设计模式,更重要的是可以加深我们对于Python面向对象的理解。

除此之外,另一个经常使用__new__场景是元类。所以今天的这篇文章其实也是为了后面介绍元类的其他用法打基础。

如果喜欢本文,可以的话,请点个关注,给我一点鼓励,也方便获取更多文章。

python提供的默认的构造方法是什么_Python面试常见问题,__init__是构造函数吗?...相关推荐

  1. java中构造方法只能有一个_对Java中类的构造方法描述正确的是()A.如果在类中没有定义,Java就提供一个默认的构造方法B.只能...

    对Java中类的构造方法描述正确的是()A.如果在类中没有定义,Java就提供一个默认的构造方法B.只能 更多相关问题 猛虎噬人卣是_______时期的陶塑代表作品. 静态网页是指网页的内容是固定的, ...

  2. python提供的内置函数有哪些_python内置函数介绍

    内置函数,一般都是因为使用频率比较频繁,所以通过内置函数的形式提供出来.对内置函数通过分类分析,基本的数据操作有数学运算.逻辑操作.集合操作.字符串操作等. 说起我正式了解内置函数之前,接触到的是la ...

  3. python提供的三种基本数值类型_Python 基础数据类型-数值类型

    Python 基础数据类型-数值类型 为什么会有数据类型?[了解] 更好分配管理内存 方便统一管理 更贴近人类分类管理习惯 数据类型种类 [熟悉] 数值类型:int.bool.float.comple ...

  4. python提供了方法用于读取文本文件内容_python提供了哪三种方法用于读取文本文件的内容?...

    三种方法分别是:"read()"."readline()"."readlines()"."read()"是一次性读取文件 ...

  5. python提供的三种基本数据类型是()_python基础之基本数据类型

    1.概念 1.1 表达式 表达式,是由数字.算符.数字分组符号(括号).自由变量和约束变量等以能求得数值的有意义排列方法所得的组合 表达式特点 表达式一般仅仅用于计算一些结果,不会对程序产生实质性的影 ...

  6. python提供的三种基本数据类型是()_Python基本数据类型

    # Python基本数据类型 ##### 1.Python简介 ##### 2.数值类型 ##### 3.序列类型 ##### 1.Python简介 1.python是一门编程语言,是一门完全面向对象 ...

  7. python中构造方法的名字,【填空题】Python提供了名称为 的构造方法,实现让类的对象完成初始化。...

    [填空题]Python提供了名称为 的构造方法,实现让类的对象完成初始化. 更多相关问题 如图是2012年元宵节灯展中一款五角星灯连续旋转闪烁所成的三个图形,照此规律闪烁,下一个呈现出来的图形是( 在 ...

  8. python默认参数 可变对象_当心Python函数可变默认参数(list,set,dict…)的陷阱

    绝大多数情况下,Python是一个干净具有一致性的语言.然而,有些少数情况会让初学者感到困惑.其中有些情况是有意识的但会成为潜在的莫名其妙,而有些可以说是语言赘肉.下面我们看看使用可变默认参数(Mut ...

  9. Python进阶-函数默认参数,特别是参数传递为空列表

    这两天遇到函数默认参数的bug,在互联网上好好总结了一下: 如非特别说明,下文均基于Python3 一.默认参数 python为了简化函数的调用,提供了默认参数机制: def pow(x, n = 2 ...

最新文章

  1. TensorRT是NVIDIA开发的深度学习推理工具,只支持推理,不支持训练 引
  2. linux ppp拨号 socket,请问GPRS模块ppp拨号不成功是什么原因
  3. [原创]TimeQuest约束外设之诡异的Create Generated Clocks用法
  4. 关于MongoDB内存占用不断上升,导致OOM问题
  5. vue打包后获取不到数据_vue 打包后,如何修改接口地址?
  6. fiddler模拟低速网络
  7. Debian 决定允许无记名投票,候选人Felix Lechner质疑
  8. html标签学习日记之(表格table)
  9. 拼多多运营模式分析 | 如何杀出电商重围?
  10. CADD课程学习(2)-- 靶点晶体结构信息
  11. 下一代数据架构Data Fabric到底是什么?
  12. 组件封装 - 省市区联动组件
  13. 交换机远程连接(eNsp)
  14. kerberos mysql配置_CDH安装之篇四:启用Kerberos认证
  15. 首都师范 博弈论 6 5 1有限次的重复博弈
  16. 北纬三十度“神命谷”旅游策划方案
  17. 出现“未报告的异常错误,必须对其进行捕获或声明以便抛出”的解决
  18. z-index细解:一个z-index的值很大为何却在一个很小的值下面?
  19. 关闭centos7下哔哔声
  20. c语言printf双精度还需要lf,C语言中printf区分双精度和单精度吗?为什么用lf输出不了双精度浮点数?...

热门文章

  1. thymeleaf文档_springboot中Thymeleaf和Freemarker模板引擎的区别
  2. springboot-swagger2
  3. php项目webpack打包,Vue项目webpack打包部署时Tomcat刷新报404错误问题如何处理
  4. 两边双虚线是什么意思_单黄线和双黄线有什么不同?很多人都记不对,被扣分都不知道...
  5. 清华大学计算机网络研究中心,清华大学信息网络工程研究中心简介
  6. idle和python区别_Python的IDLE与命令lin的区别
  7. 【PAT (Advanced Level) Practice】1050 String Subtraction (20 分)
  8. python【蓝桥杯vip练习题库】—BASIC-12十六进制转八进制
  9. Android移动开发之【Android实战项目】textview实现文字逐字显示效果
  10. 机器学习需要理解的五个基本概念