日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

詳解 PyTypeObject,Python 類型對象的載體

來源: 責編: 時間:2024-05-16 08:58:03 198觀看
導讀楔子通過 PyObject 和 PyVarObject,我們看到了所有對象的公共信息以及變長對象的公共信息。任何一個對象,不管它是什么類型,內部必有引用計數(ob_refcnt)和類型指針(ob_type)。任何一個變長對象,不管它是什么類型,內部除了引用

楔子

通過 PyObject 和 PyVarObject,我們看到了所有對象的公共信息以及變長對象的公共信息。任何一個對象,不管它是什么類型,內部必有引用計數(ob_refcnt)和類型指針(ob_type)。任何一個變長對象,不管它是什么類型,內部除了引用計數和類型指針之外,還有一個表示元素個數的 ob_size。sSd28資訊網——每日最新資訊28at.com

顯然目前沒有什么問題,一切都是符合預期的,但是當我們順著時間軸回溯的話,就會發現端倪。比如:sSd28資訊網——每日最新資訊28at.com

  • 當在內存中創建對象、分配空間的時候,解釋器要給對象分配多大的空間?顯然不能隨便分配,那么對象的內存信息在什么地方?
  • 對象是可以執行相關操作的,解釋器怎么知道某個對象支持哪些操作呢?再比如一個整數可以和一個整數相乘,一個列表也可以和一個整數相乘,即使是相同的操作,但不同類型的對象執行也會有不同的效果,那么此時解釋器又是如何進行區分的?

想都不用想,這些信息肯定都在對象的類型對象中。因為占用的空間大小實際上是對象的一個元信息,這樣的元信息和其所屬類型是密切相關的,因此它一定會出現在與之對應的類型對象當中。sSd28資訊網——每日最新資訊28at.com

至于支持的操作就更不用說了,我們平時自定義類的時候,功能函數都寫在什么地方,顯然都是寫在類里面,因此一個對象支持的操作也定義在類型對象當中。sSd28資訊網——每日最新資訊28at.com

而將對象和它的類型對象關聯起來的,毫無疑問正是該對象內部的 PyObject 的 ob_type 字段,也就是類型指針。我們通過對象的 ob_type 字段即可獲取類型對象的指針,然后通過指針獲取存儲在類型對象中的某些元信息。sSd28資訊網——每日最新資訊28at.com

下面我們來看看類型對象在底層是怎么定義的。sSd28資訊網——每日最新資訊28at.com

解密 PyTypeObject

PyObject 的 ob_type 字段的類型是 PyTypeObject *,所以類型對象由 PyTypeObject 結構體負責實現,看一看它長什么樣子。sSd28資訊網——每日最新資訊28at.com

// Include/pytypedefs.htypedef struct _typeobject PyTypeObject;// Include/cpython/object.hstruct _typeobject {    PyObject_VAR_HEAD    const char *tp_name;     Py_ssize_t tp_basicsize, tp_itemsize;     destructor tp_dealloc;    Py_ssize_t tp_vectorcall_offset;    getattrfunc tp_getattr;    setattrfunc tp_setattr;    PyAsyncMethods *tp_as_async;                                         reprfunc tp_repr;    PyNumberMethods *tp_as_number;    PySequenceMethods *tp_as_sequence;    PyMappingMethods *tp_as_mapping;    hashfunc tp_hash;    ternaryfunc tp_call;    reprfunc tp_str;    getattrofunc tp_getattro;    setattrofunc tp_setattro;    PyBufferProcs *tp_as_buffer;    unsigned long tp_flags;    const char *tp_doc;     traverseproc tp_traverse;    inquiry tp_clear;    richcmpfunc tp_richcompare;    Py_ssize_t tp_weaklistoffset;    getiterfunc tp_iter;    iternextfunc tp_iternext;    PyMethodDef *tp_methods;    PyMemberDef *tp_members;    PyGetSetDef *tp_getset;    PyTypeObject *tp_base;    PyObject *tp_dict;    descrgetfunc tp_descr_get;    descrsetfunc tp_descr_set;    Py_ssize_t tp_dictoffset;    initproc tp_init;    allocfunc tp_alloc;    newfunc tp_new;    freefunc tp_free;     inquiry tp_is_gc;     PyObject *tp_bases;    PyObject *tp_mro;     PyObject *tp_cache;     void *tp_subclasses;      PyObject *tp_weaklist;     destructor tp_del;    unsigned int tp_version_tag;    destructor tp_finalize;    vectorcallfunc tp_vectorcall;    unsigned char tp_watched;};

類型對象在底層對應的是 struct _typeobject,或者說 PyTypeObject,它保存了實例對象的元信息。sSd28資訊網——每日最新資訊28at.com

