返回顶部
首页 > 资讯 > 后端开发 > Python >详解Python中__new__方法的作用
  • 703
分享到

详解Python中__new__方法的作用

2024-04-02 19:04:59 703人浏览 安东尼

Python 官方文档:入门教程 => 点击学习

摘要

目录前言一、__new__方法简介1、初始化数据加载+解析类实例2、初始化数据加载重写new方法+解析类实例二、单例模式1、用new方法如何实现单例模式2、如何控制类仅执行一次初始化

前言

python中类的构造方法__new__方法有何作用?

Python类中有些方法名、属性名的前后都添加__双下画线,这种方法、属性通常属于Python的特殊方法和特殊属性。通过重写这些方法或直接调用这些方法来实现特殊功能。今天来聊聊构造方法__new__实际程序的应用场景。

我们知道常见的初始化__init__方法,可以重写实现自己想要的初始化逻辑。最近实际业务开发过程中碰到一类问题比如数据资源加载缓存机制的实现,用到了魔法方法中构造方法,其中__init__()和__new__是对象的构造器,合理运用将有效提高程序性能。

希望大家多结合自己的业务需求深刻理解,灵活运用,使得代码变得更加优雅。

一、__new__方法简介

接下来通过实例逐步详细阐述__ new __ 方法在类初始化过程中是什么样的存在!

1、初始化数据加载+解析类实例

class Solution(object):
    def __init__(self, name=None,data=None):
        self.name = name
        self.data = data
        #初始化加载数据
        self.xml_load(self.data)

    def xml_load(self,data):
        print("初始化init",data)

    def Parser(self):
        print("解析完成finish",self.name)

a = Solution(name="A111",data=10)
a.Parser()
b = Solution(name="A112",data=20)
b.Parser()
# print(a)与 print(b)返回了类的名称和对象的地址
print(a)
print(b)
# 可以使用内置函数id()查看python对象的内存地址
print(id(a))
print(id(b))

初始化init 10
解析完成finish A111
初始化init 20
解析完成finish A112
<__main__.Solution object at 0x0000024A3AF28D48>
<__main__.Solution object at 0x0000024A3B055C48>
2517839809864
2517841042504

注:

1、代码实例化类过程

一般使用__init__()方法初始化一个类的实例,当代码中实例化一个类的时候,第一个调用执行的是__new__()方法,当定义的类中没有重新定义__new__()方法时候,Python会默认调用该父类的__new__()方法来构造该实例,new方法就是先创建一个空间,然后每次创建一个实例化的对象,然后用开辟的空间存放这个实例化对象; 再次创建一个实例化的对象的时候,再用new方法开辟一个空间存放实例化对象。注意只有继承了object的类才有此方法。

2、内存地址和对象可相互转换

#通过_ctypes的api进行对内存地址的对象
import _ctypes
obj = _ctypes.PyObj_FromPtr(id(a))
#打印出来通过内存地址寻找到的对象
print(obj)

print(id(a))与 print(id(b))打印出来的都是内存地址(10进制),print(a)与 print(b)返回了类的名称和对象的地址,但是两者并不相同。每次实例化类都会创建分配不同的对象地址,因此,代码实例化类过程中返回类对象的地址引用也就不同。

2、初始化数据加载重写new方法+解析类实例

class Solution:
    """
    注:new方法是为实例化对象创建空间的方法,现在new方法被改写,没有将实例化对象引用返回给python的解释器
    无法为实例化对象创建空间存储,所以运行代码会报错。也没有完成初始化操作。
    """

    def __new__(cls, *args, **kwargs):
        print("对象创建空间")
        cls.instance = super().__new__(cls)
        print(cls.instance)
        # return cls.instance   #若未返回实例对象引用,实例化方法将报错:AttributeError: 'NoneType' object has no attribute 'Parser'

    def __init__(self,name,data):
        self.name = name
        self.data = data
        self.xml_load(self.data)

    def xml_load(self,data):
        print("初始化init", data)

    def Parser(self):
        print("解析完成finish",self.data)

a = Solution("A111",10)
a.Parser()
print(id(a))

注:

1、__init__()方法和__new__()方法区别

