欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

python中__new__和__init__的實(shí)現(xiàn)

 更新時(shí)間:2024年05月14日 10:05:19   作者:小徐也要努力鴨  
在Python中,每個(gè)對(duì)象都有兩個(gè)特殊的方法__new__和__init__,本文主要介紹了python中__new__和__init__的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下

1 前言

在Python中,每個(gè)對(duì)象都有兩個(gè)特殊的方法:__new__和__init__。這兩個(gè)方法在對(duì)象的創(chuàng)建和初始化過程中起著重要的作用,但它們的功能和用法有所不同。

1.1 功能上的區(qū)別

__new__方法是Python中的一個(gè)魔術(shù)方法(Magic Method),用于創(chuàng)建一個(gè)新的對(duì)象實(shí)例。當(dāng)我們?cè)赑ython中創(chuàng)建一個(gè)對(duì)象時(shí),實(shí)際上是調(diào)用了__new__方法來創(chuàng)建一個(gè)新的對(duì)象實(shí)例,然后再調(diào)用__init__方法來初始化這個(gè)對(duì)象。

__init__方法是Python中的一個(gè)普通方法,用于初始化一個(gè)已經(jīng)存在的對(duì)象。當(dāng)我們使用__new__方法創(chuàng)建一個(gè)新的對(duì)象實(shí)例后(不可為None),就會(huì)調(diào)用這個(gè)對(duì)象的__init__方法來對(duì)對(duì)象進(jìn)行初始化。

1.2 參數(shù)上的區(qū)別

__new__方法通常需要三個(gè)參數(shù):第一個(gè)參數(shù)是類(cls,即class),第二個(gè)參數(shù)是傳入的參數(shù)列表(args),即位置參數(shù);第三個(gè)參數(shù)也是傳入的參數(shù)列表(kwargs),即關(guān)鍵字參數(shù)。__new__方法的返回值是一個(gè)新的對(duì)象實(shí)例。

__init__方法通常需要1個(gè)或以上參數(shù):第一個(gè)參數(shù)是對(duì)象實(shí)例(self,也就是__new__方法返回的對(duì)象實(shí)例),后續(xù)可有可無的若干參數(shù)是傳入的參數(shù)列表(args),常用于設(shè)置實(shí)例化對(duì)象屬性。__init__方法的返回值是None。

1.3 調(diào)用時(shí)機(jī)上的區(qū)別

__new__方法在創(chuàng)建對(duì)象時(shí)被調(diào)用,它的調(diào)用時(shí)機(jī)是在__init__方法之前。__new__方法的返回值是一個(gè)新的對(duì)象實(shí)例,這個(gè)實(shí)例會(huì)被傳遞給__init__方法進(jìn)行初始化。

__init__方法在對(duì)象被創(chuàng)建后被調(diào)用,它的調(diào)用時(shí)機(jī)是在__new__方法之后。__init__方法用于對(duì)已經(jīng)存在的對(duì)象進(jìn)行初始化,它的參數(shù)列表通常包括傳遞給類的構(gòu)造函數(shù)的參數(shù)。

上述可知,沒有__new__方法,我們就無法創(chuàng)建新的對(duì)象實(shí)例;沒有__init__方法,我們就無法對(duì)已經(jīng)存在的對(duì)象進(jìn)行初始化。兩者功能上雖有差別,但是是用于共同來創(chuàng)建和初始化一個(gè)對(duì)象的,所以兩者均很重要,下面則是具體使用的分析。

2 使用

2.1 簡單示例

class Clazz:

    def __new__(cls, *args, **kwargs):
        print("調(diào)用__new__")
        print(f'cls:{cls}, args:{args}, kwargs: {kwargs}')

    def __init__(self, name):
        print("調(diào)用__init__")
        print(f'self:{self}, name:{name}')
        self.name = name


clazz = Clazz("xiaoxu")

執(zhí)行結(jié)果:

調(diào)用__new__
cls:<class '__main__.Clazz'>, args:('xiaoxu',), kwargs: {}