所以不難發現,Python 中實例對象在底層會對應不同的結構體實例,但類型對象則是對應同一個結構體實例。換句話說無論是 int、str、dict 等內置類型,還是我們使用 class 關鍵字自定義的類型,它們在 C 的層面都是由 PyTypeObject 這個結構體實例化得到的,只不過內部字段的值不同,PyTypeObject 這個結構體在實例化之后得到的類型對象也不同。sSd28資訊網——每日最新資訊28at.com

然后我們來看看 PyTypeObject 里面的字段都代表啥含義,字段還是比較多的,我們逐一介紹。sSd28資訊網——每日最新資訊28at.com

PyObject_VAR_HEAD

宏,會被替換為 PyVarObject,所以類型對象是一個變長對象。因此類型對象也有引用計數和類型,這與我們前面分析的是一致的。sSd28資訊網——每日最新資訊28at.com

tp_name

對應 Python 中類型對象的 __name__ 屬性,即類型對象的名稱。sSd28資訊網——每日最新資訊28at.com

# 類型對象在底層對應的是 PyTypeObject 結構體實例# 它的 tp_name 字段表示類型對象的名稱print(int.__name__)  # int# 動態創建一個類A = type("我是 A", (object,), {})print(A.__name__)  # 我是 A

所以任何一個類型對象都有 __name__ 屬性,也就是都有名稱。sSd28資訊網——每日最新資訊28at.com

tp_basicsize,tp_itemsize

  • tp_basicsize:表示創建實例對象所需的基本內存大小;
  • tp_itemsize:如果對象是變長對象,并且元素保存在對應的結構體內部,比如元組,那么 tp_itemsize 表示內部每個元素的內存大小。如果是定長對象,或者雖然是變長對象,但結構體本身不保存數據,而是只保存了一個指針,那么 tp_itemsize 為 0;

tp_dealloc

析構函數,對應 Python 中類型對象的 __del__,會在實例對象被銷毀時執行。sSd28資訊網——每日最新資訊28at.com

tp_vectorcall_offset

如果想調用一個對象,那么它的類型對象要定義 __call__ 函數。sSd28資訊網——每日最新資訊28at.com

class A:    def __call__(self, *args, **kwargs):        return "被調用了"a = A()# 如果調用 a,那么 type(a) 要定義 __call__ 函數print(a())"""被調用了"""# 底層會轉成如下邏輯print(A.__call__(a))"""被調用了"""# 函數也是一個實例對象,它能被調用# 說明 type(函數) 也一定實現了 __call__def some_func(name, age):    return f"name: {name}, age: {age}"# 函數的類型是 functionprint(type(some_func))"""<class 'function'>"""# 調用函數print(some_func("古明地覺", 17))"""name: 古明地覺, age: 17"""# 也可以這么做print(type(some_func).__call__(some_func, "古明地覺", 17))"""name: 古明地覺, age: 17"""

以上就是對象最通用的調用邏輯,但通用也意味著平庸,這種調用方式的性能是不高的。自定義類的實例對象還好,因為需要支持調用的場景不多,而函數則不同,盡管它也是實例對象,但它生下來就是要被調用的。sSd28資訊網——每日最新資訊28at.com

如果函數調用也走通用邏輯的話,那么效率不高,因此 Python 從 3.8 開始引入了 vectorcall 協議,即矢量調用協議,用于優化和加速函數調用。至于它是怎么優化的,后續剖析函數的時候再細說。sSd28資訊網——每日最新資訊28at.com

總之當一個對象被調用時,如果它支持 vectorcall 協議,那么會通過 tp_vectorcall_offset 找到實現矢量調用的函數指針。sSd28資訊網——每日最新資訊28at.com

注意:vectorcall 函數指針定義在實例對象中,所以 tp_vectorcall_offset 字段維護了 vectorcall 函數指針在實例對象中的偏移量,該偏移量用于定位到一個特定的函數指針,這個函數指針符合 vectorcall 協議。sSd28資訊網——每日最新資訊28at.com

如果類型對象的 tp_vectorcall_offset 為 0,表示其實例對象不支持矢量調用,因此會退化為常規調用,即通過 __call__ 進行調用。sSd28資訊網——每日最新資訊28at.com

tp_getattr,tp_setattr

對應 Python 中類型對象的 __getattr__ 和 __setattr__,用于操作實例對象的屬性。但這兩個字段已經不推薦使用了,因為它要求在操作屬性時,屬性名必須為 C 字符串,以及不支持通過描述符協議處理屬性。sSd28資訊網——每日最新資訊28at.com

所以這兩個字段主要用于兼容舊版本,現在應該使用 tp_getattro 和 tp_setattro。sSd28資訊網——每日最新資訊28at.com

