如何禁止 Python 子類覆蓋父類方法?
在昨天的文章里面,我們講到了,當子類試圖覆蓋父類的時候,可以通過類型標注來發出警告。今天,我們來講講如何直接禁止覆蓋。
Python 原生是沒有提供禁止子類覆蓋父類的方法的功能,因此我們需要自己來實現。
先來看一下實現效果:
在這段代碼里面,我們禁止子類覆蓋父類的dead()和eat()方法,但不禁止move方法。所以,當我們在子類Dog里面嘗試覆蓋父類中的dead()時,程序就報錯了。具體要覆蓋哪些方法,可以在定義類的時候指定,傳入的參數metaclass=protect('方法1', '方法2', '方法3', ...)就可以了。
那么這個protect函數是個什么東西呢?我們來看看它的代碼:
- def protect(*protected):
- """Returns a metaclass that protects all attributes given as strings"""
- class Protect(type):
- has_base = False
- def __new__(meta, name, bases, attrs):
- if meta.has_base:
- for attribute in attrs:
- if attribute in protected:
- raise AttributeError('Overriding of attribute "%s" not allowed.'%attribute)
- meta.has_base = True
- klass = super().__new__(meta, name, bases, attrs)
- return klass
- return Protect
這里,用到了 Python 的元類。如果大家對元類有興趣,可以看9.13 使用元類控制實例的創建 — python3-cookbook 3.0.0 文檔[1]。簡單的來說,元類用來定義類的創建行為。它一般的格式為:
- class 類名(metaclass=另一個類):
- ...
而大家看我們用來禁止重試的這個函數protect,它返回的就是一個Protect類。這個類繼承于type對象。
Protect類有一個__new__方法,這個方法會在使用了元類的所有子類的__init__之前被調用。在__new__里面,我們拿到了子類要定義的方法,并且檢查他們是不是在我們傳給protect的列表里面。如果在,說明這個方法不能被覆蓋。
當實現我們自己的父類Animal的時候,由于meta.has_base為 False,所以不會觸發檢查邏輯。但當我們基于Animal實現Dog子類的時候,由于meta.has_base是True,所以進入檢查邏輯。Dog的所有方法名都在attrs參數里面。循環檢查每一個方法名是否在禁止的列表中,如果在,就拋出異常。如果不在,就繼續后面的創建過程。
元類在理解上可能比較困難。如果大家無法理解上面這一段也沒有關系,直接用就是了。
參考文獻
[1] 9.13 使用元類控制實例的創建 — python3-cookbook 3.0.0 文檔: https://python3-cookbook.readthedocs.io/zh_CN/latest/c09/p13_using_mataclass_to_control_instance_creation.html
本文轉載自微信公眾號「未聞Code」,可以通過以下二維碼關注。轉載本文請聯系未聞Code公眾號。