可以看到,上述代碼先執(zhí)行了__new__,但是并未執(zhí)行__init__方法,因?yàn)橹挥挟?dāng)我們使用__new__方法創(chuàng)建一個(gè)新的對(duì)象實(shí)例后,才會(huì)調(diào)用這個(gè)對(duì)象的__init__方法來對(duì)對(duì)象進(jìn)行初始化。

__new__是一個(gè)內(nèi)置staticmethod,其首個(gè)參數(shù)必須是type類型,即要實(shí)例化的class本身,其負(fù)責(zé)為傳入的class type分配內(nèi)存、創(chuàng)建一個(gè)新實(shí)例并返回該實(shí)例,該返回值其實(shí)就是后續(xù)執(zhí)行__init__函數(shù)的入?yún)elf。

參考Python的源碼typeobject.c中定義的type_call函數(shù):

static PyObject *
 type_call(PyTypeObject *type, PyObject *args, PyObject *kwds)
 {
     PyObject *obj;
 
    if (type->tp_new == NULL) {
         PyErr_Format(PyExc_TypeError,
                     "cannot create '%.100s' instances",
                      type->tp_name);
        return NULL;
     }
 ...
    obj = type->tp_new(type, args, kwds); # 這里先執(zhí)行tp_new分配內(nèi)存、創(chuàng)建對(duì)象返回obj
     obj = _Py_CheckFunctionResult((PyObject*)type, obj, NULL);
 ...
     type = Py_TYPE(obj); # 這里獲取obj的class類型,并判定有tp_init則執(zhí)行該初始化函數(shù)
     if (type->tp_init != NULL) {
         int res = type->tp_init(obj, args, kwds);
        if (res < 0) {
             assert(PyErr_Occurred());
            Py_DECREF(obj);
             obj = NULL;
         }
         else {
             assert(!PyErr_Occurred());
        }
    }
     return obj;
}

執(zhí)行代碼class(*args, **kwargs) 時(shí),其會(huì)先調(diào)用type_new函數(shù)(__new__方法)分配內(nèi)存創(chuàng)建實(shí)例并返回為obj,而后通過Py_TYPE(obj)獲取其具體type,再進(jìn)一步檢查type->tp_init不為空則執(zhí)行該初始化函數(shù)(也就是__init__方法)。

若__new__方法返回為None,依然不會(huì)執(zhí)行__init__方法:

class Clazz:

    def __new__(cls, *args, **kwargs):
        print("調(diào)用__new__")
        print(f'cls:{cls}, args:{args}, kwargs: {kwargs}')
        return None

    def __init__(self, name):
        print("調(diào)用__init__")
        print(f'self:{self}, name:{name}')
        self.name = name

clazz = Clazz("xiaoxu")
print(clazz)

# 調(diào)用__new__
# cls:<class '__main__.Clazz'>, args:('xiaoxu',), kwargs: {}
# None

作如下修改:

class Clazz:

    def __new__(cls, *args, **kwargs):
        print("調(diào)用__new__")
        print(f'cls:{cls}, args:{args}, kwargs: {kwargs}')
        # x = super().__new__(cls) 等同寫法
        x = super(Clazz, cls).__new__(cls)
        print("self_first:", x)
        return x

    def __init__(self, name, age=99):
        print("調(diào)用__init__")
        print(f'self:{self}, name:{name}, age: {age}')
        super(Clazz, self).__init__()
        self.name = name
        # Cannot return a value from __init__
        # __init__是不需要返回值的
        # return None


clazz = Clazz("xiaoxu", age=66)
print(clazz)

因?yàn)閜ython中任何類都繼承于object 類,上述的super().__new__(cls),其實(shí)就是調(diào)用內(nèi)置的object.__new__()方法來創(chuàng)建對(duì)象實(shí)例。一般的形式有super(類名, cls).__new__(cls, … …)。

執(zhí)行結(jié)果如下:

在這里插入圖片描述