tp_as_number、tp_as_sequence、tp_as_mapping、tp_as_async

tp_as_number:實例對象為數值時,所支持的操作。這是一個結構體指針,指向的結構體中的每一個字段都是一個函數指針,指向的函數就是對象可以執行的操作,比如四則運算、左移、右移、取模等等。sSd28資訊網——每日最新資訊28at.com

tp_as_sequence:實例對象為序列時,所支持的操作,也是一個結構體指針。sSd28資訊網——每日最新資訊28at.com

tp_as_mapping:實例對象為映射時,所支持的操作,也是一個結構體指針。sSd28資訊網——每日最新資訊28at.com

tp_as_async:實例對象為協程時,所支持的操作,也是一個結構體指針。sSd28資訊網——每日最新資訊28at.com

tp_repr、tp_str

對應 Python 中類型對象的 __repr__ 和 __str__,用于控制實例對象的打印輸出。sSd28資訊網——每日最新資訊28at.com

tp_hash

對應 Python 中類型對象的 __hash__,用于定義實例對象的哈希值。sSd28資訊網——每日最新資訊28at.com

tp_call

對應 Python 中類型對象的 __call__,用于控制實例對象的調用行為。當然這屬于常規調用,而對象不僅可以支持常規調用,還可以支持上面提到的矢量調用(通過減少參數傳遞的開銷,提升調用性能)。sSd28資訊網——每日最新資訊28at.com

但要注意的是,不管使用哪種調用協議,對象調用的行為必須都是相同的。因此一個對象如果支持矢量調用,那么它也必須支持常規調用,換句話說對象如果實現了 vectorcall,那么它的類型對象也必須實現 tp_call。sSd28資訊網——每日最新資訊28at.com

如果你在實現 vectorcall 之后發現它比 tp_call 還慢,那么你就不應該實現 vectorcall,因為實現 vectorcall 是有條件的,當條件不滿足時性能反而會變差。sSd28資訊網——每日最新資訊28at.com

tp_getattro,tp_setattro

對應 Python 中類型對象的 __getattr__ 和 __setattr__。sSd28資訊網——每日最新資訊28at.com

tp_as_buffer

指向 PyBufferProcs 類型的結構體,用于共享內存。通過暴露出一個緩沖區,可以和其它對象共享同一份數據,因此當類型對象實現了 tp_as_buffer,我們也說其實例對象實現了緩沖區協議,舉個例子。sSd28資訊網——每日最新資訊28at.com

import numpy as npbuf = bytearray(b"abc")# 和 buf 共享內存arr = np.frombuffer(buf, dtype="uint8")print(arr)  # [97 98 99]# 修改 bufbuf[0] = 255# 會發現 arr 也改變了,因為它和 buf 共用一塊內存print(arr)  # [255  98  99]

所以 tp_as_buffer 主要用于那些自身包含大量數據,且需要允許其它對象直接訪問的類型。通過實現緩沖區協議,其它對象可以直接共享數據,而無需事先拷貝,這在處理大型數據或進行高性能計算時非常有用。sSd28資訊網——每日最新資訊28at.com

關于緩沖區協議,后續還會詳細介紹。sSd28資訊網——每日最新資訊28at.com

tp_flags

對應 Python 中類型對象的 __flags__,負責提供類型對象本身的附加信息,通過和指定的一系列標志位進行按位與運算,即可判斷該類型是否具有某個特征。sSd28資訊網——每日最新資訊28at.com

那么標志位都有哪些呢?我們介紹幾個常見的。sSd28資訊網——每日最新資訊28at.com

// Include/object.h// 類型對象的內存是否是動態分配的// 像內置的類型對象屬于靜態類,它們不是動態分配的#define Py_TPFLAGS_HEAPTYPE (1UL << 9)// 類型對象是否允許被繼承#define Py_TPFLAGS_BASETYPE (1UL << 10)// 類型對象的實例對象是否參與垃圾回收#define Py_TPFLAGS_HAVE_GC (1UL << 14)// 類型對象是否是抽象基類#define Py_TPFLAGS_IS_ABSTRACT (1UL << 20)

我們通過 Python 來演示一下。sSd28資訊網——每日最新資訊28at.com

