0

0

Python中new类方法和init 实例方法以及单例模式的介绍(附示例)

不言

不言

发布时间:2019-01-28 11:18:55

|

2513人浏览过

|

来源于segmentfault

转载

本篇文章给大家带来的内容是关于python中new类方法和init 实例方法以及单例模式的介绍(附示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

“Python 中的类都是单例模式?” 一天,一同事问我这样一个问题。这是一个奇怪的问题,可能你也这么认为。这里先不做解释,我们先来看看 __new__ 和 __init__ 方法。

new 与 init

__new__ 方法属于新式类,即属于 object 类。它是一个静态方法,但是其第一个参数必须是一个类(cls),这有点像一个 classmethod,其实将其看成是一个类方法也可以。该特殊方法被调用时,会创建类(cls)的一个新实例并返回,实例被创建后解释器会将该实例以及其它的参数传递给该实例的初始化函数 __init__,以对实例进行初始化。

所以,__new__ 方法是一个类方法,用于创建一个实例,而 __init__ 方法是一个实例方法,用于初始化一个实例。

__new__ 方法在实例化一个类时被调用,重写该方法应该像如下的形式:

立即学习Python免费学习笔记(深入)”;

class A(object):

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

如果 __new__ 方法不返回 cls 的一个实例,那么新的实例的 __init__ 方法不会被调用。需要注意的是,在 Python 3.3 之后,new 方法不再接收额外的参数,否则会有异常 TypeError: object() takes no parameters。

__init__ 方法在实例被创建之后被调用,该方法仅仅是对 __new__ 方法创建的实例进行一些初始化操作。注意,如果 new 方法返回实例,则 init 方法总是会被调用(这一点在用 new 方法实现单例时要特别注意)

可以来做一下验证:

class Foo(object):

    def __new__(cls, m, n):
        print "__new__ is called"
        return super(Foo, cls).__new__(cls, m, n)

    def __init__(self, m, n):
        print "__init__ is called"
        self.m = m
        self.n = n

    def __repr__(self):
        return "Foo(m={self.m}, n={self.n})".format(self=self)

    def __str__(self):
        return self.__repr__()


if __name__ == "__main__":
    f = Foo(1, 2)
    print f

输出结果:

__new__ is called
__init__ is called
Foo(m=1, n=2)

于是可以得出结论:

1、 __new__ 属于类级别的方法,即使没有被 classmethod 装饰,其决定生成实例的过程。

2、 __init__ 属于实例级别的方法,决定实例初始化的过程,比如添加属性,对初始化参数进行判断,转换等。

需要注意的是,在重写 __new__ 方法与 __init__ 方法的参数应该保持一致,否则会有 TypeError 发生。如果直接调用 object.__new__() 则在 Python 3.3 及以后的版本中不再支持传入参数,这一点参考自:https://stackoverflow.com/questions/34777773/typeerror...

__init__ 方法,在定义一个 class 的时候一般都会涉及到,也是比较常用。而 __new__ 方法则很少会用到,那么它到底有什么用途呢?

new 方法作用

__new__ 方法比较常用的作用大概是:

1、 继承内建不可变类型时(比如int, str, tuple),提供自定义实例化过程。因为如果在 __init__ 方法中做都写操作可能会无效。例如:

class CustomInt(int):

    def __init__(self, v):
        super(CustomInt, self).__init__(self, abs(v))

print CustomInt(-1)

# 输出:-1

这可能没有达到期望的效果。但可以通过重写 __new__ 方法来实现:

Delphi 7应用编程150例 全书内容 CHM版
Delphi 7应用编程150例 全书内容 CHM版

Delphi 7应用编程150例 CHM全书内容下载,全书主要通过150个实例,全面、深入地介绍了用Delphi 7开发应用程序的常用方法和技巧,主要讲解了用Delphi 7进行界面效果处理、图像处理、图形与多媒体开发、系统功能控制、文件处理、网络与数据库开发,以及组件应用等内容。这些实例简单实用、典型性强、功能突出,很多实例使用的技术稍加扩展可以解决同类问题。使用本书最好的方法是通过学习掌握实例中的技术或技巧,然后使用这些技术尝试实现更复杂的功能并应用到更多方面。本书主要针对具有一定Delphi基础知识

下载
class CustomInt(int):

    def __new__(cls, v):
        return super(CustomInt, cls).__new__(cls, abs(v))

print CustomInt(-1)

# 输出:1
  • 2、 实现自定义的元类(metaclass)。元类就是用来定义如何创建类,它的概念可能稍微复杂些,这里不做详细讨论。

  • 3、 实现单例。由于类产生实例的过程是通过 __new__ 方法来控制的,因此重写该方法来单例模式是非常方便的:

class Singleton(object):

    def __new__(cls):
        if not hasattr(cls, "_instance"):
            cls._instance = super(Singleton, cls).__new__(cls)
        return cls._instance

assert Singleton() is Singleton()  # 断言成功

所谓单例模式就是每次初始化都返回同一个实例,所以两次初始化得到的对象的内存地址应该是一样的:

print Singleton(), Singleton()

结果应该是显而易见的:

<__main__.Singleton object at 0x10d698650> <__main__.Singleton object at 0x10d698650>

装饰器实现单例

说到单例模式,除了用 __new__ 方法实现外,还有一些其他的方式,如装饰器、元类等。不同方式的实现有不同的作用,元类属于 Python 中更为高级的特性,本文不做讨论,我们来看一下用装饰器实现单例的方法。

装饰器(decorator)可以动态地修改一个类或函数的功能,即类也可以被装饰器装饰。因此可以使用装饰器来装饰某个类,使其被初始化时只生成一个实例:

from functools import wraps

def singleton(cls):
    instances = {}
    @wraps(cls)
    def getinstance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    return getinstance

@singleton
class MyClass(object):

    def __init__(self):
        pass

需要注意的是,使用装饰器实现单例时,类已经变成了一个函数,而不再是类。 如上用上例中 MyClass 初始化实例时,实际上调用的是被装饰后返回的 getinstance 函数。

__new__ 实现单例和用装饰实现单例的区别是,前者前者都是会调用 __init__ 方法,这就意味着每次初始化时用不同的参数,虽然返回的实例时同一个,但是实例的属性却被重新设置了;而后者则总是返回第一次初始化创建的示例和设置的属性,即使后面传入了不同的参数。

奇怪现象

接着,我们再来看一个 “奇怪” 的现象:

>>> class A(object):
...     pass
...
>>> print A(), A()
<__main__.A object at 0x104765450> <__main__.A object at 0x104765450>
>>> print A(), A()
<__main__.A object at 0x104765450> <__main__.A object at 0x104765450>
>>> print A(), A()
<__main__.A object at 0x104765450> <__main__.A object at 0x104765450>

是不是感觉有些难以置信,print 语句后两次创建的对象应该是不一样的,而他们却莫名奇妙的一样。这就是我讨论本文内容的原因。

一次同事问我,Python 中的类都是单例模式?我当时一脸懵逼,听了他的描述,我自己也试了下,果然存在如上所示的“奇怪”现象。于是我就去了解了 Python 单例模式的实现,在了解到 __new__ 的实现方式时,就想对 __new____init__ 有一个更加深入的了解。于是就有了本文所讨论的内容。

然后,我想着用 is 来判断下他们是否真的是同一个实例:

>>> A() is A()
False

我没有对 CPython 的源码进行过全面的阅读,所以对其很多内部的实现机制不是太了解。我猜 Python 解释器在内部可能做了优化,像 print A(), A() 这样的语句,解释器认为没有必要创建不同的对象,直接返回同一个实例的引用得了。是不是觉得解释器有些自作聪明!而当 A() is A() 这样的表达式出现时,解释器想,我不能再自作聪明,那样可能会误导别人。可是,在 print 语句那样的用法时,就已经误导我了,我都差点开始怀疑人生了!

从语法来看,大家应该知道,我在测试时使用的 Python 2。我后来也试了下 Python 3:

>>> class A():
...     pass
...
>>> print(A(), A())
<__console__.A object at 0x10fe7afd0> <__console__.A object at 0x10fed79e8>
>>> print(A(), A())
<__console__.A object at 0x10fec0cc0> <__console__.A object at 0x10feda160>
>>> print(A(), A())
<__console__.A object at 0x10fe7afd0> <__console__.A object at 0x10fed7940>
>>> A() is A()
False

我想,这样的结果才是不会让人困惑的。可能是 Python 社区意识到了这个问题并在 Python3 中进行了修正。这样的修正是好的,否则对于像我同事那样初次使用 Python 的人来说是很困惑的。

个人认为,Python3 对过去的一些“错误”的修正是好的。例如将 print 改为函数,提供了丰富的参数来控制输出的样式;对编码的调整等等。

相关文章

python速学教程(入门到精通)
python速学教程(入门到精通)

python怎么学习?python怎么入门?python在哪学?python怎么学才快?不用担心,这里为大家提供了python速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
python中print函数的用法
python中print函数的用法

python中print函数的语法是“print(value1, value2, ..., sep=' ', end=' ', file=sys.stdout, flush=False)”。本专题为大家提供print相关的文章、下载、课程内容,供大家免费下载体验。

192

2023.09.27

python print用法与作用
python print用法与作用

本专题整合了python print的用法、作用、函数功能相关内容,阅读专题下面的文章了解更多详细教程。

17

2026.02.03

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

930

2023.08.02

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

602

2024.08.29

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

294

2025.08.29

C++中int的含义
C++中int的含义

本专题整合了C++中int相关内容,阅读专题下面的文章了解更多详细内容。

212

2025.08.29

class在c语言中的意思
class在c语言中的意思

在C语言中,"class" 是一个关键字,用于定义一个类。想了解更多class的相关内容,可以阅读本专题下面的文章。

768

2024.01.03

python中class的含义
python中class的含义

本专题整合了python中class的相关内容,阅读专题下面的文章了解更多详细内容。

25

2025.12.06

PHP高性能API设计与Laravel服务架构实践
PHP高性能API设计与Laravel服务架构实践

本专题围绕 PHP 在现代 Web 后端开发中的高性能实践展开,重点讲解基于 Laravel 框架构建可扩展 API 服务的核心方法。内容涵盖路由与中间件机制、服务容器与依赖注入、接口版本管理、缓存策略设计以及队列异步处理方案。同时结合高并发场景,深入分析性能瓶颈定位与优化思路,帮助开发者构建稳定、高效、易维护的 PHP 后端服务体系。

4

2026.03.04

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
最新Python教程 从入门到精通
最新Python教程 从入门到精通

共4课时 | 22.5万人学习

Django 教程
Django 教程

共28课时 | 4.7万人学习

SciPy 教程
SciPy 教程

共10课时 | 1.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号