__new__()方法用于创建实例,类实例化之前会首先调用,它是class的方法,是个静态方法。而__init__()方法用户初始化实例,该方法用在实例对象创建后被调用,它是实例对象的方法,用于设置类实例对象的一些初始值。

如果类中同时出现了__init__()方法和__new__()方法,则先调用__new__()方法后调用__init__()方法。__new__()方法是创建实例的第一步,执行完了需要返回创建的类的实例,否则则报错,无法执行__init__()方法。其中,__init__()方法将不返回任何信息。

2、重写__new__()方法

def __new__(cls, *args, **kwargs):
    print(cls)  # cls 代表的是Solution这个类本身<class'__ main __.Solution'>
    cls.instance = super().__new__(cls)  # object().__ new __()
    print(cls.instance)
    return cls.instance

super()与object.__new__(cls)都是在调用父类的new方法,必须把父类的new方法返回给函数,才能开辟空间,因此必须添加return。代码的执行顺序是:先执行new方法,然后执行init方法,最后是其它方法。

二、单例模式

单例模式最初的定义出现于《设计模式》:“保证一个类仅有一个实例,并提供一个访问它的全局访问点。”

单例的使用主要是在需要保证全局只有一个实例可以被访问的情况,比如系统日志的输出、操作系统的任务管理器等。

1、用new方法如何实现单例模式

class Solution:
    # 1、记录第一个被创建对象的引用,代表着类的私有属性
    _instance = None # 静态变量 存储在类的命名空间里的

    def __init__(self,name,data):
        self.name = name
        self.data = data
        self.xml_load(self.data)

    def __new__(cls, *args, **kwargs):
        # 2.判断该类的属性是否为空;对第一个对象没有被创建,我们应该调用父类的方法,为第一个对象分配空间
        if cls._instance == None:  
            # 3.把类属性中保存的对象引用返回给python的解释器
            cls._instance = object.__new__(cls)  # 3
            return cls._instance
        # 如果cls._instance不为None,直接返回已经实例化了的实例对象
        else:
            return cls._instance  # 必须把地址返回给new方法,让它有存储空间

    def xml_load(self,data):
        print("初始化init",self.name,data)

    def Parser(self):
        print("解析完成finish",self.name)

a = Solution("A11",10)  #第一次开辟一个对象空间地址,后面创建都是在该地址上进行的
a.Parser()
b = Solution("A12",20)  #b把a覆盖掉
b.Parser()
print(id(a))
print(id(b))
# 内存地址,而且它们的内存地址都是一样的
print(a.name)
print(b.name)

输出

初始化init A11 10
解析完成finish A11
初始化init A12 10
解析完成finish A12
2465140199816
2465140199816
A12
A12 

注:

1、单例模式始终只有一个空间,该空间一直重复利用。

首先定义一个类的私有属性_instance,用来记录第一个被创建对象的引用,如果cls._instance为None说明该类还没有实例化过,则实例化该类并返回实例对象。

通过以下数据测试可知,print(obj.name, obj.data)最后打印出来的都是A12,第一次打印"A11"时,属性为空,执行if语句开辟了一个空间存放该属性;从 第二次打已经开辟了空间 ,执行else语句,直接返回"A12"到原来的空间中,把前面的盖数据覆盖掉。

def task(id,data):
    obj = Solution("{0}".fORMat(id), "{0}".format(data))
    print(obj.name, obj.data)

import threading
ID=["A11","A12","A13","A14","A12"]
DATA=[10,20,30,40,20]
for i in range(5):
    t = threading.Thread(target=task(ID[i],DATA[i]), args=[i, ])
    t.start()

输出

<__main__.Solution object at 0x00000221B2129148>
初始化init A11 10
A11 10
初始化init A12 20
A12 20
初始化init A13 30
A13 30
初始化init A14 40
A14 40
初始化init A12 20
A12 20

2、单例模式另外一种实现方法 

def __new__(cls,*args,**kwargs):
    # hasattr查询目标并判断有没有,not  1==1  返回的是False
    # if语句后面的
    # not 条件整体为True时,执行cls.instance = object....代码

    # if语句后面的
    # not 条件整体为False时,执行return代码
    if not hasattr(cls,"instance"):     # hasattr查、判断的作用
        cls.instance = object.__new__(cls)
    return cls.instance