# 是否是自定義的動態類Py_TPFLAGS_HEAPTYPE = 1 << 9class A:    pass# 如果與運算的結果為真,則表示是動態類,否則不是print(A.__flags__ & Py_TPFLAGS_HEAPTYPE)"""512"""print(int.__flags__ & Py_TPFLAGS_HEAPTYPE)"""0"""# 類型對象是否允許被繼承Py_TPFLAGS_BASETYPE = 1 << 10# object 顯然允許被繼承,因此與運算的結果為真print(object.__flags__ & Py_TPFLAGS_BASETYPE)"""1024"""# 但 memoryview 就不允許被繼承try:    class B(memoryview):        passexcept TypeError as e:    print(e)    """    type 'memoryview' is not an acceptable base type    """print(memoryview.__flags__ & Py_TPFLAGS_BASETYPE)"""0"""# 類型對象的實例對象是否參與垃圾回收Py_TPFLAGS_HAVE_GC = 1 << 14# int 的實例對象(整數)不會產生循環引用,所以不會參與垃圾回收print(int.__flags__ & Py_TPFLAGS_HAVE_GC)"""0"""# 但列表是會參與垃圾回收的print(list.__flags__ & Py_TPFLAGS_HAVE_GC)"""16384"""# 類型對象是否是抽象基類Py_TPFLAGS_IS_ABSTRACT = 1 << 20from abc import ABCMeta, abstractmethod# 顯然 C 不是抽象基類,而 D 是class C:    passclass D(metaclass=ABCMeta):    @abstractmethod    def foo(self):        passprint(C.__flags__ & Py_TPFLAGS_IS_ABSTRACT)"""0"""print(D.__flags__ & Py_TPFLAGS_IS_ABSTRACT)"""1048576"""

所以這就是 tp_flags 的作用,它負責描述一個類型對象都具有哪些額外特征。sSd28資訊網——每日最新資訊28at.com

tp_doc

對應 Python 中類型對象的 __doc__。sSd28資訊網——每日最新資訊28at.com

class People:    """以前我沒得選"""print(People.__doc__)"""以前我沒得選"""

這個比較簡單。sSd28資訊網——每日最新資訊28at.com

tp_traverse,tp_clear

這兩個字段是一對,負責參與垃圾回收機制。sSd28資訊網——每日最新資訊28at.com

  • tp_traverse:用于標記階段,通過遍歷實例對象所引用的其它對象,確定對象之間的引用關系,幫助垃圾回收器識別出所有活躍的對象和出現循環引用的對象。
  • tp_clear:用于清除階段,負責減少出現循環引用的對象的引用計數。

tp_richcompare

負責實現對象的比較邏輯,包含 >、>=、<、<=、!=、==。sSd28資訊網——每日最新資訊28at.com

tp_weaklistoffset

弱引用列表的偏移量。sSd28資訊網——每日最新資訊28at.com

tp_iter、tp_iternext

對應 Python 中類型對象的 __iter__ 和 __next__。sSd28資訊網——每日最新資訊28at.com

tp_methods

負責保存類型對象里的成員函數,我們以 list 為例。sSd28資訊網——每日最新資訊28at.com

tp_members

負責指定可以綁定在實例對象上的屬性,我們使用 class 關鍵字定義動態類的時候,會在 __init__ 函數中給實例對象綁定屬性,而對于底層 C 來說,需要通過 tp_members 字段。 sSd28資訊網——每日最新資訊28at.com

以 slice 為例,它負責創建一個切片。sSd28資訊網——每日最新資訊28at.com

lst = list(range(10))print(lst[1: 7: 2])  # [1, 3, 5]# 等價于print(lst[slice(1, 7, 2)])  # [1, 3, 5]

slice 是一個底層實現好的靜態類,接收 start、end、step 三個參數,所以它底層的 tp_members 就是這么定義的。sSd28資訊網——每日最新資訊28at.com

對于靜態類而言,可以給 self 綁定哪些屬性、以及類型是什么,都已經事先在 tp_members 里面寫死了,后續不可以新增或刪除屬性。sSd28資訊網——每日最新資訊28at.com

s = slice(1, 7, 2)# 靜態類的實例對象不可以新增或刪除屬性try:    s.xx = "xx"except AttributeError as e:    print(e)    """    'slice' object has no attribute 'xx'    """# 至于能否修改,則看定義屬性時是否要求屬性是 READONLY# 對于 slice 來說,它的三個屬性都是 READONLY,所以不能修改try:    s.start = 2except AttributeError as e:    print(e)    """    readonly attribute    """

但使用 class 自定義的動態類而言,新增、刪除、修改屬性都是可以的,至于里面的更多細節,后續在介紹類的時候會詳細剖析。sSd28資訊網——每日最新資訊28at.com

tp_getset

