2018年8月2日 星期四

Python 源碼解析之 list 與 tuple 的 特性與 操作methods 比較

本文最終將清楚了解:
得知了 list 與 tuple 的 methods 之差異,例如 list 可以 sort 排序, 而 tuple 就不能 sort 排序;

以及兩者的特性:
List其特性有:
List is a collection which is ordered and changeable. Allows duplicate members.
且tuple因其特性有:
Tuple is a collection which is ordered and unchangeable. Allows duplicate members.
是ordered順序性與unchangeable(不可改變)的特性。

內文開始:
使用Python語法開發的工程師,應該都知道list 與 tuple這兩種的差異性。也應熟知它們之間可用的methods,list是比較多,tuple是比較少。
List其特性有:
List is a collection which is ordered and changeable. Allows duplicate members.
且tuple因其特性有:
Tuple is a collection which is ordered and unchangeable. Allows duplicate members.
是ordered順序性與unchangeable(不可改變)的特性。為何呢??

當你是資深Python工程師,我個人觀點是要很深入了解,Python其底層源碼是什麼? 才會讓 List 與 tuple 兩種有些差異性。 [ 註: 或者是從事 Python 程式設計講師 也是要非常熟源碼 ]

先剖析 List 是 ordered and changeable;tuple是 ordered and unchangeable。

觀察Python 源碼的 listobject.h 與 tupleobject.h,

在 listobject.h 有 PyListObject 的宣告:
typedef struct {
    PyObject_VAR_HEAD
    PyObject **ob_item;
    Py_ssize_t allocated;
} PyListObject;

在 tupleobject.h有 PyTupleObject 的宣告:
typedef struct {
    PyObject_VAR_HEAD
    PyObject *ob_item[1];
} PyTupleObject;

兩者第一個 struct member是 PyObject_VAR_HEAD,這定義在 object.h,有關之程式碼如下:

#define PyObject_VAR_HEAD PyVarObject ob_base;

PyObject_VAR_HEAD是個巨集,其marco defination是 PyVarObject ob_base;

再來 PyVarObject 的定義如下:

typedef struct {
    PyObject ob_base;
    Py_ssize_t ob_size; /* Number of items in variable part */
} PyVarObject;

PyVarObject第一個member是PyObject,其定義如下:

typedef struct _object {
    _PyObject_HEAD_EXTRA
    Py_ssize_t ob_refcnt;
    struct _typeobject *ob_type;
} PyObject;

PyObject第一個member是 _PyObject_HEAD_EXTRA ,其定義如下:

再分析一下源碼的 _PyObject_HEAD_EXTRA,

#ifdef Py_TRACE_REFS
/* Define pointers to support a doubly-linked list of all live heap objects. */
#define _PyObject_HEAD_EXTRA \
    struct _object *_ob_next; \
    struct _object *_ob_prev;
#define _PyObject_EXTRA_INIT 0, 0,
#else
#define _PyObject_HEAD_EXTRA
#define _PyObject_EXTRA_INIT
#endif

這段是如果定義了 Py_TRACE_REFS 則 Objects 會變成是一個雙向列表, 但是在release版本下沒有定義 Py_TRACE_REFS , 因此這個 MARCO 可以忽略.

最終的 list 與 tuple 之結構型態的 member 呈現分別是:

在 listobject.h 有 PyListObject 的宣告:
typedef struct {
    Py_ssize_t ob_refcnt;
    struct _typeobject *ob_type;
    Py_ssize_t ob_size;
    PyObject **ob_item;  //此處PyObject宣告了List的 ob_item
    Py_ssize_t allocated;
} PyListObject;

在 tupleobject.h有 PyTupleObject 的宣告:
typedef struct {
    Py_ssize_t ob_refcnt;
    struct _typeobject *ob_type;
    Py_ssize_t ob_size;
    PyObject *ob_item[1]; //此處PyObject宣告了 tuple 的 ob_item
} PyTupleObject;

在最終的 list 與 tuple 之結構型態知道,list 是以 PyObject **ob_item; 來處理其 item data;
而 tuple 是以 PyObject *ob_item[1]; 來處理其 item data。
因其型態特性不同,致在其對應的PyTypeObject也不同。