2、如何控制类仅执行一次初始化方法

以上实现了单例模式对象空间的重复利用,但是有时候我们想初始化过程只加载一次,避免频繁请求浪费系统资源(如数据库连接请求数据)。

class Solution:
    #定义类变量
    # 记录第一个被创建对象的引用,代表着类的私有属性
    _instance = None
    #记录是否执行过初始化动作
    init_flag = False

    def __init__(self,name,data):
        self.name = name
        self.data = data
        #使用类名调用类变量,不能直接访问。
        if Solution.init_flag:
            return
        self.xml_load(self.data)
        # 修改类属性的标记
        Solution.init_flag = True

    def __new__(cls, *args, **kwargs):
        # 判断该类的属性是否为空;对第一个对象没有被创建,我们应该调用父类的方法,为第一个对象分配空间
        if cls._instance == None: 
            # 把类属性中保存的对象引用返回给python的解释器
            cls._instance = object.__new__(cls)  
            return cls._instance
        #如果cls._instance不为None,直接返回已经实例化了的实例对象
        else:
            return cls._instance 

    def xml_load(self,data):
        print("初始化init",self.name,data)

    def Parser(self):
        print("解析完成finish",self.name)

a = Solution("A11",10)  #第一次实例化对象地址,后面创建都是在该地址上进行的
a.Parser()
b = Solution("A12",20)  #b把a覆盖掉
b.Parser()
print(id(a))
print(id(b))
print(a.name)
print(b.name)

输出

初始化init A11 10
解析完成finish A11
解析完成finish A12
2280855720328
2280855720328
A12
A12 

注:

1、单例模式下仅加载一次初始化过程。

这时候我们在类空间中再添加一个init_flag属性来记录是否已经执行过初始化操作即可实现加载一次初始化过程。从以上两次实例化过程结果来看,对象引用地址不变,结果被最后一次实例化数据覆盖且初始化init只被打印一次。

2、单例模式下一次资源加载注意点

单例模式下控制类仅进行一次初始化过程适用于资源一次性加载进缓存的过程,对于多进程应用可采用多例模式实现。

三、多例模式

多个实例对象空间引用地址完全独立,从而保持避免不同请求资源不被占用。将同一个对象请求归为同一个实例。

class Solution:
    ##定义类实例化对象字典,即不同的实例对象对应不同的对象空间地址引用
    _loaded = {}

    def __init__(self,name,data):
        self.name = name
        self.data = data
        self.xml_load(self.data)

    def __new__(cls, name,*args):
        if cls._loaded.get(name) is not None:
            client = cls._loaded.get(name)
            print(f"已经存在访问对象 {name}")
            print(client)
            return client
        # 把类属性中保存的对象引用返回给python的解释器
        print(f"正在创建访问对象 {name}")
        client = super().__new__(cls)
        # 为该类实例name添加一个空间对象地址引用
        print(client)    
        cls._loaded[name] = client
        return client

    def xml_load(self,data):
        print("初始化init",self.name,data)

    def Parser(self):
        print("解析完成finish",self.name)

if __name__ == '__main__':
    print("多例模式实例")
    a = Solution("A11",10)
    a.Parser()
    b = Solution("A11",10)
    b.Parser()
    c = Solution("A12", 20)
    c.Parser()
    print(f"{a is b}")
    print(a.name)
    print(b.name)
    print(c.name)

注:

1、多例模式始终具有多个空间,不同空间完全独立。

我们在类空间中定义类实例化对象字典,即建立不同的实例对象和对象空间地址引用键值对,从而实现多例模式。通过类字典判断实例对象是否创建,节省创建的成本。

2、多例模式测试过程

当创建相同的实例对象name="A11"时,程序首先在实例池中搜索cls._loaded.get(name),若存在则直接返回已创建的实例对象空间。多例模式完美的实现了不同访问对象具体不同的实例化对象地址。

3、多例模式下缓冲机制的实现

进一步优化多例模式初始化过程,比如读取文件或者数据库时仅进行一次初始化加载。