指向一個 PyGetSetDef 結構體數組,里面的每個結構體都定義了一個屬性的名稱、獲取該屬性的函數、設置該屬性的函數、屬性的文檔字符串。sSd28資訊網——每日最新資訊28at.com

  • name:屬性的名稱,Python 代碼中用來訪問屬性的字符串。
  • get:屬性的 getter 函數,如果設置了這個函數,Python 代碼讀取屬性時會調用它。
  • set:屬性的 setter 函數,如果設置了這個函數,Python 代碼修改屬性時會調用它。
  • doc:屬性的文檔字符串,為屬性提供描述信息。
  • closure:一個 void * 指針,用于傳遞額外的信息給 getter 和 setter,不常用。

所以我們發現 tp_getset 的作用不就類似于 @property 裝飾器嗎?tp_getset 數組里面的每個結構體負責實現一個 property 屬性。sSd28資訊網——每日最新資訊28at.com

tp_base

對應 Python 中類型對象的 __base__,返回繼承的第一個父類。sSd28資訊網——每日最新資訊28at.com

tp_dict

對應 Python 中類型對象的 __dict__,即屬性字典。sSd28資訊網——每日最新資訊28at.com

tp_descr_get、tp_descr_set

對應 Python 中類型對象的 __get__ 和 __set__,用于實現描述符。sSd28資訊網——每日最新資訊28at.com

tp_dictoffset

注意它和 tp_dict 的區別,tp_dict 表示類型對象的屬性字典,而 tp_dictoffset 表示實例對象的屬性字典在實例對象中的偏移量。關于屬性字典,后續會詳細介紹。sSd28資訊網——每日最新資訊28at.com

tp_init

對應 Python 中類型對象的 __init__,用于實例對象屬性的初始化。sSd28資訊網——每日最新資訊28at.com

tp_alloc

負責為實例對象申請內存,申請多大呢?取決于 tb_basicsize 和 tp_itemsize。sSd28資訊網——每日最新資訊28at.com

tp_new

對應 Python 中類型對象的 __new__,即構造函數,在 tp_new 內部會調用 tp_alloc 為實例對象申請內存。sSd28資訊網——每日最新資訊28at.com

tp_free

內存釋放函數,負責釋放實例對象所占的內存,注意它和 tp_dealloc 的區別與聯系。tp_dealloc 表示析構函數,當對象的引用計數降到零的時候執行,內部會負責如下工作。sSd28資訊網——每日最新資訊28at.com

  • 減少引用的其它對象的引用計數;
  • 釋放對象擁有的資源,比如文件句柄或網絡連接;
  • 調用內存釋放函數來釋放對象本身占用的內存,這一步由 tp_free 來完成。

所以要注意這幾個字段之間的區別,我們再總結一下。sSd28資訊網——每日最新資訊28at.com

tp_is_gc

指示該類型對象的實例對象是否參與垃圾回收。sSd28資訊網——每日最新資訊28at.com

如果參與垃圾回收,那么 tp_flags & Py_TPFLAGS_HAVE_GC 的結果不等于 0。sSd28資訊網——每日最新資訊28at.com

tp_bases

對應 Python 中類型對象的 __bases__,返回一個元組,里面包含直接繼承的所有父類。sSd28資訊網——每日最新資訊28at.com

tp_mro

對應 Python 中類型對象的 __mro__,返回一個元組,里面包含自身以及直接繼承和間接繼承的所有父類,直到 object。sSd28資訊網——每日最新資訊28at.com

注意:返回的元組中的類是有順序關系的,它基于 C3 線性化算法生成,定義了方法解析的順序。當 Python 需要查找方法或屬性時,將按照此順序進行搜索。sSd28資訊網——每日最新資訊28at.com

tp_cache

該字段不再使用,因此這里不做介紹。sSd28資訊網——每日最新資訊28at.com

tp_subclasses

等價于 Python 中類型對象的 __subclasses__,會返回繼承該類的所有子類。sSd28資訊網——每日最新資訊28at.com

class A:    passclass B(A):    passclass C(B):    passprint(A.__subclasses__())"""[<class '__main__.B'>]"""

但是只返回直接繼承的子類,間接繼承的不算,比如這里只返回了 B,而 C 沒有返回。sSd28資訊網——每日最新資訊28at.com

tp_weaklist

實例對象的弱引用列表,注意:每個實例對象都會有,而前面還提到了一個 tp_weaklistoffset,它便是弱引用列表在實例對象當中的偏移量。如果偏移量為 0,那么表示當前類型對象的實例對象不支持弱引用。sSd28資訊網——每日最新資訊28at.com

tp_del

和 tp_dealloc 作用相同,但 tp_del 主要是兼容以前的舊版本,現在直接使用 tp_dealloc 即可。sSd28資訊網——每日最新資訊28at.com

tp_version_tag

用于標記類型對象的版本,每當類型的定義發生變化時(例如添加、刪除或修改成員函數),這個版本標簽就會更新。解釋器會使用這個版本標簽來確定方法緩存是否有效,從而避免在每次方法調用時都重新解析和查找。sSd28資訊網——每日最新資訊28at.com