list 的 PyTypeObject 源碼在 listobject.c,程式如下:
PyTypeObject PyList_Type = {
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
    "list",
    sizeof(PyListObject),
    0,
    (destructor)list_dealloc,                   /* tp_dealloc */
    0,                                          /* tp_print */
    0,                                          /* tp_getattr */
    0,                                          /* tp_setattr */
    0,                                          /* tp_reserved */
    (reprfunc)list_repr,                        /* tp_repr */
    0,                                          /* tp_as_number */
    &list_as_sequence,                          /* tp_as_sequence */
    &list_as_mapping,                           /* tp_as_mapping */
    PyObject_HashNotImplemented,                /* tp_hash */
    0,                                          /* tp_call */
    0,                                          /* tp_str */
    PyObject_GenericGetAttr,                    /* tp_getattro */
    0,                                          /* tp_setattro */
    0,                                          /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
        Py_TPFLAGS_BASETYPE | Py_TPFLAGS_LIST_SUBCLASS, /* tp_flags */
    list___init____doc__,                       /* tp_doc */
    (traverseproc)list_traverse,                /* tp_traverse */
    (inquiry)_list_clear,                       /* tp_clear */
    list_richcompare,                           /* tp_richcompare */
    0,                                          /* tp_weaklistoffset */
    list_iter,                                  /* tp_iter */
    0,                                          /* tp_iternext */
    list_methods,                               /* tp_methods */
    0,                                          /* tp_members */
    0,                                          /* tp_getset */
    0,                                          /* tp_base */
    0,                                          /* tp_dict */
    0,                                          /* tp_descr_get */
    0,                                          /* tp_descr_set */
    0,                                          /* tp_dictoffset */
    (initproc)list___init__,                    /* tp_init */
    PyType_GenericAlloc,                        /* tp_alloc */
    PyType_GenericNew,                          /* tp_new */
    PyObject_GC_Del,                            /* tp_free */
};

tuple 的 PyTypeObject 源碼在 tupleobject.c,程式如下:
PyTypeObject PyTuple_Type = {
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
    "tuple",
    sizeof(PyTupleObject) - sizeof(PyObject *),
    sizeof(PyObject *),
    (destructor)tupledealloc,                   /* tp_dealloc */
    0,                                          /* tp_print */
    0,                                          /* tp_getattr */
    0,                                          /* tp_setattr */
    0,                                          /* tp_reserved */
    (reprfunc)tuplerepr,                        /* tp_repr */
    0,                                          /* tp_as_number */
    &tuple_as_sequence,                         /* tp_as_sequence */
    &tuple_as_mapping,                          /* tp_as_mapping */
    (hashfunc)tuplehash,                        /* tp_hash */
    0,                                          /* tp_call */
    0,                                          /* tp_str */
    PyObject_GenericGetAttr,                    /* tp_getattro */
    0,                                          /* tp_setattro */
    0,                                          /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
        Py_TPFLAGS_BASETYPE | Py_TPFLAGS_TUPLE_SUBCLASS, /* tp_flags */
    tuple_new__doc__,                           /* tp_doc */
    (traverseproc)tupletraverse,                /* tp_traverse */
    0,                                          /* tp_clear */
    tuplerichcompare,                           /* tp_richcompare */
    0,                                          /* tp_weaklistoffset */
    tuple_iter,                                 /* tp_iter */
    0,                                          /* tp_iternext */
    tuple_methods,                              /* tp_methods */
    0,                                          /* tp_members */
    0,                                          /* tp_getset */
    0,                                          /* tp_base */
    0,                                          /* tp_dict */
    0,                                          /* tp_descr_get */
    0,                                          /* tp_descr_set */
    0,                                          /* tp_dictoffset */
    0,                                          /* tp_init */
    0,                                          /* tp_alloc */
    tuple_new,                                  /* tp_new */
    PyObject_GC_Del,                            /* tp_free */
};

我們現將注意力放在其 methods 操作方法:
list 的 methods 操作方法,源碼如下:
static PyMethodDef list_methods[] = {
    {"__getitem__", (PyCFunction)list_subscript, METH_O|METH_COEXIST, "x.__getitem__(y) <==> x[y]"},
    LIST___REVERSED___METHODDEF
    LIST___SIZEOF___METHODDEF
    LIST_CLEAR_METHODDEF
    LIST_COPY_METHODDEF
    LIST_APPEND_METHODDEF
    LIST_INSERT_METHODDEF
    LIST_EXTEND_METHODDEF
    LIST_POP_METHODDEF
    LIST_REMOVE_METHODDEF
    LIST_INDEX_METHODDEF
    LIST_COUNT_METHODDEF
    LIST_REVERSE_METHODDEF
    LIST_SORT_METHODDEF
    {NULL,              NULL}           /* sentinel */
};
而 tuple 的 methods 操作方法,源碼如下:
static PyMethodDef tuple_methods[] = {
    TUPLE___GETNEWARGS___METHODDEF
    TUPLE_INDEX_METHODDEF
    TUPLE_COUNT_METHODDEF
    {NULL,              NULL}           /* sentinel */
};

到此得知了 list 與 tuple 的 methods 之差異,例如 list 可以 sort 排序, 而 tuple 就不能 sort 排序;

以及兩者的特性:
List其特性有:
List is a collection which is ordered and changeable. Allows duplicate members.
且tuple因其特性有:
Tuple is a collection which is ordered and unchangeable. Allows duplicate members.
是ordered順序性與unchangeable(不可改變)的特性。

沒有留言:

張貼留言

FPGA Verilog 的學習經驗,提供給要入門的新手

今天簡單說說 FPGA Verilog 的學習經驗,提供給要入門的新手: 1.對自己寫的FPGA Verilog程式,所生成的數位電路要心中有數。 這一點個人認為很重要,就正如寫 C語言,心中要能生成對應的組合語言一樣,我是這樣要求自己的。 雖然 FPGA Verilog語言...