結(jié)果也印證了上述提到的,__new__方法的返回值,就是后續(xù)執(zhí)行__init__函數(shù)的入?yún)elf

小結(jié)說明:

1、繼承自object的新式類才有__new__。

2、__new__至少要有一個(gè)參數(shù)cls,代表當(dāng)前類,此參數(shù)在實(shí)例化時(shí)由Python解釋器自動(dòng)識(shí)別。

3、__new__必須要有返回值,返回實(shí)例化出來的實(shí)例,這點(diǎn)在自己實(shí)現(xiàn)__new__時(shí)要特別注意,可以return父類(通過super(當(dāng)前類名, cls))__new__出來的實(shí)例,或者直接是object的__new__出來的實(shí)例。

4、__init__有一個(gè)參數(shù)self,就是這個(gè)__new__返回的實(shí)例,__init__在__new__的基礎(chǔ)上可以完成一些其它初始化的動(dòng)作,__init__不需要返回值。

5、如果__new__創(chuàng)建的是當(dāng)前類的實(shí)例,會(huì)自動(dòng)調(diào)用__init__函數(shù),通過return語句里面調(diào)用的__new__函數(shù)的第一個(gè)參數(shù)是 cls 來保證是當(dāng)前類實(shí)例,如果是其他類的類名,那么實(shí)際創(chuàng)建返回的就是其他類的實(shí)例,就不會(huì)調(diào)用當(dāng)前類的__init__函數(shù),也不會(huì)調(diào)用其他類的__init__函數(shù)。

6、在定義子類時(shí)沒有重新定義__new__()時(shí),Python默認(rèn)是調(diào)用該類的直接父類的__new__()方法來構(gòu)造該類的實(shí)例,如果該類的父類也沒有重寫__new__(),那么將一直按此規(guī)矩追溯至object的__new__()方法,因?yàn)閛bject是所有新式類的基類。

7、如果子類中重寫了__new__()方法,那么你可以自由選擇任意一個(gè)的其他的新式類(必定要是新式類,只有新式類必定都有__new__(),因?yàn)樗行率筋惗际莖bject的后代,而經(jīng)典類則沒有__new__()方法)的__new__()方法來制造實(shí)例,包括這個(gè)新式類的所有前代類和后代類,只要它們不會(huì)造成遞歸死循環(huán)。不能調(diào)用自己的__new__,因?yàn)槭沁f歸死循環(huán)調(diào)用。

8、對(duì)于子類的__init__,其調(diào)用規(guī)則跟__new__是一致的,當(dāng)然如果子類和父類的__init__函數(shù)都想調(diào)用,可以在子類的__init__函數(shù)中加入對(duì)父類__init__函數(shù)的調(diào)用。

2.2 __new__的作用

參考Python官方文檔,__new__方法主要是當(dāng)你繼承一些不可變的class時(shí)(比如int, str, tuple), 提供給你一個(gè)自定義這些類的實(shí)例化過程的途徑,另外就是實(shí)現(xiàn)自定義的metaclass。

(1)根據(jù)int舉個(gè)栗子:

class PositiveInteger(int):
    def __new__(cls, *args, **kwargs):
        print("param:", args)
        return super(PositiveInteger, cls).__new__(cls, abs(args[0]))


p = PositiveInteger(-5)
print(p)

執(zhí)行結(jié)果:

在這里插入圖片描述

(2)再根據(jù)tuple舉個(gè)栗子:

__new__方法自定義要求保證實(shí)例創(chuàng)建、并且必須記得返回實(shí)例對(duì)象的一系列固定邏輯正確,而__init__方法相當(dāng)簡單只需要設(shè)置想要設(shè)置的屬性即可,出錯(cuò)的可能性很小,絕大部分場景用戶完全只需要更改__init__方法,用戶無需感知__new__的相關(guān)邏輯。