tp_finalize

負責在對象被銷毀之前執行相應的清理操作,確保資源得到妥善處理,它的調用時機在對象的引用計數達到零之后、tp_dealloc(負責釋放對象的內存)被調用之前。sSd28資訊網——每日最新資訊28at.com

該字段不常用,一般只出現在生成器和協程當中。然后 tp_dealloc、tp_del、tp_finalize 三個字段的類型是一致的,都是 destructor 類型,那么它們三者有什么區別呢?sSd28資訊網——每日最新資訊28at.com

  • tp_dealloc:在所有類型對象中都需要定義,因為它是管理實例對象生命周期的關鍵,它負責減少引用的其它對象的引用計數,以及釋放當前對象占用的內存,當然也可以執行必要的清理操作。 
  • tp_finalize:負責在對象的生命周期結束前執行相關清理操作,一般只用于生成器和協程當中,此時會和 tp_dealloc 搭配使用。
  • tp_del:除非是兼容遺留代碼,否則應避免使用 tp_del,而是依賴于更現代的垃圾回收和清理機制,即使用 tp_dealloc。

以上就是 PyTypeObject 的各個字段的含義。sSd28資訊網——每日最新資訊28at.com

一些常見的類型對象

下面來介紹一些常見的類型在底層的定義。sSd28資訊網——每日最新資訊28at.com

  • int -> PyLong_Type
  • str -> PyUnicode_Type
  • float -> PyFloat_Type
  • complex -> PyComplex_Type
  • tuple -> PyTuple_Type
  • list -> PyList_Type
  • dict -> PyDict_Type
  • set -> PySet_Type
  • frozenset -> PyFrozenSet_Type
  • type -> PyType_Type

Python 底層的 C API 和對象的命名都遵循統一的標準,比如類型對象均以 Py***_Type 的形式命名,當然啦,它們都是 PyTypeObject 結構體實例。sSd28資訊網——每日最新資訊28at.com

所以我們發現,Python 里的類在底層是以全局變量的形式靜態定義好的。sSd28資訊網——每日最新資訊28at.com

所以實例對象可以有很多個,但類型對象則是唯一的,在底層直接以全局變量的形式靜態定義好了。sSd28資訊網——每日最新資訊28at.com

比如列表的類型是 list,列表可以有很多個,但 list 類型對象則全局唯一。sSd28資訊網——每日最新資訊28at.com

data1 = [1, 2, 3]data2 = [4, 5, 6]print(    data1.__class__ is data2.__class__ is list)  # True

如果站在 C 的角度來理解的話:sSd28資訊網——每日最新資訊28at.com

data1 和 data2 變量均指向了列表,列表在底層對應 PyListObject 結構體實例。里面字段的含義之前說過,但需要注意的是,指針數組里面保存的是對象的指針,而不是對象。不過為了方便,圖中就用對象代替了。sSd28資訊網——每日最新資訊28at.com

然后列表的類型是 list,在底層對應 PyList_Type,它是 PyTypeObject 結構體實例,保存了列表的元信息(比如內存分配信息、支持的相關操作等)。sSd28資訊網——每日最新資訊28at.com

而將這兩者關聯起來的便是 ob_type,它位于 PyObject 中,是所有對象都具有的。因為變量只是一個 PyObject * 指針,那么解釋器要如何得知變量指向的對象的類型呢?答案便是通過 ob_type 字段。sSd28資訊網——每日最新資訊28at.com

小結

類型對象全局唯一,在底層以全局變量的形式存在,不管是什么類型對象,均由 PyTypeObject 結構體實例化得到,而不同的實例對象則對應不同的結構體。sSd28資訊網——每日最新資訊28at.com

將實例對象和類型對象關聯起來的,則是實例對象的 ob_type 字段,在 Python 里面可以通過調用 type 或者獲取 __class__ 屬性查看。sSd28資訊網——每日最新資訊28at.com

關于類型對象的更多內容,后續會繼續介紹。sSd28資訊網——每日最新資訊28at.com

本文鏈接:http://m.www897cc.com/showinfo-26-88309-0.html詳解 PyTypeObject,Python 類型對象的載體

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: OrangePi AIPro Kunpeng Pro 主頻提升,加量不加價!

下一篇: FLIP,一種高端優雅但簡單易用的前端動畫思維

