亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

類裝飾器禁用 __init_subclass__

類裝飾器禁用 __init_subclass__

三國紛爭 2022-05-19 15:41:47
我已經四處尋找這個問題的答案,但找不到任何東西。如果之前已經問過這個問題,我深表歉意。在我所知道的 3-4 種方法中,我知道從父類對子類執行給定方法(編輯__new__元類的方法、掛鉤builtins.__build_class__、使用__init_subclass__或使用abc.abstractmethod)我通常最終使用__init_subclass__,主要是因為易于使用而且,與 不同@abc.abstractmethod的是,對子類的約束是根據子類定義而不是類實例化來檢查的。例子:class Par():    def __init_subclass__(self, *args, **kwargs):        must_have = 'foo'        if must_have not in list(self.__dict__.keys()):            raise AttributeError(f"Must have {must_have}")    def __init__(self):        passclass Chi(Par):    def __init__(self):        super().__init__()這個示例代碼顯然會拋出錯誤,因為Chi沒有foo方法。盡管如此,我還是偶然發現了這樣一個事實,即上游類的這個約束可以通過使用一個簡單的類裝飾器來繞過:def add_hello_world(Cls):    class NewCls(object):        def __init__(self, *args, **kwargs):            self.instance = Cls(*args, **kwargs)        def hello_world(self):            print("hello world")    return NewCls@add_hello_worldclass Par:    def __init_subclass__(self, *args, **kwargs):        must_have = "foo"        if must_have not in list(self.__dict__.keys()):            raise AttributeError(f"Must have {must_have}")    def __init__(self):        passclass Chi(Par):    def __init__(self):        super().__init__()c = Chi()c.hello_world()上面的代碼運行沒有問題?,F在,不管我裝飾的類是Par(當然,如果Par是庫代碼,作為用戶代碼開發人員我什至可能無法訪問它),我無法真正解釋這種行為。對我來說很明顯可以使用裝飾器向現有類添加方法或功能,但我從未見過不相關的裝飾器(只是打印hello world,甚至不會與類創建混淆)禁用已經存在的方法班級。這是預期的 Python 行為嗎?或者這是某種錯誤?老實說,據我了解,這可能會帶來一些安全問題。這只發生在__init_subclass__數據模型上嗎?還是也給別人?
查看完整描述

1 回答

?
炎炎設計

TA貢獻1808條經驗 獲得超4個贊

請記住,裝飾器語法只是函數應用程序:


class Par:

    def __init_subclass__(...):

         ...



Par = add_hello_world(Par)

最初綁定到Par定義的類__init_subclass__;內部定義的新類add_hello_world沒有,這就是裝飾后名稱Par所指的類,以及您要繼承的類。


順便說一句,您仍然可以Par通過__init__.


顯式調用裝飾器:


class Par:

    def __init_subclass__(self, *args, **kwargs):

        must_have = "foo"

        if must_have not in list(self.__dict__.keys()):

            raise AttributeError(f"Must have {must_have}")


    def __init__(self):

        pass


Foo = Par  # Keep this for confirmation


Par = add_hello_world(Par)

我們可以確認閉包保留了對原始類的引用:


>>> Par.__init__.__closure__[0].cell_contents

<class '__main__.Par'>

>>> Par.__init__.__closure__[0].cell_contents is Par

False

>>> Par.__init__.__closure__[0].cell_contents is Foo

True

如果您確實嘗試對其進行子類化,您將得到預期的錯誤:


>>> class Bar(Par.__init__.__closure__[0].cell_contents):

...   pass

...

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

  File "tmp.py", line 16, in __init_subclass__

    raise AttributeError(f"Must have {must_have}")

AttributeError: Must have foo


查看完整回答
反對 回復 2022-05-19
  • 1 回答
  • 0 關注
  • 106 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號