理論上是可以通過多次調(diào)用__init__函數(shù)進(jìn)行初始化的,但是任何實(shí)例都只可能被創(chuàng)建一次,因?yàn)槊看握{(diào)用__new__函數(shù)理論上都是創(chuàng)建一個(gè)新實(shí)例返回(特殊情況如單例模式則只返回首次創(chuàng)建的實(shí)例),而不會(huì)存在重新構(gòu)造已有實(shí)例的情況。

針對(duì)__init__可被多次調(diào)用的情況,mutable和immutable對(duì)象會(huì)有不同的行為,因?yàn)閕mmutable對(duì)象(不可變對(duì)象)從語義上來說首次創(chuàng)建、初始化完成后就不可以修改了,所以后續(xù)再調(diào)用其__init__方法應(yīng)該無任何效果才對(duì),示例如下:

a = [1, 2, 3]
print(id(a), a)

# 對(duì)list實(shí)例重新初始化改變其取值為[4, 5]
a.__init__([4, 5])
print(id(a), a)

b = (1, 2, 3)
print(id(b), b)

# 對(duì)tuple實(shí)例嘗試重新初始化并無任何效果,
# 符合對(duì)immutable類型的行為預(yù)期
b.__init__((4, 5))
print(id(b), b)

執(zhí)行結(jié)果如下:

在這里插入圖片描述

定義、繼承immutable class,tuple的栗子:

class PositiveTuple(tuple):

    def __init__(self, *args, **kwargs):
        print('get in init one, self:', id(self), self)
        # 直接通過索引賦值的方式會(huì)報(bào): 
        # PositiveTuple' object does not support item assignment
        # for i, x in enumerate(self):
        # self[i] = abs(x)
        # 只能嘗試對(duì)self整體賦值
        self = tuple(abs(x) for x in self)
        print('get in init two, self:', id(self), self)


t = PositiveTuple([-3, -2, 5])
print(id(t), t)

執(zhí)行結(jié)果:

在這里插入圖片描述

可以看到雖然在__init__中重新對(duì)self進(jìn)行了賦值,其實(shí)只是相當(dāng)于新生成了一個(gè)tuple對(duì)象28859528,t指向的依然是最開始生成好的實(shí)例28847512。

如下為使用自定義__new__的方法:

class PositiveTuple(tuple):

    def __new__(cls, *args, **kwargs):
        self = super().__new__(cls, *args, **kwargs)

        print('get in init one, self:', id(self), self)
        # 直接通過索引賦值的方式會(huì)報(bào): PositiveTuple' object does not support item assignment
        # for i, x in enumerate(self):
        # self[i] = abs(x)
        # 只能嘗試對(duì)self整體賦值
        self = tuple(abs(x) for x in self)
        print('get in init two, self:', id(self), self)
        return self


t = PositiveTuple([-3, -2, 5])
print(id(t), t)

執(zhí)行結(jié)果如下:

在這里插入圖片描述

可以看到一開始調(diào)用super.__new__時(shí)其實(shí)已經(jīng)創(chuàng)建了一個(gè)實(shí)例27667864,而后通過新生成一個(gè)全部轉(zhuǎn)化為正數(shù)的tuple 27679880賦值后返回,最終返回的實(shí)例t也就最終需要的全正數(shù)tuple。

(3)通過__new__方法實(shí)現(xiàn)單實(shí)例:

class Singleton(object):
    def __new__(cls):
        if not hasattr(cls, 'instance'):
            cls.instance = super(Singleton, cls).__new__(cls)
        # 每次生成的都是同一個(gè)實(shí)例
        return cls.instance


s1 = Singleton()
s2 = Singleton()

s1.attr1 = 'xiaoxu'
print(s1.attr1, s2.attr1)
print(s1 is s2)  # 返回True表明是同一個(gè)實(shí)例
print(s1 == s2)
# xiaoxu xiaoxu
# True
# True

一般比如字典使用==比較是比較值相等,is是比較地址相等。而在Class對(duì)象比較中,使用==和is都是比較地址相等(可以通過自定義__eq__來實(shí)現(xiàn)想要的效果)。