class Solution:
    ##定义类实例化对象字典,即不同的实例对象对应不同的对象空间地址引用
    _loaded = {}

    def __new__(cls, name,data,*args):
        if cls._loaded.get(name) is not None:
            client = cls._loaded.get(name)
            print(f"已经存在访问对象 {name}")
            print(client)
            return client
        print(f"正在创建访问对象 {name}")
        # 把类属性中保存的对象引用返回给python的解释器
        client = super().__new__(cls)
        print(client)
        # 为该类实例name添加一个空间对象地址引用
        cls._loaded[name] = client
        client._init_db(name,data)
        return client

    def _init_db(self,name,data):
        self.name = name
        self.data = data
        self.xml_load(self.data)

    def xml_load(self,data):
        print("初始化init",self.name,data)

    def Parser(self):
        print("解析完成finish",self.name)

if __name__ == '__main__':
    print("多例模式实例-缓存")
    a = Solution("A11",10)
    a.Parser()
    b = Solution("A11",10)
    b.Parser()
    c = Solution("A12", 20)
    c.Parser()
    print(f"{a is b}")
    print(a.name)
    print(b.name)
    print(c.name)

输出

正在创建访问对象 A11
<__main__.Solution object at 0x0000024198989148>
初始化init A11 10
解析完成finish A11
已经存在访问对象 A11
<__main__.Solution object at 0x0000024198989148>
解析完成finish A11
正在创建访问对象 A12
<__main__.Solution object at 0x00000241989891C8>
初始化init A12 20
解析完成finish A12
True
A11
A11
A12

注:多例模式下多个实例化对象均只进行一次初始化过程。

重写__new__方法中每个实例对象创建后绑定初始化_init_db()方法执行一次,后面遇到同一个实例对象将不会发生什么,直接返回已创建的实例对象。从测试结果来看,创建相同的实例对象name="A11"时,第二次将略过初始化数据加载过程,很好的实现了缓存机制。

总结

本文结合项目背景详细介绍了__new__方法实现单例模式和多例模式以及缓存机制的实现!

1、__new__ 方法是在类创建实例的时候自动调用的。

2、 实例是通过类里面的 __ new __ 方法是在类创建出来的。

3、 先调用__new__ 方法创建实例,再调用 __ init __方法初始化实例。

4、 __new__ 方法,后面的括号里面的cls代表的是类本身。

5、__new__ 方法,判断类属性为空就去开辟空间,否则复用原来的地址。

更多的特殊方法比如1、自我描述方法:__repr__2、析构方法:__del__ 3、列出对象所有属性(包括方法)名:__dir__4、__dict__属性:查看对象内部所有属性名和属性值组成的字典5、__getattr__\__setattr__等。

当然还有metaclass类的__new__方法,可以动态修改程序中的一批类,这个功能在开发一些基础性的框架时非常有用,可以使用metaclass为某一批需要通用功能的类添加方法。

以上就是详解Python中__new__方法的作用的详细内容,更多关于Python __new__的资料请关注编程网其它相关文章!

--结束END--

本文标题: 详解Python中__new__方法的作用

