Python 源碼閱讀:tuple
(點擊
上方藍字
,快速關注我們)
來源:伯樂在線 - wklken
如有好文章投稿,請點擊 → 這裡了解詳情
示例
>>>
a
=
()
>>>
b
=
()
>>>
id
(
a
)
==
id
(
b
)
True
>>>
a
=
(
1
,
)
>>>
b
=
(
1
,
)
>>>
id
(
a
)
==
id
(
b
)
False
源碼位置 Include/tupleobject.h | Objects/tupleobject.c
結構
定義
typedef
struct
{
PyObject_VAR_HEAD
PyObject *
ob_item
[
1
];
}
PyTupleObject
;
說明
1.
PyObject_VAR_HEAD
PyTupleObject
在底層是個變長對象(
需要存儲列表元素個數).
雖然
,
在
python
中,
tuple
是不可變對象
2.
PyObject *
ob_item
[
1
];
指向存儲元素的數組
結構
構造方法
PyAPI_FUNC(PyObject *) PyTuple_New(Py_ssize_t size);
構造
看下構造方法定義
PyObject *
PyTuple_New
(
register Py_ssize_t
size
)
{
register PyTupleObject *
op
;
Py_ssize
_
t
i
;
// 大小為負數, return
if
(
size
0
)
{
PyErr_BadInternalCall
();
return
NULL
;
}
// 如果大小=0, 空元組, 直接取free_list第一個返回
#if PyTuple_MAXSAVESIZE > 0
if
(
size
==
0
&
free_list
[
0
])
{
op
=
free_list
[
0
];
Py_INCREF
(
op
);
#ifdef COUNT_ALLOCS
tuple_zero_allocs
++
;
#endif
return
(
PyObject *
)
op
;
}
// 如果free_list可分配, 從free_list取一個
if
(
size
PyTuple_MAXSAVESIZE
&
(
op
=
free_list
[
size
])
!=
NULL
)
{
// 上面 op = free_list[size] 取得單鏈表頭
// free_list指向單鏈表下一個元素, 對應位置閾值--
free_list
[
size
]
=
(
PyTupleObject *
)
op
->
ob_item
[
0
];
numfree
[
size
]
--
;
#ifdef COUNT_ALLOCS
fast_tuple_allocs
++
;
#endif
// 初始化 ob_size和ob_type
/* Inline PyObject_InitVar */
#ifdef Py_TRACE_REFS
Py_SIZE
(
op
)
=
size
;
Py_TYPE
(
op
)
= &
PyTuple_Type
;
#endif
_Py_NewReference
((
PyObject *
)
op
);
}
else
#endif // free_list不可用
{
// 計算空間
Py_ssize_t
nbytes
=
size *
sizeof
(
PyObject *
);
/* Check for overflow */
if
(
nbytes
/
sizeof
(
PyObject *
)
!=
(
size_t
)
size
||
(
nbytes
>
PY_SSIZE_T_MAX
-
sizeof
(
PyTupleObject
)
-
sizeof
(
PyObject *
)))
{
return
PyErr_NoMemory
();
}
// 分配內存
op
=
PyObject_GC_NewVar
(
PyTupleObject
,
&
PyTuple_Type
,
size
);
if
(
op
==
NULL
)
return
NULL
;
}
// 初始化ob_item每個元素
for
(
i
=
0
;
i
size
;
i
++
)
op
->
ob_item
[
i
]
=
NULL
;
// 第一次分配空數組, 將其放入free_list第一個位置
#if PyTuple_MAXSAVESIZE > 0
if
(
size
==
0
)
{
free_list
[
0
]
=
op
;
++
numfree
[
0
];
Py_INCREF
(
op
);
/* extra INCREF so that this is never freed */
}
#endif
#ifdef SHOW_TRACK_COUNT
count_tracked
++
;
#endif
_PyObject_GC_TRACK
(
op
);
// 返回
return
(
PyObject *
)
op
;
}
簡化步驟
1.
如果
size
=
0
,
從
free_list
[
0
]
取,
直接返回
2.
否則
,
確認
free_list
[
size
],
是否可用
,
可用獲取
3.
否則
,
從內存分配新的空間
4.
初始化
,
返回
回收
定義
static
void
tupledealloc
(
register PyTupleObject *
op
)
{
register
Py_ssize
_
t
i
;
// 獲取元素個數
register Py_ssize_t
len
=
Py_SIZE
(
op
);
PyObject_GC_UnTrack
(
op
);
Py_TRASHCAN_SAFE_BEGIN
(
op
)
if
(
len
>
0
)
{
i
=
len
;
// 遍歷, 析構每個元素
while
(
--
i
>=
0
)
Py_XDECREF
(
op
->
ob_item
[
i
]);
// 與對象緩衝池相關
#if PyTuple_MAXSAVESIZE > 0
if
(
len
ob_item
[
0
]
=
(
PyObject *
)
free_list
[
len
];
numfree
[
len
]
++
;
free_list
[
len
]
=
op
;
goto
done
;
/* return */
}
#endif
}
// 調用回收
Py_TYPE
(
op
)
->
tp_free
((
PyObject *
)
op
);
done
:
Py_TRASHCAN_SAFE_END
(
op
)
}
簡化流程
1.
回收
ob
_
item每個元素
2.
如果符合條件
,
放入到
free
_
list
3.
否則
,
回收
tuple對象緩衝池
定義
/* Speed optimization to avoid frequent malloc/free of small tuples */
#ifndef PyTuple_MAXSAVESIZE
#define PyTuple_MAXSAVESIZE 20
#endif
#ifndef PyTuple_MAXFREELIST
#define PyTuple_MAXFREELIST 2000
#endif
#if PyTuple_MAXSAVESIZE > 0
static
PyTupleObject *
free_list
[
PyTuple_MAXSAVESIZE
];
static
int
numfree
[
PyTuple_MAXSAVESIZE
];
#endif
結論
1.
作用
:
優化小tuple
的mall
/
free
2.
PyTuple_MAXSAVESIZE
=
20
會被緩存的
tuple
長度閾值,
20
,
長度
free_list的結構
回頭看回收跟對象緩衝池相關的邏輯
條件:
if
(
len
<
PyTuple_MAXSAVESIZE
&& //
len
<
20
numfree
[
len
]
<
PyTuple_MAXFREELIST
&& //
numfree
[
len
]
<
2000
Py_TYPE
(
op
)
== &
PyTuple_Type
)
//
是tuple
類型操作
op
->
ob_item
[
0
]
=
(
PyObject *
)
free_list
[
len
];
//ob_item指向free_list[len] 單鏈表頭
numfree
[
len
]
++
;
// len位置計數+1
free_list
[
len
]
=
op
;
// op變成單鏈表的頭
goto
done
;
/* return */
即過程
1.
如果
size
=
0
,
直接從
free_list
[
0
]
取
2.
如果
size
!=
0
,
判斷
size
20
走內存分配邏輯
------------------
回收時
如果
size
注意
1.
回收時
,
ob
_
item都會被回收,
只是本身對象緩存下來
2.
這裡
free_list
,
復用
ob
_
item作為鏈表指針,
指向下一個位置
(
通用整數對象池也是復用指針的方式,
不過用的是
ob_type
)
本系列
《Python 源碼閱讀:類型》
《Python 源碼閱讀:對象》
《Python 源碼閱讀:String》
《Python 源碼閱讀:int》
《Python 源碼閱讀:list》
看完本文有收穫?請轉
發分享給更多人
關注「P
ython開發者」,提升Python技能


※Flask 中模塊化應用的實現
※編程零基礎,如何變身年薪百萬的機器學習工程師?
※讓你的 Python 代碼優雅又地道,15篇 Python 技術熱文
※Python 源碼閱讀:list
※Flask 應用中的 URL 處理
TAG:Python開發者 |