標簽:
  • 熱門焦點
  • K60至尊版剛預熱 一加Ace2 Pro正面硬剛

    Redmi這邊剛如火如荼的宣傳了K60 Ultra的各種技術和硬件配置,作為競品的一加也坐不住了。一加中國區總裁李杰發布了兩條微博,表示在自家的一加Ace2上早就已經采用了和PixelWo
  • 容量越大越不壞?24萬塊硬盤故障率報告公布 這些產品零故障

    8月5日消息,云存儲服務商Backblaze發布了最新的硬盤故障率報告,年故障率有所上升。Backblaze發布的硬盤季度統計數據,其中包括故障率等重要方面。這些結
  • 一年經驗在二線城市面試后端的經驗分享

    忠告這篇文章只適合2年內工作經驗、甚至沒有工作經驗的朋友閱讀。如果你是2年以上工作經驗,請果斷劃走,對你沒啥幫助~主人公這篇文章內容來自 「升職加薪」星球星友 的投稿,坐
  • 多線程開發帶來的問題與解決方法

    使用多線程主要會帶來以下幾個問題:(一)線程安全問題  線程安全問題指的是在某一線程從開始訪問到結束訪問某一數據期間,該數據被其他的線程所修改,那么對于當前線程而言,該線程
  • 每天一道面試題-CPU偽共享

    前言:了不起:又到了每天一到面試題的時候了!學弟,最近學習的怎么樣啊 了不起學弟:最近學習的還不錯,每天都在學習,每天都在進步! 了不起:那你最近學習的什么呢? 了不起學弟:最近在學習C
  • 重估百度丨“晚熟”的百度云,能等到春天嗎?

    &copy;自象限原創作者|程心排版|王喻可2016年7月13日,百度云計算戰略發布會在北京舉行,宣告著百度智能云的正式啟程。彼時的會場座無虛席,甚至排隊排到了門外,在場的所有人幾乎都
  • 最“俊美”淘寶賣家,靠直播和短視頻圈粉,上架秒光,年銷3000萬

    來源 | 電商在線文|易琬玉編輯|斯問受訪店鋪:Ringdoll戒之人形圖源:微博@御座的黃山、&ldquo;Ringdoll戒之人形&rdquo;淘寶店鋪有關外貌的評價,黃山已經聽累了。生于1985年的他,哪
  • AMD的AI芯片轉單給三星可能性不大 與臺積電已合作至2nm制程

    據 DIGITIMES 消息,英偉達 AI GPU 出貨逐季飆升,接下來 AMD MI 300 系列將在第 4 季底量產。而半導體業內人士表示,近日傳出 AMD 的 AI 芯片將轉單給
  • 北京:科技教育體驗基地開始登記

      北京“科技館之城”科技教育體驗基地登記和認證工作日前啟動。首批北京科技教育體驗基地擬于2023年全國科普日期間掛牌,后續還將開展常態化登記。  北京科技教育體驗基
