$ ./configure --prefix=/Users/andrew_liu/GitClone/cpython/Build $ make $ make install
#1. Python内建对象
Python世界中一切都是对象
1 2 3 4 5 6 7 8 9 10
/* PyObject_HEAD defines the initial segment of every PyObject. */ #define PyObject_HEAD \ _PyObject_HEAD_EXTRA \ Py_ssize_t ob_refcnt; \ struct _typeobject *ob_type;
/* PyObject_VAR_HEAD defines the initial segment of all variable-sizecontainer objects */ #define PyObject_VAR_HEAD \ PyObject_HEAD \ Py_ssize_t ob_size; /* Number of items in variable part 指明变长对象中容纳元素的个数*/
#intoject.c #小整数 #ifndef NSMALLPOSINTS #define NSMALLPOSINTS 257 #endif #ifndef NSMALLNEGINTS #define NSMALLNEGINTS 5 #endif #if NSMALLNEGINTS + NSMALLPOSINTS > 0 /* References to small integers are saved in this array so that they can be shared. The integers that are saved are those in the range -NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive). */
#大整数 #define BLOCK_SIZE 1000 /* 1K less typical malloc overhead */ #define BHEAD_SIZE 8 /* Enough for a 64-bit pointer */ #define N_INTOBJECTS ((BLOCK_SIZE - BHEAD_SIZE) / sizeof(PyIntObject))
typedef struct { PyObject_VAR_HEAD #ob_size表示使用中的内存 /* Vector of pointers to list elements. list[0] is ob_item[0], etc. * 0 <= ob_size <= allocated * len(list) == ob_size * ob_item == NULL implies ob_size == allocated == 0 */ PyObject **ob_item; /* ob_item contains space for 'allocated' elements. The number currently in use is ob_size. */ Py_ssize_t allocated; #记录申请的总内存大小 } PyListObject;
static int ins1(PyListObject *self, Py_ssize_t where, PyObject *v) { Py_ssize_t i, n = Py_SIZE(self); PyObject **items; if (v == NULL) { PyErr_BadInternalCall(); return -1; } if (n == PY_SSIZE_T_MAX) { PyErr_SetString(PyExc_OverflowError, "cannot add more objects to list"); return -1; }
if (list_resize(self, n+1) == -1) #调整列表容量realloc return -1;
if (where < 0) { #确定插入点 为负的时候 where += n; if (where < 0) where = 0; } if (where > n) #大于容量时 where = n; items = self->ob_item; for (i = n; --i >= where; ) #元素后移 items[i+1] = items[i]; Py_INCREF(v); items[where] = v; #插入元素 return 0; }
static void list_dealloc(PyListObject *op) { Py_ssize_t i; PyObject_GC_UnTrack(op); Py_TRASHCAN_SAFE_BEGIN(op) if (op->ob_item != NULL) { #销毁元素列表 /* Do it backwards, for Christian Tismer. There's a simple test case where somehow this reduces thrashing when a *very* large list is created and immediately deleted. */ i = Py_SIZE(op); while (--i >= 0) { Py_XDECREF(op->ob_item[i]); #减少引用计数 } PyMem_FREE(op->ob_item); } if (numfree < PyList_MAXFREELIST && PyList_CheckExact(op)) #销毁list对象自身, 先查看缓冲池是否满了, 没满就放入缓冲池 free_list[numfree++] = op; else Py_TYPE(op)->tp_free((PyObject *)op); Py_TRASHCAN_SAFE_END(op) }
#dictobject.h typedef struct { /* Cached hash code of me_key. Note that hash codes are C longs. * We have to use Py_ssize_t instead because dict_popitem() abuses * me_hash to hold a search finger. */ Py_ssize_t me_hash; PyObject *me_key; PyObject *me_value; } PyDictEntry;
/* The table contains ma_mask + 1 slots, and that's a power of 2. * We store the mask instead of the size because the mask is more * frequently needed. */ Py_ssize_t ma_mask; #entry个数, 因为作大量与操作, 所以不用ma_size命名
/* ma_table points to ma_smalltable for small tables, else to * additional malloc'ed memory. ma_table is never NULL! This rule * saves repeated runtime null-tests in the workhorse getitem and * setitem calls. */ PyDictEntry *ma_table; #entry小于8,则指向ma_smalltable, 超过8个会额外申请内存, 并且指针指向内存, 不会为NULL PyDictEntry *(*ma_lookup)(PyDictObject *mp, PyObject *key, long hash); PyDictEntry ma_smalltable, [PyDict_MINSIZE]; #至少有8个entry被创建 };
i = (size_t)hash & mask; #哈希, 定位第一个entry ep = &ep0[i]; #entry处理Unused或者entry中key匹配 if (ep->me_key == NULL || ep->me_key == key) return ep;
if (ep->me_key == dummy) #第一个entry的key为dummy状态 freeslot = ep; #freeslot指向第一个处于Dummy的entry else { #检查Active的entry if (ep->me_hash == hash) { startkey = ep->me_key; Py_INCREF(startkey); cmp = PyObject_RichCompareBool(startkey, key, Py_EQ); Py_DECREF(startkey); if (cmp < 0) return NULL; if (ep0 == mp->ma_table && ep->me_key == startkey) { if (cmp > 0) return ep; } else { /* The compare did major nasty stuff to the * dict: start over. * XXX A clever adversary could prevent this * XXX from terminating. */ return lookdict(mp, key, hash); } } freeslot = NULL; }
/* In the loop, me_key == dummy is by far (factor of 100s) the least likely outcome, so test for that last. */ for (perturb = hash; ; perturb >>= PERTURB_SHIFT) { i = (i << 2) + i + perturb + 1; ep = &ep0[i & mask]; if (ep->me_key == NULL) #到达Unused状态entry,搜索失败 return freeslot == NULL ? ep : freeslot; if (ep->me_key == key) #检查是否引用相同 return ep; if (ep->me_hash == hash && ep->me_key != dummy) { #检查值是否相同 startkey = ep->me_key; Py_INCREF(startkey); cmp = PyObject_RichCompareBool(startkey, key, Py_EQ); Py_DECREF(startkey); if (cmp < 0) return NULL; if (ep0 == mp->ma_table && ep->me_key == startkey) { if (cmp > 0) return ep; } else { /* The compare did major nasty stuff to the * dict: start over. * XXX A clever adversary could prevent this * XXX from terminating. */ return lookdict(mp, key, hash); } } else if (ep->me_key == dummy && freeslot == NULL) #设置freeslot freeslot = ep; } assert(0); /* NOT REACHED */ return 0; }
For both, when the key isn’t found a PyDictEntry* is returned for which the me_value field is NULL
/* Restructure the table by allocating a new table and reinserting all items again. When entries have been deleted, the new table may actually be smaller than the old one. */ static int dictresize(PyDictObject *mp, Py_ssize_t minused) { Py_ssize_t newsize; PyDictEntry *oldtable, *newtable, *ep; Py_ssize_t i; int is_oldtable_malloced; PyDictEntry small_copy[PyDict_MINSIZE];
/* Get space for a new table. */ oldtable = mp->ma_table; assert(oldtable != NULL); is_oldtable_malloced = oldtable != mp->ma_smalltable;
if (newsize == PyDict_MINSIZE) { #新table可以使用mp->ma_smalltable /* A large table is shrinking, or we can't get any smaller. */ newtable = mp->ma_smalltable; if (newtable == oldtable) { if (mp->ma_fill == mp->ma_used) { /* No dummies, so no point doing anything. */ return 0; } /* We're not going to resize it, but rebuild the table anyway to purge old dummy entries. Subtle: This is *necessary* if fill==size, as lookdict needs at least one virgin slot to terminate failing searches. If fill < size, it's merely desirable, as dummies slow searches. */ assert(mp->ma_fill > mp->ma_used); memcpy(small_copy, oldtable, sizeof(small_copy)); #将旧table拷贝, 进行备份 oldtable = small_copy; } } else { #新table需要在系统堆上申请 newtable = PyMem_NEW(PyDictEntry, newsize); if (newtable == NULL) { PyErr_NoMemory(); return -1; } }
/* Make the dict empty, using the new table. */ assert(newtable != oldtable); mp->ma_table = newtable; mp->ma_mask = newsize - 1; memset(newtable, 0, sizeof(PyDictEntry) * newsize); mp->ma_used = 0; i = mp->ma_fill; mp->ma_fill = 0;
/* Copy the data over; this is refcount-neutral for active entries; dummy entries aren't copied over, of course */ for (ep = oldtable; i > 0; ep++) { if (ep->me_value != NULL) { /* active entry */ --i; insertdict_clean(mp, ep->me_key, (long)ep->me_hash, ep->me_value); } else if (ep->me_key != NULL) { /* dummy entry */ --i; assert(ep->me_key == dummy); Py_DECREF(ep->me_key); } /* else key == value == NULL: nothing to do */ }
if (is_oldtable_malloced) PyMem_DEL(oldtable); return 0; }