定義
typedef
struct pystringobject;
說明
pyobject_var_head構造方法 pystring_fromstringpystringobject是變長物件, 比定長物件多了乙個ob_size欄位
ob_shash
儲存字串的hash值, 如果還沒計算等於-1
當string_hash被呼叫, 計算結果會被儲存到這個字段乙份, 後續不再進行計算
用來快取當前字串的雜湊值,這在以字串作為key的dict物件查詢時非常有用。
ob_sstate
如果是interned, !=0, 否則=0
interned後面說
char ob_sval[1]
這個ob_sval比較巧妙,定義的是乙個字元的陣列,他的作用其實是作為實際分配記憶體後,字串緩衝區的指標。在實際給字串物件分配記憶體時,python申請的記憶體大小為pystringobject + 字串長度。字元指標指向一段記憶體, char陣列指標, 指向乙個ob_size+1大小陣列(c中字串最後要多乙個字元
\0
表字串結束)
//預設未初始化, 均為null
static pystringobject *characters[uchar_max + 1];
static pystringobject *nullstring;
pyobject *
pystring_fromstring(const
char *str)
// 長度=0, 且nullstring已定義, 返回nullstring
if (size == 0 && (op = nullstring) != null)
// 字元緩衝池邏輯
// 長度=1, 且characters[*str & uchar_max]字元已定義
if (size == 1 && (op = characters[*str & uchar_max]) != null)
// 申請記憶體空間
/* inline pyobject_newvar */
op = (pystringobject *)pyobject_malloc(pystringobject_size + size);
if (op == null)
return pyerr_nomemory();
// 初始化
pyobject_init_var(op, &pystring_type, size);
op->ob_shash = -1; //未計算hash, -1
op->ob_sstate = sstate_not_interned; //未intern, 0
// 將字元陣列拷貝到pystringobject
py_memcpy(op->ob_sval, str, size+1);
// 在nullstring和字元緩衝池對應位置未初始化時, 會走到這個邏輯
/* share short strings */
if (size == 0) else
if (size == 1)
// 返回
return (pyobject *) op;
}
說明
和整數物件一樣,字串物件同樣有物件池機制提供對ascii碼物件的快速引用。其原理是,當字串被建立時,pystring_fromstring函式會檢查字串長度,如果長度為1,則先建立字串,然後intern字串,最後將字串物件指標儲存在characters物件池中。以後訪問字串,如果長度為1,就直接從物件池中返回物件指標。結論步驟簡化
1. 計算長度
2. 長度0, 空字串, 是返回已定義好的nullstring
3. 長度1, 字元, 返回字元緩衝池裡面的
4. 都不是, 分配記憶體, 初始化
長度0/長度1, 會用到intern機制interned機制注意, intern機制對長度》1的字串也適用
對於相同的兩個字串,python不會為其建立兩個字串物件,以節省記憶體。intern機制由乙個全域性的intern字典維護。當新建乙個字串時,執行時會在intern字典中查詢是否已經存在這個字串,如果存在,則直接增加字典裡的字串物件的引用計數;如果不存在,則將其新增到字典中。interned定義
void
pystring_interninplace(pyobject **p)
}// 在interned字典中已存在, 修改, 返回intern獨享
t = pydict_getitem(interned, (pyobject *)s);
if (t)
// 在interned字典中不存在, 放進去
if (pydict_setitem(interned, (pyobject *)s, (pyobject *)s) < 0)
// 加入interned字典(key-value), 會導致refcnt+2, 去掉, 保證當外部沒有引用時, refcnt=0, 可以進行**處理, (不-2, refcnt永遠》=2)
/* the two references in interned are not counted by refcnt.
the string deallocator will take care of this */
py_refcnt(s) -= 2;
// 修改字串物件的ob_sstate標誌位, sstate_interned_mortal
pystring_check_interned(s) = sstate_interned_mortal;
}
str使用的地方
// 構造方法
pyapi_func(pyobject *) pystring_fromstring(const
char *);
pyapi_func(pyobject *) pystring_fromstringandsize(const
char *, py_ssize_t);
// sstate_interned_mortal, 計數0會被**
pyobject *
pystring_internfromstring(const
char *cp)
// sstate_interned_immortal, 永遠不會被銷毀
void
pystring_internimmortal(pyobject **p)
示例
>>> a = ''
>>> b = ''
>>> id(a) == id(b)
true
>>> a = 'x'
>>> b = 'x'
>>> id(a) == id(b)
true
>>> a = "abc"
>>> b = "abc"
>>> id(a) == id(b)
true
好處
一旦字串被intern, 會被python儲存到字典中, 整個python執行期間, 系統中有且僅有乙個物件. 下次相同字串再進入, 不會重複建立物件.
字串拼接有兩種方式:「+」操作符和str.join(iterable)函式。
『a』 + 『b』 + 『c』
or 」.join([『a』, 『b』, 『c』])
「+」操作符會呼叫string_concat函式。它會重新申請一塊記憶體,然後將兩個字串複製到新的記憶體中,返回乙個字串物件。也就是說,n個字串拼接會呼叫n-1次string_concat函式,即反覆申請n-1次記憶體,效率是非常底下的。string_concat:
一次』+』=分配一次記憶體空間, 拷貝兩次. n次鏈結, 需要n-1次記憶體分配.
string_join:所以官方推薦str.join(iterable)函式,它接受乙個list或tuple等可迭代物件,統計有多少個字串,統一分配一次記憶體,然後複製左右字串到新記憶體中,並返回字串物件。可見後者效率要高很多
計算序列所有元素字串總長度, 用
pystring_fromstringandsize((char*)null, sz)分配記憶體空間, 然後逐一拷貝. 一次記憶體操作.
python原始碼剖析 Python原始碼剖析
第頁共 頁python 原始碼剖析 物件機制 1.物件 在python 的世界中,一切都是物件,乙個整數是乙個物件,乙個字串也是 乙個物件,更為奇妙的是,型別也是乙個物件,整數型別是乙個物件,字串類 型也是乙個物件。從 年guido 在那個聖誕節揭開 python 世界的大幕開始,一直到現在,pyt...
原始碼剖析 Hashtable 原始碼剖析
hashtable同樣是基於雜湊表實現的,同樣每個元素都是key value對,其內部也是通過單鏈表解決衝突問題,容量不足 超過了閾值 時,同樣會自動增長。hashtable也是jdk1.0引入的類,是執行緒安全的,能用於多執行緒環境中。hashtable同樣實現了serializable介面,它支...
原始碼剖析之CopyOnWriteArrayList
copyonwritearraylist jdk1.5新增的執行緒安全的arraylist實現。b 使用場景 讀取頻繁,寫較少。理由 底層的安全性 本質上是依賴於執行緒讀取的資料副本來實現的。因此每次寫都是要複製底層陣列資料的,如果寫頻繁勢必會造成大量的效能消耗。如果寫非常頻繁,那麼可以根據實際情況...