本文链接: https://lsjlt.com/news/144568.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 详解Python中__new__方法的作用
    目录前言一、__new__方法简介1、初始化数据加载+解析类实例2、初始化数据加载重写new方法+解析类实例二、单例模式1、用new方法如何实现单例模式2、如何控制类仅执行一次初始化...
    99+
    2024-04-02
  • 一文详解Python中__new__方法的作用
    前言Python中类的构造方法__new__方法有何作用?Python类中有些方法名、属性名的前后都添加__双下画线,这种方法、属性通常属于Python的特殊方法和特殊属性。通过重写这些方法或直接调用这些方法来实现特殊功能。今天来聊聊构造方...
    99+
    2023-05-14
    Python __new__
  • Python 中__new__方法的作用是什么
    今天就跟大家聊聊有关Python 中__new__方法的作用是什么,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。__new__ 的作用在Python中__new__方法与__init...
    99+
    2023-06-02
  • Python中__new__方法有什么作用
    本篇内容介绍了“Python中__new__方法有什么作用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、__new__方法简介接下来通过...
    99+
    2023-07-06
  • 详解Python中的__new__、__init__、__call__三个特殊方法
    __new__: 对象的创建,是一个静态方法,第一个参数是cls。(想想也是,不可能是self,对象还没创建,哪来的self) __init__ : 对象的初始化, 是一个实例方法,第一个参数是self。 ...
    99+
    2022-06-04
    详解 方法 Python
  • Python中__new__方法有什么用
    这篇文章主要为大家展示了“Python中__new__方法有什么用”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Python中__new__方法有什么用”这篇文章吧。一、__new__方法简介接...
    99+
    2023-06-29
  • Python中class内置方法__init__与__new__作用与区别解析
    目录背景__init__方法作用__new__方法作用__init__ && __new__联系使用__new__的场景定义、继承immutable class使用m...
    99+
    2024-04-02
  • 实例解析Python中的__new__特殊方法
    __new__ 方法是什么? 如果将类比喻为工厂,那么__init__()方法则是该工厂的生产工人,__init__()方法接受的初始化参 数则是生产所需原料,__init__()方法会按照方法中的语句负责...
    99+
    2022-06-04
    实例 方法 Python
  • Python 中类的构造方法 __New__的妙用
    目录1、概述 2、__new__ 和 __init__ 的区别3、应用1:改变内置的不可变类型4、应用2:实现一个单例5、应用3:客户端缓存6、应用4:不同文件不同的解密方法1、概述...
    99+
    2024-04-02
  • Python中__init__和__new__方法有什么用
    这篇文章给大家分享的是有关Python中__init__和__new__方法有什么用的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。__ init __方法init方法负责对象的初始化,系统执行该方法前,其实该对象...
    99+
    2023-06-02
  • 详解python中的IO操作方法
    目录python文件I/Oraw_input函数input函数打开和关闭文件open 函数file对象的属性close()方法write()方法read()方法Python with...
    99+
    2024-04-02
  • python操作yaml的方法详解
    目录一、参考链接二、python类型转换为yaml三、yaml转换为python类型总结一、参考链接 https://pyyaml.org/wiki/PyYAMLDocumentat...
    99+
    2024-04-02
  • Python利用flask操作Redis的方法详解
    目录简单示例python flask 操作 Redis 更多内容通过 Flask 存储字符串、列表和字典使用 Flask 操作 Redis 可以通过安装 Redis 的 Python...
    99+
    2023-02-01
    Python flask操作Redis Python flask Redis Python 操作Redis
  • 详解Python中魔法方法的使用
    目录迭代器的大小元编程自省(introspection)魔法属性创建自己魔法方法python中的魔法方法是一些可以让你对类添加“魔法”的特殊方法,它们经常是两...
    99+
    2022-12-19
    Python魔法方法使用 Python魔法方法
  • python中OrderedDict的使用方法详解
    很多人认为python中的字典是无序的,因为它是按照hash来存储的,但是python中有个模块collections(英文,收集、集合),里面自带了一个子类 OrderedDict,实现了对字典对象中元素...
    99+
    2022-06-04
    使用方法 详解 python
  • python中defaultdict方法的使用详解
    目录默认值可以很方便使用判断语句检查使用dict.setdefault()方法使用collections.defaultdict类defaultdict类是如何实现的在旧版本的Pyt...
    99+
    2023-05-16
    python defaultdict用法 python defaultdict使用 python defaultdict
  • python中.format()方法使用详解
    目录前言一、简单使用方法1.无参数2. key value3. 列表4. 字典5. 类6. 魔法参数二、参数使用方法前言 format语法格式: str.format()str是指字...
    99+
    2024-04-02
  • python中validators库的使用方法详解
    目录前言validators.between(value, min=None, max=None)validators.domain(value)validators.email(v...
    99+
    2024-04-02
  • Python对PDF文件的常用操作方法详解
    目录工具从PDF中提取文本旋转和叠加页面加密PDF文件创建PDF文件补充工具 python3.7 Pycharm PDF PyPDF2 reportlab 从PDF中提取文本 PyP...
    99+
    2024-04-02
  • 详解python中静态方法staticmethod用法
    在开发的时候, 可以使用类对方法进行封装,如果某一个方法需要访问到对象的实例属性,可以把这个方法封装成一个实例方法。如果某一个方法不需要访问对象的实例属性,但是需要访问到类的类属性,...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作