通過上述的分析,在實(shí)際應(yīng)用中,我們通常就可以同時(shí)使用__new__和__init__方法來創(chuàng)建和初始化一個(gè)對(duì)象。通過重寫這兩個(gè)方法,我們可以自定義對(duì)象的創(chuàng)建和初始化過程,從而實(shí)現(xiàn)更加靈活和強(qiáng)大的功能。

到此這篇關(guān)于python中__new__和__init__的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)python __new__和__init__內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Python 爬蟲爬取指定博客的所有文章

    Python 爬蟲爬取指定博客的所有文章

    因?yàn)镚oogle App Engine 被墻,我無法繼續(xù)完善我的Moven project 還有20+天才回去,怕到時(shí)候會(huì)忘記project的進(jìn)度和細(xì)節(jié)就趁著個(gè)冷的什么都不想干的時(shí)候, 大概的總結(jié)一下
    2016-02-02
  • python進(jìn)程的狀態(tài)、創(chuàng)建及使用方法詳解

    python進(jìn)程的狀態(tài)、創(chuàng)建及使用方法詳解

    這篇文章主要介紹了python進(jìn)程的狀態(tài)、創(chuàng)建及使用方法,結(jié)合實(shí)例形式詳細(xì)分析了Python進(jìn)程的概念、原理、工作狀態(tài)、創(chuàng)建以及使用方法,需要的朋友可以參考下
    2019-12-12
  • Python 中閉包與裝飾器案例詳解

    Python 中閉包與裝飾器案例詳解

    這篇文章主要介紹了Python 中閉包與裝飾器案例詳解,本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • python之pkl文件的用法及說明

    python之pkl文件的用法及說明

    這篇文章主要介紹了python之pkl文件的用法及說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • Python實(shí)現(xiàn)可設(shè)置持續(xù)運(yùn)行時(shí)間、線程數(shù)及時(shí)間間隔的多線程異步post請(qǐng)求功能

    Python實(shí)現(xiàn)可設(shè)置持續(xù)運(yùn)行時(shí)間、線程數(shù)及時(shí)間間隔的多線程異步post請(qǐng)求功能

    這篇文章主要介紹了Python實(shí)現(xiàn)可設(shè)置持續(xù)運(yùn)行時(shí)間、線程數(shù)及時(shí)間間隔的多線程異步post請(qǐng)求功能,涉及Python網(wǎng)絡(luò)請(qǐng)求的創(chuàng)建、發(fā)送、響應(yīng)、處理等相關(guān)操作技巧,需要的朋友可以參考下
    2018-01-01
  • python實(shí)現(xiàn)跨年表白神器--你值得擁有

    python實(shí)現(xiàn)跨年表白神器--你值得擁有

    這篇文章主要介紹了python實(shí)現(xiàn)跨年表白神器的方法,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2021-01-01
  • Python合并2個(gè)字典成1個(gè)新字典的方法(9種)

    Python合并2個(gè)字典成1個(gè)新字典的方法(9種)

    這篇文章主要介紹了Python合并2個(gè)字典成1個(gè)新字典的方法,本文通過實(shí)例代碼給大家分享9中方法,需要的朋友可以參考下
    2019-12-12
  • python重要函數(shù)eval多種用法解析

    python重要函數(shù)eval多種用法解析

    這篇文章主要介紹了python重要函數(shù)eval多種用法解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-01-01
  • 使用Python腳本zabbix自定義key監(jiān)控oracle連接狀態(tài)

    使用Python腳本zabbix自定義key監(jiān)控oracle連接狀態(tài)

    這篇文章主要介紹了使用Python腳本zabbix自定義key監(jiān)控oracle連接狀態(tài),本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-08-08
  • python爬蟲 urllib模塊url編碼處理詳解

    python爬蟲 urllib模塊url編碼處理詳解

    這篇文章主要介紹了python爬蟲 urllib模塊url編碼處理詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-08-08

最新評(píng)論