python程序设计⼼得体会感想-从Python学习中得到的⼀点感
我们经常性的使⽤语⾔,⽐如被我们忽略的⼈类语⾔。语⾔作为沟通的⼯具,它的功⽤再清楚不过了,不⼀样的是,程序设计语⾔是⼈和计算机之间沟通的⼯具。可以说,冯诺依曼计算机的五⼤部件决定了计算机的⽂化和程序语⾔的基本结构。Python虚拟机实现了虚拟栈区,所以才叫虚拟机,Python中在虚拟机中执⾏的代码叫字节码,正如x86栈中执⾏的代码--汇编。不论如何,我们需要明⽩其运⾏的基本原理,这样理解Python的源码就⽐较容易了。⽐如Python中出现的类,要知道C语⾔是不⽀持类和对象,但Python的出发点就是⼀切皆对象,原因是C语⾔从语法上实现了Python中类和对象的概念。 python函数学习感想
程序设计语⾔
我们经常性的使⽤语⾔,⽐如被我们忽略的⼈类语⾔(中⽂、英⽂等)。语⾔作为沟通的⼯具,它的功⽤再清楚不过了,不⼀样的是,程序设计语⾔是⼈和计算机之间沟通的⼯具。
任何语⾔的诞⽣都需要⽂化,这其中包括计算机。计算机最初提出来的原因是为了解决可计算性,⽽我们现在普遍使⽤的计算机构造,就是冯诺依曼计算机。
冯诺依曼计算机有五⼤部件:控制器、存储器、运算器、输⼊、输出
可以说,冯诺依曼计算机的五⼤部件决定了计算机的⽂化和程序语⾔的基本结构。计算机最底层的运⾏完全依靠⾼低电平,⽽⾼低电平决定了计算机的⼆进制体系,计算机的存储器存放了数据和指令,⽽控制器和运算器合作完成指令所表明的具体任务。那么数据和指令就需要在存储器和运算器之间频繁被传输。如下图所⽰:
如此看来,计算机指令最频繁的操作就是:将数据从存储器运输到运算器(load)和将数据从运算器运输回存储器(store),这也是汇编语⾔中最为常见的指令。所以,以冯诺依曼计算机结构设计的程序语⾔必然有频繁的赋值操作,⽽频繁赋值操作就会使得存储器(内存)中的变量变得难以维护。所以,我们需要给变量设置⼀定的访问权限(作⽤域),以免变量被污染。但是,这只是⼀定程度上解决了副作⽤的问题。
我们现在设想⼀下,假如让⼀段程序在计算机中运⾏,我们需要为它做什么。⾸先,我们需要将该段可执⾏的代码(指令)加载到内存空间中,即代码区,并指出指令开始的地⽅在哪⾥;同时,我们需要为程序运⾏设置⼀个环境,⽤来维护程序运⾏过程中每⼀时刻变量的变化,这就是最为经典的x86运⾏时栈结构。因为栈结构对变量的⼤⼩要求特别严格,所以,我们需要为某些不知道变量⼤⼩或者我们可以⾃由控制变量空间的变量分配内存,这就是堆。当然,我们还需要维护⼀些程序运⾏过程中的全局/静态变量。于是,我们便可以得到程序在内存中执⾏过程时的分配情况,如下图:
代码区我们不需要操⼼,会有⼀条指针(寄存器PC(Programming count))指向当前指令,每执⾏完⼀条指令PC⾃动加⼀。两个数据区也不需要我们操⼼,在程序执⾏前,它们已经进来了。
堆区是供程序员进⾏动态分配使⽤的内存区,具体使⽤可以参看《操作系统》中内存管理的章节。堆区是⽤链表进⾏维护的,如图,假设我们需要申请2M⼤⼩的内存,其实其真正分配给我们的内存要⼤于2M,⽽前⾯的部分我们叫做元信息,其中存放了关于这块内存的信息(⼤⼩),以及链表指针。
⽽对于程序员来说,最为操⼼的部分,应该是栈区,因为我们所见的程序,最后都是在栈区中运⾏的。Python虚拟机实现了虚拟栈区,所以才叫虚拟机,Python中在虚拟机中执⾏的代码叫字节码(bytecode),正如x86栈中执⾏的代码--汇编。
栈--在数据结构中定义为⼀种后进先出的线性结构。我们看⼀下C语⾔程序是如何在栈中执⾏的。为了讲述⽅便,我们写⼀段简单的C语⾔代码:
int addOne(int a) { int m = 0; //加⼀个局部变量 m = a+1; return m; } int main(void) { i = 10; addOne(i); return 0; }
C语⾔程序执⾏的⼊⼝是main()函数。所以,当这段可执⾏代码被加载到内存中的时候,毫⽆疑问,寄存器PC指向了main()函数的起始位置。在程序开始执⾏之前,栈为空,所以,栈顶指针和栈底指针重合。我们从 i=10 开始执⾏:在栈中分配⼀个整型⼤⼩的空间,栈顶指针:SP = SP+4 :
接下来执⾏函数: addOne(i) :如果要执⾏函数就要知道函数在栈空间中分配多⼤的内存,所以,C语⾔函数需要在使⽤函数之前进⾏声明(定义可以放在使⽤之后,如果上段代码,我将addOne()函数的定义放在main()函数之后,就会报错)。
函数声明的作⽤就是告诉栈空间:我需要多⼤的内存执⾏该函数。
⼀个函数的栈空间分为三部分:参数列表区、局部变量区、保存指针区
参数列表区:函数定义中的参数列表;
保存指针区:当函数执⾏完以后,需要知道下⼀条指令的位置
局部变量区:函数定义中的局部变量;
所以, addOne(i) 执⾏的栈空间为:
由此可以看出:
函数在参数列表区赋值了i的值,所以,函数的执⾏不会改变原来i的值,即传值⽅式为值传递;
参数列表是倒序压⼊栈中的,为的是访问⽅便和实现可变参数列表;
保存指针区保留了下⼀条指令的位置;
局部变量区和参数列表区是分开存放的;
当函数执⾏完成后,三部分都被清空,PC拿到保存的下⼀条指令地址,继续向下执⾏,⽽SP指针则回到i的位置
既然我们知道了函数在栈中的分布情况,我们是不是可以更改其中的保存指针呢?如下代码,通过数组下标越界来访问到PC,同时将PC的值减去4(⼀条指令的长度),即回退到函数执⾏的位置,此程序将陷⼊死循环。(如果我们把第五⾏和第四⾏的代码互换,⼜会出现什么情况呢?)
#include int foo() { int a[5]; int i; for(i=0;i<6;i++) { a[i] = a[i]-4; printf("loop"); } } int main(void) { foo(); }
当然关于x86运⾏时栈,我们还有很多需要学习的地⽅。不论如何,我们需要明⽩其运⾏的基本原理,这样理解Python的源码就⽐较容易了。
编程范式的问题
在编程设计语⾔发展的过程中,出现了很多⽅法学以及由此产⽣的新概念。但,我把所有的这些新概念都归结为语法、语义层⾯的表述。⽐如Python中出现的类,要知道C语⾔是不⽀持类和对象,但Python的出发点就是⼀切皆对象,原因是C语⾔从语法上实现了Python中类和对象的概念。
我们在学习语⾔的时候,经常遇到关于编程范式的问题。那么,最为常见的编程范式我认为有三类:
⾯向过程
⾯向对象
函数式编程
⾯向过程和⾯向对象同出⼀⽀,都是从冯诺依曼体系中层层抽象上来的。在解决可计算性问题的时候,除了冯诺依曼提出的⽅案,还有⼀个⼈提出了⼀种⽅案:lambda演算,它应该就是函数式编程的起源。函数式编程最后还要依靠传统的语⾔来解释。所以,我想函数式编程也是语义层次的⼀种抽象⽅式。编程先学c语言还是python
在⾯向对象中出现了很多新的概念,⽐如元类、基类、继承、多态等等,但我想这些都是语义层次的概念。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。