1 | pass |
其实很多东西都已经在https://www.python.org上面有了。该好好看看上面的文档。
1 | pass |
其实很多东西都已经在https://www.python.org上面有了。该好好看看上面的文档。
十二月,2014年的最后一个月,想想还有好多东西要学,但是感觉一直没有前进。所以想向现代简明魔法得作者学习,列个计划,有了计划就会比较清晰。接下来就等执行了。
集齐7种排序,召唤神猿。(插入排序,冒泡排序,选择排序,归并排序,桶排序,堆排序,快速排序) 学基本的队列,树,图这些基本的数据结构和常用算法。 把shell学好。同时学学awk, sed这些。 学一门新的语言。(会在Lisp和Erlang之间选一个) 《高性能mysql》(关系型数据库) 《Redis入门指南》(公司需要) 《MongoDB权威指南》(之前看过,不用不用全忘了) 《深入理解计算机系统》(操作系统太重要了) 《计算机组成与软硬件接口》(稍微了解点硬件的知识) 《编译原理》(编译原理太重要了) 《Unix网络编程:套接字api》 《Unix网络编程:进程间通信》 《Unix编程艺术》 《Linux系统编程》 《剑指Offer》(开阔视野,知道自己的不足点) 《数学之美》(杨老师推荐的) 《Python源码剖析》(好吧,其实看了两边了,还是有很多很多地方不清楚) 《线性代数》(总感觉特别重要) 《代码整洁之道》(跟工作十分相关) 《C专家编程》(总感觉自己还没有达到看这本书的高度) 《30天自制操作系统》(过年有空自己写个操作系统吧) 《异类 : 不一样的成功启示录》
保护视力,人人有责:http://www.nowamagic.net/librarys/veda/detail/2689
mac上装gdb还是比较麻烦的,好像homebrew上面很多东西被墙了,装东西很慢不过花了整整一个晚上的时间终于装好了。
首先你想用gdb调试你写的C代码,那么在用Gcc编译的时候一定要把源代码信息也一切编译进去。
gcc quick_sort.c -o a.out -g
这里的-g就表示吧源代码信息一起编译进输出文件。
启动gdb有两种方式。
gdb命令后面直接接调试的可执行文件
gdb [options] [executable-file [core-file or process-id]]
gdb a.out
也可以直接打gdb命令启动gdb,然后在交互环境通过file + 目标文件这个命令来导入需要调试的可执行文件。
l(list) 表示显示代码,l 23列出第23行前后5行代码(18-28行)。
b(breakpoint) 表示设置断点,b main这个命令可以在main函数开始的地方设置断点。 也可以这样用b 12这个命令可以在12行设置一个断点。
c(continue) 表示继续执行到下一个断点。
n(next) 表示执行下一条语句,注意这里的下一条语句是不会进入被调用的函数内部的。
s(step) 表示单步调试的下一步, 这里的下一步是会进入被调用的函数内部的。
d(delete) 表示删除断点。 d 2表示删除编号为2的断点,断点的编号是递增的。
r(run) 表示启动执行程序。可以在调试一半然后重新启动执行程序。
p(print) 输出一个变量。p a输出a的值。
clear 清除所有断点。
基本上目前常用点的都在上面了。
俗话说得好,连7种基本的排序算法都没学会,还想找女朋友?好吧,还是乖乖回去写排序吧。
排序有很多种。最常用的就这7种:插入排序,冒泡排序,选择排序,归并排序,桶排序,堆排序,快速排序。
插入排序:
1 | #include "stdio.h" |
选择排序:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43#include "stdio.h"
#define ARRAYLEN 10
void sort(int* array, int len)
{
int i = 0;
for(i=0; i<len; i++)
{
int j = 0;
int min = i;
for(j=i; j<len; j++)
{
if(array[min] > array[j])
{
min = j;
}
}
if(min != j)
{
int item = array[min];
array[min] = array[i];
array[i] = item;
}
}
}
int main(void)
{
int array[ARRAYLEN] = {};
int i = 0;
for(i=0; i< ARRAYLEN; i++)
{
scanf("%d", &array[i]);
}
sort(array, ARRAYLEN);
for(i=0; i< ARRAYLEN; i++)
{
printf("%d ", array[i]);
}
printf("\n");
return 0;
}
冒泡排序:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38#include "stdio.h"
#define ARRAYLEN 10
void sort(int* array, int len)
{
int i = 0;
int j = 0;
for(i=len-1; i>0; i--)
{
for(j=0; j<i; j++)
{
if(array[j] > array[j+1])
{
int item = array[j];
array[j] = array[j+1];
array[j+1] = item;
}
}
}
}
int main(void)
{
int array[ARRAYLEN] = {};
int i = 0;
for(i=0; i< ARRAYLEN; i++)
{
scanf("%d", &array[i]);
}
sort(array, ARRAYLEN);
for(i=0; i< ARRAYLEN; i++)
{
printf("%d ", array[i]);
}
printf("\n");
return 0;
}
归并排序:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68#include "stdio.h"
#define ARRAYLEN 10
void merge_array(int* left, int len_left, int* right, int len_right, int* tmp)
{
int i = 0;
int j = 0;
int k = 0;
while(i<len_left && j<len_right)
{
if(left[i]< right[j])
{
tmp[k++] = left[i++];
}
else
{
tmp[k++] = right[j++];
}
}
while(i < len_left)
{
tmp[k++] = left[i++];
}
while(j < len_right)
{
tmp[k++] = right[j++];
}
for(i=0;i<k;i++)
{
left[i] = tmp[i];
}
}
void merge_sort(int *array, int start, int end, int* tmp)
{
int middle = (start + end) / 2;
if(end-start < 1)
{
return;
}
merge_sort(array, start, middle, tmp);
merge_sort(array, middle+1, end, tmp);
merge_array(&array[start], middle-start+1, &array[middle+1], end-middle, tmp);
}
void sort(int* array, int len)
{
int tmp[ARRAYLEN] = {};
merge_sort(array, 0, len-1, tmp);
}
int main(void)
{
int array[ARRAYLEN] = {};
int i = 0;
for(i=0; i< ARRAYLEN; i++)
{
scanf("%d", &array[i]);
}
sort(array, ARRAYLEN);
for(i=0; i< ARRAYLEN; i++)
{
printf("%d ", array[i]);
}
printf("\n");
return 0;
}
桶排序1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39#include "stdio.h"
#define ARRAYLEN 10
void sort(int* array, int max, int min, int len)
{
int range[ARRAYLEN + 1] = {};
int i = 0;
for(i=0; i<len; i++)
{
range[array[i] - min]++;
}
int j = 0;
for(i=0; i<max-min+1; i++)
{
while(range[i] != 0)
{
array[j++] = i+min;
range[i]--;
}
}
}
int main(void)
{
int array[ARRAYLEN] = {};
int i = 0;
for(i=0; i< ARRAYLEN; i++)
{
scanf("%d", &array[i]);
}
sort(array, ARRAYLEN, 1, ARRAYLEN);
for(i=0; i< ARRAYLEN; i++)
{
printf("%d ", array[i]);
}
printf("\n");
return 0;
}
堆排序1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68#include "stdio.h"
#define ARRAYLEN 11
void heap_adjust(int* array, int id, int size)
{
int lchild = id * 2;
int rchild = lchild + 1;
if(id > size/2)
{
return;
}
int max = id;
if(lchild <= size && array[lchild] > array[max])
{
max = lchild;
}
if(rchild <= size && array[rchild] > array[max])
{
max = rchild;
}
if(max != id)
{
int item = array[id];
array[id] = array[max];
array[max] = item;
heap_adjust(array, max, size);
}
}
void build_heap(int* array, int len)
{
int i = 1;
for(i=len/2; i>0; i--)
{
heap_adjust(array, i, len);
}
}
void sort(int* array, int len)
{
int i=1;
build_heap(array, len-1);
for(i=len-1;i>0;i--)
{
int item = array[1];
array[1] = array[i];
array[i] = item;
heap_adjust(array, 1, i-1);
}
}
int main(void)
{
int i = 0;
int array[ARRAYLEN] = {};
for(i=1; i< ARRAYLEN; i++)
{
scanf("%d", &array[i]);
}
sort(array, ARRAYLEN);
for(i=1; i< ARRAYLEN; i++)
{
printf("%d ", array[i]);
}
printf("\n");
return 0;
}
快速排序1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57#include "stdio.h"
#define ARRAYLEN 10
void sort(int* array, int len)
{
if(len < 2)
{
return;
}
int start = 0;
int i = 0;
int base = array[0];
int position = 0;
int j = len-1;
while(i != j)
{
while(j!=i && array[j]>base)
{
j--;
}
if(i != j)
{
array[position] = array[j];
position = j;
}
while(j!=i && array[i]<base)
{
i++;
}
if(i!=j)
{
array[position] = array[i];
position = i;
}
}
array[position] = base;
sort(array, position+1);
sort(&array[position+1], len-position-1);
}
int main(void)
{
int array[ARRAYLEN] = {};
int i = 0;
for(i=0; i< ARRAYLEN; i++)
{
scanf("%d", &array[i]);
}
sort(array, ARRAYLEN);
for(i=0; i< ARRAYLEN; i++)
{
printf("%d ", array[i]);
}
printf("\n");
return 0;
}
这几天经历了人生中第一次流量撑爆服务器,中间发生了不少问题。我负责交易模块,最主要的问题就是库存的问题。
身为一个互联网程序员,尤其是电商行业负责订单和支付环节的程序员,真的需要绝对清晰。必须清晰考虑各种各样的环节,必须十分清晰地把控每一个细节。尤其是要注意多线程的问题。
如果出bug了,就请大家喝饮料,道个歉。
最近一段日子,虽然学到了一些互联网的游戏规则,但是感觉自己没有什么技术成长。前段时间静爷给我推荐了leetcode。一个程序员眼界很重要,思想同样很重要。思想牛逼的时候效率自然就提高了。还有一点就是以后尽量少加班,加班是程序员最大的敌人。
另外今天看了之前看过的几篇文章,有了新的体会。
聪明的程序员使用50%-70%的时间用来思考,尝试和权衡各种设计和实现,而用30% – 50%的时间是在忙碌着编码,调试和测试。聪明的老板也会让团队这样做。而傻逼的老板,苦逼的程序员会拿出来100%-150%的时间来忙着赶进度,返工,重构,fix 大量的bug… 所以, 越差的团队一般会越忙,而且还忙不完。
http://coolshell.cn/articles/11656.html
http://coolshell.cn/articles/12052.html
http://coolshell.cn/articles/4102.html
自从看了《Python源码剖析》,越来越觉得这是一本操作系统入门书籍。毕竟虚拟机也带有点操作系统的味道,今天逛google,发现以前的一个好朋友也在用Python。以前什么事情都是被她超越,这次不能输给她了。随着对Python的深入,我现在开始明白数据结构的重要性了。只有明白了数据结构和一些常用算法,才能有机会对操作系统绝对清晰。熟悉汇编也是清晰操作系统的必经之路(因为汇编涉及了x86架构)。好了,废话不多说,进入正题。
上面这段话是我昨天写的,今天我看了Python源码的整数部分。先不说Python中整数系统,我先说说看这一章的感受。感受最大的就是C语言,大一的时候学了一点C语言的语法,之后就没有写C了。现在看C代码来真心相当吃力,之前还一直没有体会,这可能跟我那糟糕的数据结构有关,现在越来越明白数据结构的重要性,越来越明白C语言和数据结构是密不可分的东西,它与一些高级语言不一样,高级语言一般都把内存操作封装了,让你可以把注意力集中在业务逻辑上面。不懂C和数据结构的人永远没法绝对清晰。
在Python中整数是通过PyIntObject对象来实现的,PyIntObject是一个immutable(不可变对象),所谓不可变对象就是说PyIntObject所维护的值是永远不会变化的,也正是由于这些不变性,才实现了Python整数池机制。
typedef struct { PyObject_HEAD long ob_ival; } PyIntObject;
Python中整数对象只不过是对C中long类型的一个包装(加上了类型信息和引用计数)。对象的元信息都是存放在类型对象上的,而PyIntObject的类型是PyInt_Type
PyTypeObject PyInt_Type = { PyObject_HEAD_INIT(&PyType_Type) 0, "int", /* 名字 */ sizeof(PyIntObject), /* 创建时候内存大小 */ 0, (destructor)int_dealloc, /* 析钩函数 */ (printfunc)int_print, /* print方法 */ 0, /* tp_getattr 跟tp_getattro类似,不过已经呗弃用 */ 0, /* tp_setter 跟tp_setattro类似,不过已经被弃用 */ (cmpfunc)int_compare, /* 比较操作 */ (reprfunc)int_repr, /* tp_repr repr()调用的方法*/ &int_as_number, /* tp_as_number 数值类型操作的集合 */ 0, /* tp_as_sequence 序列类型操作的集合 */ 0, /* tp_as_mapping map类型操作的集合 */ (hashfunc)int_hash, /* tp_hash 计算哈希值的方法 */ 0, /* tp_call 这个我表示不可理解,int明明十一个可调用对象为什么这里会是0 */ (reprfunc)int_repr, /* tp_str str()调用的方法 */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer 不知道是干嘛用的 */ PyTPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES | PyTPFLAGS——BASETYPE /* tp_flags */ int_doc, /* tp_doc __doc__ */ 0, /* tp_traverse 不知道是干嘛用的*/ 0, /* tp_clear 不知道是干嘛用的*/ 0, /* tp_richcompare 富比较 */ 0, /* tp_weaklistoffset */ 0, /* tp_iter 创建迭代器 */ 0, /* tp_iternext 迭代器的next()方法 */ int_methods, /* tp_methods 成员函数集合 */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get 一个descript */ 0, /* tp_descr_set 一个descript */ 0, /* tp_dictoffset */ 0, /* tp_init 初始化方法 */ 0, /* tp_alloc 申请内存 */ int_new, /* tp_new 申请内存 */ (freefunc)int_free, /* tp_free 释放内存 */ } static PyNumberMethods int_as_number = { (binaryfunc)int_add, /* nb_add */ (binaryfunc)int_sub, /* nb_subtract */ ... (binaryfunc)int_div, /* nb_floor_divide */ int_true_divide, /* nb_true_divide */ 0, /* nb_inplace_floor_divide floor_divide*/ 0, /* nb_inplace_true_divide 真除*/ }
[intobject.h] // 牺牲类型安全换取效率 #define PyInt_AS_LONG(op) (((PyIntObject *)(op)) -> ob_ival) [intobject.c] #define CONVERT_TO_LONG(obj, lng) \ if (PyInt_Check(obj)) { \ lng = PyInt_AS_LONG(obj); \ } \ else { Py_INCREF(Py_NotImplemented); \ return Py_NotImplemented; \ }好吧,其实我还看到了int类型的__doc__,这个坑爹的东西昨天坑我到一点多。 Python提供了3种途径用来创建PyIntObject对象
PyObject* PyInt_FromeLong(long ival) PyObject* PyInt_FromString(char *s, char **pend, int base) #ifdef Py_USING_UNICODE PyObject* PyInt_FromUnicode(Py_UNICODE *s, int length, int base) #endif
今天还看到一句很牛逼的话:在Python中,所有的对象都是存活在系统堆上的。对于什么是系统堆我现在还没概念,所以操作系统真的很重要。不然永远不可能绝对清晰。
由于整数对象十分常用,如果频繁得申请释放内存会导致Python性能低下,所以这里采用了整数池的方式来提高性能(我个人觉得这个设计很牛逼,有些事情一个人做不了,但是如果一群人能做得更好)。整数池又分为小整数池和大整数池,这两者的差别在于一个是全部缓存(小整数),一个是部分缓存(大整数)。
小整数池在Python虚拟机激活的时候就会创建,那个时候申请一块内存,将常用的小整数(默认5-257)放在里面,需要用的时候去里面拿,永远也不会销毁。
大整数池是需要用到小整数之外的整数时,去操作系统申请一块内存,用来存放创建的整数,并缓存在这里,下次用的时候来这里拿。如果内存不够了,继续向操作系统申请。这些内存抽象成一个单向链表,由block_list维护,其中的所有空闲内存由free_list维护(也是一条单向链表)。
#define BLOCK_SIZE 1000 #define BHEAD_SIZE 8 #define N_INTOBJECTS ((BLOCK_SIZE - BHEAD_SIZE) / sizeof(PyIntObject)) struct _intblock { struct _intblock *next; PyIntObject objects[N_INTOBJECTS]; }; typedef struct _intblock PyIntBlock; static PyIntBlock *block_list = NULL; static PyIntObject *free_list = NULL;
创建和删除整数
PyObject* PyInt_FromLong(long ival) { register PyIntObject *v; #if NSMALLNEGINTS + NSMALLPOSINTS > 0 if (-NSMALLNEGINTS <= ival="" &&="" <="" nsmallposints)="" {="" v="small_ints[ival" +="" nsmallnegints];="" py_incref(v);="" return="" (pyobject="" *)="" v;="" }="" #endif="" [2]:为通用整数对象池申请新的空间="" if="" (free_list="=" null)="" ((free_list="fill_free_list())" =="NULL)" null;="" [3]:(inline)内嵌pyobject_new的行为="" free_list="(PyIntObject" *)v="" -=""> ob_type; PyObject_INIT(v, &PyInt_Type); v -> ob_ival = ival; return (PyObject *)v; } =>
睡觉了,明天再写。祝贺今天张聪学长升职。
在Python中,一切皆对象。1989年,Guido在圣诞节正式发布了Python。Python是用C写的。所谓的对象在计算机底层其实只是一些内存的集合,这些内存可以是离散的也可以是连续的。为了编程方便,我们在写业务的时候往往不考虑底层的实现,直接把可以看作整体的内存抽象成对象。一般来说,对象是不能被静态初始化的,并且也不能在栈空间上生存。唯一的例外就是类型对象。Python中所有的内建类型对象都是被静态初始化的。
在Python中一个对象一旦被创建,它在内存中的大小就是不变的,也就是说,可变长数据都是通过指针来实现的。这样做可以确保Python对象不会印象内存周围的对象。
所有的Python对象的基类都是object。object在C源码中对应的就是PyObject。
[object.h] typedef stryct _object { PyObject_HEAD } PyObject; #define PyObject_HEAD \ _PyObject_HEAD_EXTRA \ int ob_refcnt; \ struct _typeobject *ob_type; 其实结构很简单 typedef struct _object { int ob_refcnt; struct _typeobject *ob_type; }这个结构体是Python对象机制的核心。ob_refcnt主要是为了实现垃圾回收机制。ob_type则指向一个类型对象,表明这个对象的类型。也就是说,Python中最简单的一个对象必须包含两个信息:引用计数和类型信息。因为所有的Python对象都继承自PyObject,所以每个对象都包含了PyObject_HEAD这个宏,而且这个宏中的内容会出现在每一个Python对象所占有的内存最开始的字节中。这样以后无论是什么Python对象我们都能用PyObject * 来引用了。
typedef struct { PyObject_HEAD long ob_ival; } PyIntObject;这是Python中整数对应的C结构。ob_ival表示整数值。对于字符串这样的变长类型。python中做了如下处理。
#define PyObject_VAR_HEAD \ PyObject_HEAD \ int ob_size; typedef struct { PyObject_VAR_HEAD } PyVarObject;PyVarObject表示Python中的变长类型,其中ob_size表示包含内部对象的数量。 想要创建一个对象,首先要做的就是给这个对象分配内存。至于分配多少内存,我们没有把这个值记录在对象中。因为每个类型需要的内存是固定的,所以我们将每个对象占用的内存大小存放在了类型对象(PyTypeObjec)中。
typedef struct _typeobject { PyObject_VAR_HEAD char *tp_name; /* 类型名称 For printing, in format ". " */ int tp_basicsize, tp_itemsize; /* 内存大小 for allocation*/ destructor tp_dealloc; /* 内存回收函数 */ printfunc tp_print; /* print调用的方法 */ ... hashfunc tp_hash; /* 计算哈希值 */ ternaryfunc tp_call; /* 不知道是干嘛用的,在后面的章节看到过。好像tp_call不为空,那么这个类型就是可调用的 */ ... } PyTypeObject;
有两种方法可以在Python中创建一个整数,一个是通过CAPI,一个是通过PyInt_Type对象来创建。CAPI是个很高大上的东西,从学Python到现在还从来没真正用过,真是惭愧惭愧呀,看来离Pythoner的距离还是相当遥远啊。这里给两个例子就跳过了。
但是CAPI只能创建内建对象,对于用户自定义的类型,则只能通过类型对象来创建。首先通过类型对象PyIntType找到tp_new这个属性,如果这个属性为NULL,则寻找基类(PyObject)的tp_new。tp_new方法会找到PyInt_Type中记录的tp_basicsize。从而完成内存申请。PyIntObject的tp_basicsize的值为sizeof(PyIntObject)。在完成内存申请之后会调用PyInt_Type中的tp_init,完成一些初始化工作。对应到C++,tp_new可以看作new关键字,tp_init可以看作是构造函数。
PythonTypeObject定义了很多函数指针,这些指针都是这个对象的一些方法。在这些操作方法中,有三组非常重要的操作族。在PyTypeObject中,它们是tp_as_number, tp_as_sequence, tp_as_mapping。他们分别指向PyNumberMethods, PySequenceMethods和PyMappingMethods函数。
typedef PyObject * (*binaryfunc)(PyObject *, PyObject *); typedef struct { binaryfunc nb_add; binaryfunc nb_syvtract; } PyNumberMethods;在object.h这个文件中定义了这三个族的操作都有哪些(不小心看到的)。当然只要重写这些操作对应的包装器就能使一个对象拥有多个族的操作。 关于类型的类型(PyType_Type)这个东西我想我还是比较熟悉的。
PyTypeObject PyType_Type = { PyObject_HEAD_INIT(&PyType_Type) 0, /* ob_size */ "type", /* tp_name */ sizeof(PyHeapTypeObject), /* tp_basicsize */ sizeof(PyMemberDef), /* tp_itemsize */ }type对象是普通类型对象的类型,也叫metaclass,当然你也可以自定义一个类型的metaclass。 在Python内部有一个十分常用的范型指针PyObject *。在Python内部各个函数之间传递的都是PyObject*,也正是由于PyObject*,Python实现了多态机制。 Python和C#一样,都是采用引用计数来实现垃圾回收机制的。不过有一个有意思的地方就是Python的类型对象是不遵守垃圾回收机制的,也就是说一个类型对象被创建,就永远不会析构。
#define _Py_NewReference(op) ((op)->ob_refcnt = 1) // 一个对象创建时,将这个对象初始化成引用为1 #define _Py_Deakkoc(op) ((*(op)->ob_type->tp_dealloc)((PyObject *)(op))) // 析钩函数 #define PyINCREF(op) ((op)->ob_refcnt++) // 增加引用计数 #define Py_DECREF(op) \ // 减少引用计数 if (--(op)->ob_refcnt != 0) \ ; else _Py_Dealloc((PyObject *)(op)) #define Py_XINCREF(op) if ((op) == NULL) ; else Py_INCREF(op) // 增加引用 #define Py_XDECREF(op) if ((op) == NULL) ; else Py_DECREF(op) // 减少引用
还有一点需要注意的是,这里析构函数不一定会通过free释放内存,大部分时候是将内存换给内存对象池。因为频繁地申请和释放内存会导致Python效率低下。
第一章的最后一部分是介绍Robert Chen将Python对象系统分成五大类。
好了今天就到这里吧,第一章讲的内容大部分也已经清晰了。这几天比较累,还是早点休息吧。
对了,端端说很不甘心,我也很不敢心,好好加油吧。
难得有个周末是休息两天的。趁着这两天有空,一口气吧Python虚拟机的那几章看完了,顺便还看了Python动态导入模块和Python运行时初始化的部分。现在就剩下线程和内存管理的部分没有看了。
马上就要看完《Python源码剖析》了,总算是明白Python是个神码东西了,不过并着绝对清晰的原则,感觉还是有大部分东西不明白,所以趁热打铁,我决定开始看第二遍。之前计划好了,十月是属于Python源码的月。
首先绝对要先感觉作者Robert Chen,这家伙的极客精神绝对值得我们大家学习。那些不明白的地方正是我的成长点所在。
Python源码大概分三个部分,第一部分是编译器部分,包含了词法分析和语法分析等。第二部分是一些重要的类库和核心模块。第三部分是Python解释器,也就是Python虚拟机,整个Python最核心的模块。
下面是一些Python源码中的目录以及简介。
Python的安装也比较方便,
还有修改Python源码的时候尽量使用Python提供的CAPI,不然有可能会出乱七八糟的错误。
《Python源码剖析》分三部分:
Python源码,通往Pythoner的必经之路。加油
def f():
print ‘hello’
Python函数在编译的时候会创建单独的PyCodeObject对象,但是有意思的是上面这段代码会分布在两个PyCodeObject,def f():这句话编译的字节码位于文件PyCodeObject中,这句话其实是创建了一个PyFuncObject对象,并将这个对象和f这个变量在global命名空间中创建约束,然后创建一个新的PyCodeObject,print ‘hello’的字节码就是放在这个PyCodeObject中。
Python虚拟机的运行原理和exe文件在x86架构的CPU中执行的原理类似。都是通过栈帧的方式来执行的(其实就是一个栈指针在一个栈中移来移去,然后不停地出栈入栈,不停创建栈帧),至于具体怎么运行的比较麻烦,我就不说的,自己看书去。简单来说栈帧和python中命名空间是对应的。一个栈帧对应一个命名空间,所以一个栈帧也对应一个PyCodeObject对象。在Python中栈帧的抽象叫做PyFrameObject。
[frameobject.h] typedef struct _frame { PyObject_VAR_HEAD struct _frame *f_back; // 执行环境链上的前一个frame PyCodeObject *f_code; // PyCodeObject对象 PyObject *f_builtins; // builtin命名空间 PyObject *f_globals; // global命名空间 PyObject *f_locals; // local命名空间 PyObject **f_valuestack; // 运行时栈的栈底位置 PyObject **f_stacktop; // 运行时栈的栈顶位置 ... int f_lasti; // 上一条字节码指令在f_code中的偏移位置 int f_lineno; // 当前字节码对应的源代码行 PyObject *f_localsplus[1]; // 动态内存。(局部变量 + cell对象集合 + free对象集合 + 运行时栈) } PyFrameObject;
书本上说这里的f_code存放的是一个待执行的PyCodeObject对象,而不是当前正在执行的PyCodeObject,我表示有点小怀疑,待会我去验证一下。f_builtins, f_globals, f_locals分别对应了三个PyDictObject,维护了builtin, global, 三个命名空间中name,以及local命名空间中name和value之间的对应关系。因为每个PyCodeBlock所需要的栈空间大小都是不一样的,所以PyFrameObject是一个可变长的对象,而f_localsplus所指向的就是PyCodeBlock所需要的栈空间和其他一些内存。实际上,运行时栈空间是f_valuestack和f_stacktop之间的空间,其他的空间是用来存放闭包变量的。Python标准库中的sys模块提供了对PyFrameObject的访问,具体的函数是sys._getframe()。
在Python中赋值语句会在local命名空间中创建约束(所谓的约束,就是变量名和变量名对应的值的对应关系)。赋值语句在Python中不单单指存在赋值符号的语句,还包括了def, class, import,for这样的语句。关于命名空间这个东西记住一点:Python中命名空间是静态的,而不是动态的。也就是说作用域仅仅由源程序的文本决定,而不是由运行时动态决定的(所以也叫词法作用域)。至于Python的LGB规则和LEGB规则我也不想多说了,只要按照最内嵌套作用域规则来推导就行了。关于Python的静态作用域,还有一个比较有意思的问题,之前我们老板面试我的时候问过我O(^_^)O
a = 1 def g(): print a def f(): print a a = 2 print a g() f()
当时我认为输出的结果是1 1 2。其实这段代码在运行f()第一个print a的时候就报错了,”local variable ‘a’ referenced before assignment”。简单的说,原因就是在编译成pyc文件的时候,f()中a的变量就已经确定是local变量了,所以在执行第一句print a的时候,发现a是未定义的,所以就报错了。通过dis查看编译生成的字节码就能看出两个print a的差异(g()中的print a和f()中的第一个print a的差异)。
g()中print a的字节码为 # 0 LOAD_GLOBAL 0 # 3 PRINT_ITEM # 4 PRINT_NEWLINE f()中第一句print a的字节码为 # 0 LOAD_FAST 0 # 3 PRINT_ITEM # 4 PRINT_NEWLINE
LOAD_GLOBAL的意思是从global空间中加载变量 而 LOAD_FAST是从local空间中加载变量。
在Python中Python解释器充当了CPU的角色,Python解释器中可能运行了很多进程,每个进程中可能有很多线程,而线程下面又有很多栈帧。这样一想整个Python虚拟机的框架就出来了。O(^_^)O