Top 日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不
亚洲成人在线视频播放| 国产婷婷色一区二区三区| 欧美高清在线视频| 欧美日韩视频一区二区| 亚洲深夜av| 亚洲欧美在线另类| 久久久噜噜噜久久中文字幕色伊伊 | 亚洲电影av| 亚洲精品无人区| 亚洲欧美日韩在线不卡| 久久一区视频| 国产精品国产a级| 伊人久久亚洲热| 亚洲图片欧洲图片av| 久久看片网站| 欧美视频精品在线| 在线成人激情| 亚洲与欧洲av电影| 欧美成人r级一区二区三区| 国产精品视频九色porn| 亚洲丰满在线| 欧美亚洲一区二区在线| 欧美剧在线观看| 国产一区白浆| 一区二区三区 在线观看视| 久久久久免费视频| 欧美日在线观看| 在线看欧美视频| 亚洲欧美日韩一区二区在线| 欧美成人精品三级在线观看| 国产精品一区三区| 99精品国产热久久91蜜凸| 久久精品一二三| 欧美午夜在线一二页| 亚洲高清在线| 欧美中文字幕| 国产精品久久久久久久久动漫| 在线日韩电影| 欧美有码在线视频| 国产精品久久国产愉拍| 亚洲欧洲免费视频| 久久久久一区二区三区| 国产精品一区视频网站| 一区二区不卡在线视频 午夜欧美不卡'| 久久久久一区二区三区四区| 国产精品久久久久影院色老大| 亚洲欧洲一区二区三区久久| 久久久国产精品一区| 国产精品久久久久9999| 亚洲看片免费| 欧美成人tv| 亚洲电影专区| 久久免费视频观看| 国产一区二区三区在线观看免费| 亚洲一区二区三区在线观看视频| 欧美另类高清视频在线| 亚洲国产精品国自产拍av秋霞| 久久精品综合一区| 国产视频一区三区| 性做久久久久久免费观看欧美| 国产精品成人一区二区三区夜夜夜| 亚洲精选一区| 欧美国产精品v| 亚洲国产另类精品专区 | 欧美日韩亚洲91| 99精品99久久久久久宅男| 欧美精品免费看| 亚洲美女色禁图| 欧美日韩www| 日韩视频免费观看| 欧美黄色一区二区| 亚洲精品激情| 欧美日本久久| 一本色道**综合亚洲精品蜜桃冫| 欧美理论电影在线观看| 日韩视频免费看| 欧美日韩喷水| 亚洲视频电影图片偷拍一区| 欧美性猛交xxxx乱大交退制版| 一区二区欧美日韩| 国产精品二区影院| 亚洲在线一区二区三区| 国产精品视频区| 先锋影院在线亚洲| 国产精品一区久久久| 久久爱另类一区二区小说| 国产在线成人| 久久亚洲综合色一区二区三区| 在线不卡免费欧美| 老司机精品视频网站| 亚洲国产另类久久精品| 欧美福利视频在线| 日韩一级网站| 国产精品成人免费精品自在线观看| 亚洲午夜免费视频| 国产日韩欧美在线播放| 久久国内精品自在自线400部| 今天的高清视频免费播放成人 | 欧美日韩ab| 亚洲新中文字幕| 国产精品日韩欧美大师| 国产亚洲人成a一在线v站| 亚洲素人一区二区| 久久精品亚洲热| 欧美体内she精视频在线观看| 一本色道久久综合一区| 欧美色播在线播放| 亚洲在线中文字幕| 国产婷婷色综合av蜜臀av | 久久人人九九| 亚洲国产日韩综合一区| 欧美日韩色婷婷| 亚洲欧美在线看| 黄色综合网站| 欧美激情aaaa| 亚洲午夜激情网站| 国产一区二区三区四区hd| 亚洲一级在线观看| 国产综合亚洲精品一区二| 欧美成熟视频| 亚洲午夜久久久久久尤物 | 欧美日韩欧美一区二区| 欧美一区久久| 亚洲人成人77777线观看| 欧美日韩国产精品一卡| 欧美一级淫片aaaaaaa视频| 伊人色综合久久天天| 欧美精品免费观看二区| 午夜视频在线观看一区二区三区| 国产日韩欧美三区| 免费在线看成人av| 亚洲欧美日韩国产一区二区三区 | 欧美日韩国产一中文字不卡 | 欧美精品乱码久久久久久按摩| 中文欧美字幕免费| 黑人巨大精品欧美一区二区小视频| 欧美大秀在线观看| 亚洲欧美电影在线观看| **性色生活片久久毛片| 欧美亚州一区二区三区| 欧美一区二区私人影院日本| 亚洲韩国精品一区| 国产精品日韩一区| 欧美黑人在线观看| 久久久999成人| 亚洲图片在区色| 在线日韩中文字幕| 国产精品视频一区二区高潮| 女同性一区二区三区人了人一| 中国日韩欧美久久久久久久久| 伊人夜夜躁av伊人久久| 国产精品一区一区| 欧美黑人国产人伦爽爽爽| 欧美一级午夜免费电影| 99天天综合性| 在线成人激情黄色| 国产欧美精品va在线观看| 欧美理论大片| 免费高清在线一区| 亚洲免费在线视频| 亚洲精品国产系列| 狠狠色狠狠色综合日日91app| 国产精品久久久久77777| 欧美激情一区二区三区蜜桃视频| 久久精品国产清高在天天线| 亚洲欧美国产一区二区三区| 亚洲精品一区二区三区av| 伊人成人在线| 国产一区二区三区电影在线观看| 国产精品swag| 欧美剧在线观看| 欧美成人精品1314www| 久久久久成人精品免费播放动漫| 亚洲欧美国产高清va在线播| 一本一本久久| 日韩午夜在线播放| 亚洲精品1区2区| 亚洲第一色在线| 狠狠色丁香久久综合频道| 国产亚洲午夜高清国产拍精品| 国产精品视频yy9099| 国产精品www网站| 欧美日韩三级一区二区| 欧美精品国产一区二区| 欧美高清视频一二三区| 男人的天堂亚洲| 久久天堂成人| 久久久久久亚洲综合影院红桃 | 亚洲少妇自拍| 日韩午夜在线电影| 亚洲久久在线| 亚洲毛片在线观看.| 最新高清无码专区| 亚洲人成网站999久久久综合| 亚洲成在人线av| 极品av少妇一区二区| 国产专区欧美精品| 韩国av一区二区三区在线观看| 国产九区一区在线| 国产一区二区av| 国产亚洲成av人片在线观看桃| 国产久一道中文一区| 国产亚洲人成a一在线v站| 国产原创一区二区|