Python虛擬機之super超級魔法的使用和工作原理詳解
super類的使用
在 Python 中,我們經(jīng)常使用繼承來構(gòu)建類的層次結(jié)構(gòu)。當(dāng)子類繼承了父類的屬性和方法時,有時我們需要在子類中調(diào)用父類的方法或?qū)傩浴_@就是super
類的用武之地。
super
函數(shù)的一般用法是在子類中調(diào)用父類的方法,格式為super().method()
。這樣可以方便地使用父類的實現(xiàn),并在子類中添加自己的特定行為。
下面是一個示例代碼,演示了super
函數(shù)的使用:
class Parent: def __init__(self, name): self.name = name def say_hello(self): print(f"Hello, I'm {self.name}") class Child(Parent): def __init__(self, name, age): super().__init__(name) self.age = age def say_hello(self): super().say_hello() print(f"I'm {self.name} and I'm {self.age} years old") child = Child("Alice", 10) child.say_hello()
輸出結(jié)果為:
Hello, I'm Alice
I'm Alice and I'm 10 years old
在上述示例中,Child
類繼承自Parent
類。在Child
類的構(gòu)造函數(shù)中,我們使用super().__init__(name)
來調(diào)用父類Parent
的構(gòu)造函數(shù),以便在子類中初始化父類的屬性。在say_hello
方法中,我們使用super().say_hello()
調(diào)用父類Parent
的say_hello
方法,并在子類中添加了額外的輸出。
除了調(diào)用父類的方法,super
函數(shù)還可以用于訪問父類的屬性。例如,super().attribute
可以用來獲取父類的屬性值。
super類的工作原理
Super 設(shè)計的目的
要理解super
類的工作原理,我們需要了解Python中的多重繼承和方法解析順序(Method Resolution Order,MRO)。多繼承是指一個類可以同時繼承多個父類。在Python中,每個類都有一個內(nèi)置屬性__mro__
,它記錄了方法解析順序。MRO是根據(jù)C3線性化算法生成的,它決定了在多重繼承中調(diào)用方法的順序。當(dāng)對象進行方法調(diào)用的時候,就會從類的 mro 當(dāng)中的第一個類開始尋找,直到最后一個類為止,當(dāng)?shù)谝淮伟l(fā)現(xiàn)對應(yīng)的類有相應(yīng)的方法時就進行返回就調(diào)用這個類的這個方法。
Super 類的的簽名為 class super(type, object_or_type=None),這個類返回的是一個 super 對象,也是一個代理對象,當(dāng)使用這個對象進行方法調(diào)用的時候,這個調(diào)用會轉(zhuǎn)發(fā)給 type 父類或同級類。object_or_type 參數(shù)的作用是用于確定要搜索的方法解析順序(也就是通過object_or_type得到具體的 mro),對于方法的搜索從 type 后面的類開始。
例如,如果 的 object_or_type 的 mro 是 D -> B -> C -> A -> object
并且type的值是 B
,則進行方法搜索的順序為C -> A -> object
,因為搜索是從 type 的下一個類開始的。
下面我們使用一個例子來實際體驗一下:
class A: def __init__(self): super().__init__() def method(self): print("In method of A") class B(A): def __init__(self): super().__init__() def method(self): print("In method of B") class C(B): def __init__(self): super().__init__() def method(self): print("In method of C") if __name__ == '__main__': print(C.__mro__) obj = C() s = super(C, obj) s.method() s = super(B, obj) s.method()
上面的程序輸出結(jié)果為:
(<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
In method of B
In method of A
在上面的代碼當(dāng)中繼承順序為,C 繼承 B,B 繼承 A,C 的 mro 為,(C, B, A, object),super(C, obj)
表示從 C 的下一個類開始搜索,因此具體的搜索順序為 ( B, A, object),因此此時調(diào)用 method 方法的時候,會調(diào)用 B 的 method 方法,super(B, obj)
表示從 B 的下一個類開始搜索,因此搜索順序為 (A, object),因此此時調(diào)用的是 A 的 method 方法。
Super 和棧幀的關(guān)系
在上一小節(jié)當(dāng)中我們在使用 super 進行測試的時候,都是給了 super 兩個參數(shù),但是需要注意的是我們在一個類的 __init__
方法當(dāng)中并沒有給 super 任何參數(shù),那么他是如何找到 super 需要的兩個參數(shù)呢?
這其中的魔法就是在 Super 類對象的初始化會獲取當(dāng)前棧幀的第一個參數(shù)對象,這個就是對應(yīng)上面的 object_or_type 參數(shù),type 就是局部變量表當(dāng)中的一個參數(shù) __class__
,我們可以通過查看類方法的局部變量去驗證這一點:
import inspect class A(object): def __init__(self): super().__init__() print(inspect.currentframe().f_locals) def bar(self): pass def foo(self): pass class Demo(A): def __init__(self): super().__init__() print(inspect.currentframe().f_locals) def bar(self): super().bar() print(inspect.currentframe().f_locals) def foo(self): print(inspect.currentframe().f_locals) if __name__ == '__main__': demo = Demo() demo.bar() demo.foo()
上面的代碼輸出結(jié)果為:
{'self': <__main__.Demo object at 0x103059040>, '__class__': <class '__main__.A'>}
{'self': <__main__.Demo object at 0x103059040>, '__class__': <class '__main__.Demo'>}
{'self': <__main__.Demo object at 0x103059040>, '__class__': <class '__main__.Demo'>}
{'self': <__main__.Demo object at 0x103059040>}
從上面的例子我們可以看到當(dāng)我們進行方法調(diào)用且方法當(dāng)中有 super 的使用時,棧幀的局部變量表當(dāng)中會多一個字段 __class__
,這個字段表示對應(yīng)的類,比如在 Demo 類當(dāng)中,這個字段就是 Demo,在類 A 當(dāng)中這個字段就是 A 。為什么要進行這樣的處理呢,這是因為需要調(diào)用相應(yīng)位置類的父類方法,因此所有的使用 super 的位置的 type 都必須是所在類。而在前面我們已經(jīng)說明了object_or_type 表示的是棧幀當(dāng)中的第一個參數(shù),也就是對象 self,這一點從上面的局部變量表也可以看出來,通過這個對象我們可以知道對象本身的 mro 序列了。在 super 得到兩個參數(shù)之后,也就能夠?qū)崿F(xiàn)對應(yīng)的功能了。
CPython的實現(xiàn)
在本小節(jié)當(dāng)中我們來仔細看一下 CPython 內(nèi)部是如何實現(xiàn) super 類的,首先來看一下他的 __init__
方法(刪除了error checking 代碼):
static int super_init(PyObject *self, PyObject *args, PyObject *kwds) { superobject *su = (superobject *)self; PyTypeObject *type = NULL; // 表示從哪個類的后面開始查詢,含義和 上文當(dāng)中的 type 一樣 PyObject *obj = NULL; // 表示傳遞過來的對象 PyTypeObject *obj_type = NULL; // 表示對象 obj 的類型 // 獲取 super 的兩個參數(shù) type 和 object_or_type if (!PyArg_ParseTuple(args, "|O!O:super", &PyType_Type, &type, &obj)) return -1; if (type == NULL) { /* Call super(), without args -- fill in from __class__ and first local variable on the stack. */ PyFrameObject *f; PyCodeObject *co; Py_ssize_t i, n; f = _PyThreadState_GET()->frame; // 得到當(dāng)前棧幀 // 棧幀的第一個參數(shù)表示對象 obj = f->f_localsplus[0]; if (obj == NULL && co->co_cell2arg) { /* The first argument might be a cell. */ n = PyTuple_GET_SIZE(co->co_cellvars); for (i = 0; i < n; i++) { if (co->co_cell2arg[i] == 0) { PyObject *cell = f->f_localsplus[co->co_nlocals + i]; assert(PyCell_Check(cell)); obj = PyCell_GET(cell); break; } } } if (co->co_freevars == NULL) n = 0; else { assert(PyTuple_Check(co->co_freevars)); n = PyTuple_GET_SIZE(co->co_freevars); } // 下面的代碼表示獲取 type 對象,也就是從局部變量表當(dāng)中獲取到 __class__ for (i = 0; i < n; i++) { PyObject *name = PyTuple_GET_ITEM(co->co_freevars, i); assert(PyUnicode_Check(name)); if (_PyUnicode_EqualToASCIIId(name, &PyId___class__)) { Py_ssize_t index = co->co_nlocals + PyTuple_GET_SIZE(co->co_cellvars) + i; PyObject *cell = f->f_localsplus[index]; type = (PyTypeObject *) PyCell_GET(cell); break; } } } if (obj == Py_None) obj = NULL; if (obj != NULL) { // 這個函數(shù)是用于獲取 obj 的 type obj_type = supercheck(type, obj); if (obj_type == NULL) return -1; Py_INCREF(obj); } return 0; }
在上面的代碼執(zhí)行完成之后就得到了一個 super 對象,之后在進行函數(shù)調(diào)用的時候就會將對應(yīng)類的方法和對象 obj 綁定成一個方法對象返回,然后在進行方法調(diào)用的時候就能夠成功調(diào)用了。
class Demo: def __init__(self): print(super().__init__) if __name__ == '__main__': Demo()
輸出結(jié)果:
<method-wrapper '__init__' of Demo object at 0x100584070>
總結(jié)
super 是 Python 面向?qū)ο缶幊坍?dāng)中非常重要的一部分內(nèi)容,在本篇文章當(dāng)中詳細介紹了 super 內(nèi)部的工作原理和 CPython 內(nèi)部部分源代碼分析了 super 的具體實現(xiàn)。在 Python 當(dāng)中 super 的使用方式分為兩種一種是可以直接使用參數(shù),另外一種是在類的方法當(dāng)中不使用參數(shù),后者的實現(xiàn)稍微復(fù)雜一點,他會從當(dāng)前棧幀和局部變量表當(dāng)中分別取出類對象和類,作為 super 的參數(shù),從而實現(xiàn) super 的功能。
以上就是Python虛擬機之super超級魔法的使用和原理詳解的詳細內(nèi)容,更多關(guān)于Python super超級魔法原理的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
win10安裝tensorflow-gpu1.8.0詳細完整步驟
這篇文章主要介紹了win10安裝tensorflow-gpu1.8.0詳細完整步驟,本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2020-01-01pandas.DataFrame.to_json按行轉(zhuǎn)json的方法
今天小編就為大家分享一篇pandas.DataFrame.to_json按行轉(zhuǎn)json的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-06-06