Python 實(shí)現(xiàn)slots的繼承

__slots__是python的一大神器。

它有兩大的特點(diǎn):

  1. 限制 MonkeyPatch,阻止python的一部分動(dòng)態(tài)性。將對(duì)象中允許擁有的實(shí)例屬性事先約定好。
  2. 使用它會(huì)提升實(shí)例屬性訪問(wèn)性能。

python文檔中這樣介紹它

3.3.2.4. slots
slots 允許我們顯式地聲明數(shù)據(jù)成員(例如特征屬性)并禁止創(chuàng)建 dictweakref (除非是在 slots 中顯式地聲明或是在父類中可用。)
相比使用 dict 此方式可以顯著地節(jié)省空間。 屬性查找速度也可得到顯著的提升。

首先第一點(diǎn),python的動(dòng)態(tài)性一部分源自于__dict__,屬性都保存在__dict__的一個(gè)字典中,我們可以隨時(shí)向這個(gè)字典添加新內(nèi)容,這是MonkeyPatch的能力。
而當(dāng)我們顯示的聲明了__slots__,python將不會(huì)給這個(gè)類創(chuàng)建__dict____weakref__

  • 注: __weakref__是軟引用,類似于指針,不會(huì)增加引用計(jì)數(shù)器

沒(méi)有了__dict__我們便不能隨意創(chuàng)建實(shí)例屬性,而必須遵守__slots__的約定。

對(duì)于性能而言,使用了__slots__后,屬性會(huì)直接存儲(chǔ)到對(duì)象中,而不是__dict__中,相當(dāng)于少了一次檢索步驟。

__slots__的兩大優(yōu)點(diǎn),對(duì)于python來(lái)說(shuō),是難以拒絕的誘惑。既能限制不必要的動(dòng)態(tài)性,又能提高性能!
但是__slots__遇到繼承時(shí),就會(huì)出現(xiàn)很多問(wèn)題。準(zhǔn)確的說(shuō),__slots__不會(huì)被繼承。當(dāng)我們用一個(gè)不帶有__slots__的子類,繼承帶有__slots__的父類時(shí)。子類還是會(huì)生成__dict____weakref__。

這有時(shí)不是我們所希望的,也許我們希望父親嚴(yán)明的紀(jì)律可以被孩子們傳承

用metaclass來(lái)解決這個(gè)問(wèn)題

這是之前寫的一個(gè)metaclass,創(chuàng)建新的類對(duì)象時(shí),會(huì)進(jìn)行以下邏輯。

  1. 判斷是否具有顯示聲明的父類
  2. 尋找父類是否有slots,沒(méi)有則跳過(guò)
  3. 尋找子類是否的slots,如果無(wú)則置為()
  • 注: 下列代碼只做了單繼承的邏輯
class SlotsMeta(type):
    def __new__(mcs, *args, **kwargs):
        if super_class := args[1]:
            if getattr(super_class[0], "__slots__", None):
                __attr = args[2]
                if not __attr.get("__slots__"):
                    __attr["__slots__"] = ()
                    args = (args[0], args[1], __attr)
        return super().__new__(mcs, *args, **kwargs)


class SlotsClass(metaclass=SlotsMeta):
    __slots__ = ('a', 'b', 'c')

    def __init__(self):
        self.a = 1
        self.b = 2
        self.c = 3


class SubClass(SlotsClass):

    def __init__(self):
        self.d = 4
        self.e = 5
        self.a = 7

這樣會(huì)變成什么樣的結(jié)果呢?

  1. 父有子無(wú):子類會(huì)被約束為__slots__ = (),完全遵循父類的規(guī)則
  2. 父有子有:無(wú)變化,子類的的實(shí)際范圍為父子__slots__ = ()的并集
  3. 父無(wú)子有:無(wú)變化
  4. 父無(wú)子無(wú):無(wú)變化

實(shí)際上我們的目的正是解決,父類規(guī)定的__slots__約束不了無(wú)__slots__子類的問(wèn)題。這個(gè)結(jié)果令人非常滿意

注意這里子類的d,在pycharm等IDE中不會(huì)提示錯(cuò)誤,因?yàn)?strong>pycharm無(wú)法探知你的metaclass做了這樣 逆天改命 的邏輯。

但報(bào)錯(cuò)還是會(huì)正常存在的

注意!

需要注意一點(diǎn),__slots__類對(duì)實(shí)例屬性的約束,而類對(duì)象無(wú)法通過(guò)該屬性,約束自己。即為類對(duì)象添加新屬性,仍然是被允許的。

新增類屬性

仍舊被允許

按照正常思路,也許我們應(yīng)該到metaclass寫一個(gè)__slots__,但實(shí)際上這是不被允許的。
抽空找時(shí)間我會(huì)考慮下有無(wú)可行性。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容