目录
- [一 Python简介](#一 Python简介)
-
- [1.1 Python定义](#1.1 Python定义)
- [1.2 Python发展史](#1.2 Python发展史)
- [1.3 Python编译和运行过程](#1.3 Python编译和运行过程)
- [1.4 Python的应用](#1.4 Python的应用)
- [1.5 练习题](#1.5 练习题)
- [二 程序设计思想](#二 程序设计思想)
一 Python简介
1.1 Python定义
Python 是一种简单易学并且结合了解释性、编译性、互动性和面向对象的脚本语言。Python提供了高级数据结构,它的语法和动态类型以及解释性使它成为广大开发者的首选编程语言。
1.2 Python发展史
Python的创始人为Guido van Rossum。1989年,Guido为了打发圣诞节的无趣,决心开发一个新的脚本解释程序,做为ABC 语言的一种继承。
Python 是由其他语言发展而来的,其中主要包括 ABC、Modula-3、C、C++、SmallTalk、Unix shell 等。
1.3 Python编译和运行过程
虽然Python源代码文件(.py)可以直接使用Python命令运行,但实际上Python并不是直接解释Python源代码,它是具有一个编译和运行的过程,具体过程如下图:

首先将Python源代码(.py文件)编译生成Python字节码(Python Byte Code,字节码文件的扩展名一般是.pyc),然后再由Python虚拟机(Python Virtual Machine,简称PVM)来执行Python字节码,最后在终端输出运行结果。
通过以上编译和运行过程可分析:Python是一种解释型语言,指的是解释Python字节码,而不是Python源代码。这种机制的基本思想与Java和.NET是一致的。
Python解释器 (Interpreter)是把Python语言翻译成计算机 CPU 能听懂的机器指令。
Python虚拟机:
python并不将py文件编译为机器码来运行,而是由python虚拟机一条条地将py语句解释运行,python虚拟机的执行方式就是模仿普通x86可执行文件运行方式。
虚拟机输入为字节码.pyc文件,由字节码文件加载器将二进制的.pyc文件加载到内存,由执行引擎解释执行,输出为字节码文件的执行结果。
Python字节码(.pyc):
Python中的字节码(bytecode) 是一种数据类型, Python代码的编译结果就是bytecode对象。bytecode对象可以由虚拟机加载后直接运行,而pyc文件就是bytecode在硬盘上的保存形式。
1.4 Python的应用
Python的应用主要有以下领域:
-
Linux/UNIX运维:提供API(Application Programming
Interface应用程序编程接口),能方便进行系统维护和管理。
-
GUI程序开发(PyQt、Kivy等)
-
Web程序开发(Django、Flask等框架):支持最新的XML技术。
-
移动App开发(PyQt、Kivy等):Python的PyOpenGL模块封装了"OpenGL应用程序编程接口",能进行二维和三维图像处理。PyGame模块可用于编写游戏软件。
-
网络爬虫(为搜索引擎、深度学习等领域提供数据源)
-
网络编程(基于Socket等协议):提供丰富的模块支持sockets编程,能方便快速地
-
开发分布式应用程序。很多大规模软件开发计划例如Zope,Mnet
及BitTorrent. Google都在广泛地使用它。
-
图形处理:有PIL、Tkinter等图形库支持,能方便进行图形处理。
-
文本处理:python提供的re模块能支持正则表达式,还提供SGML,XML分析模块,许多程序员利用python进行XML程序的开发。
-
数据库编程:可通过遵循Python DB-API(数据库应用程序编程接口)规范的模块与Microsoft SQLServer,Oracle,Sybase,DB2,Mysql、SQLite等数据库通信。python自带有一个Gadfly模块,提供了一个完整的SQL环境。
-
数据科学:NumPy扩展提供大量与许多标准数学库的接口。机器学习(scikit-learn、TensorFlow框架)、数据统计分析和可视化(Matplotlib、seaborn框架)。
bash
python应用举例:
Google - 谷歌在很多项目中用python作为网络应用的后端,如Google Groups、Gmail、Google Maps等
NASA - 美国宇航局,从1994年起把python作为主要开发语言
豆瓣网 - 图书、唱片、电影等文化产品的资料数据库网站
Torchlight --Python编写的大型3D游戏,原Blizzard公司人员制作发行,开源
Blender - 以C与Python开发的开源3D绘图软件 在科学研究中也得到广泛的应用
1.5 练习题
bash
if __name__ == '__main__':
str = "Hello,World!"
print(str)
if __name__ == '__main__':
print("Hello," + "World!")
if __name__ == '__main__':
str1 = "Hello,"
str2 = "World!"
print('str1' + 'str2')
if __name__ == '__main__':
str1 = "Hello,"
str2 = "World!"
print(str1+str2)
bash
运行结果:
Hello,World!
Hello,World!
str1str2
Hello,World!
二 程序设计思想
Python是一种面向对象oop(Object Oriented Programming)的脚本语言。
面向对象是采用基于对象(实体)的概念建立模型,模拟客观世界分析、设计、实现软件的办法。
在面向对象程序设计中,对象包含两个含义,其中一个是数据,另外一个是动作。面向对象的方法把数据和方法组合成一个整体,然后对其进行系统建模。
python编程思想的核心 就是理解功能逻辑。
2.1 基本的程序设计模式
任何的程序设计都包含IPO。
bash
I:Input 输入,程序的输入
P:Process 处理,程序的主要逻辑过程
O:Output 输出,程序的输出
若通过计算机实现某个功能,那么基本的程序设计模式包含三个部分,如下:
bash
确定IPO:明确需要实现功能的输入和输出,以及主要的实现逻辑过程;
编写程序:将计算求解的逻辑过程通过编程语言进行设计展示;
调试程序:对编写的程序按照逻辑过程进行调试,确保程序按照正确逻辑正确运行。
2.2 解决复杂问题的有效方法:自顶向下(设计)
(1) 自顶向下-分而治之
如果要实现功能的逻辑比较复杂的时候,就需要对其进行模块化设计,将复杂问题进行分解,转化为多个简单问题,其中简单问题又可以继续分解为更加简单的问题,直到功能逻辑可以通过模块程序设计实现,这也是程序设计的自顶向下特点。
具体如下:
1、将一个总问题表达为若干个小问题组成的形式
2、使用同样方法进一步分解小问题
3、直至,小问题可以用计算机简单明了的解决
理解自顶向下的设计思维 :分而治之
(2) 步骤
程序总体框架
程序设计
测试结果
(3) 示例 斐波那契数列
自顶向下的方式其实就是使用递归来求解子问题,最终解只需要调用递归式,子问题逐步往下层递归的求解。
bash
#递归1(带记忆的递归)
cache = {}
def fib(number):
if number in cache:
return cache[number]
if number == 0 or number == 1:
return 1
else:
cache[number] = fib(number - 1) + fib(number - 2)
return cache[number]
if __name__ == '__main__':
print(fib(35))##这行代码只有在直接运行 domo.py 时才会执行
#普通的递归2
def fibonacci_recursive(n):
if n == 1 or n==0:
return 1
else:
return fibonacci_recursive(n-1) + fibonacci_recursive(n-2)
# 测试递归方法
n = 35
print(f"斐波那契数列的第 {n} 项是: {fibonacci_recursive(n)}")
解析:
这段代码定义了一个名为 fib 的函数,它用于计算斐波那契数列中第 number 项的值。这个函数使用了记忆化(memoization)技术来提高效率,通过一个名为 cache 的字典来存储已经计算过的斐波那契数,避免重复计算。
1、记忆化字典:
bash
cache = {}:定义一个空字典,用于存储已经计算过的斐波那契数。
2、斐波那契函数:
bash
def fib(number):定义一个函数,接受一个参数 number,表示斐波那契数列中的项数。
bash
if number in cache:检查 number 是否已经在 cache 字典中。如果是,直接返回该值,避免重复计算。
bash
if number == 0 or number == 1:斐波那契数列的前两项都是1。
bash
else:对于其他项,递归计算 fib(number - 1) 和 fib(number - 2),并将结果相加,存储在 cache[number] 中。
bash
return cache[number]:返回计算并存储在缓存中的值。
3、主程序:
bash
if __name__ == '__main__'::确保以下代码只在脚本被直接运行时执行,而不是被导入时执行。
bash
print(fib(35)):调用 fib 函数计算第35项的斐波那契数,并打印结果。
代码执行
当你直接运行这个脚本时,if __name__ == '__main__': 之后的代码块将被执行,计算并打印斐波那契数列的第35项。由于使用了记忆化技术,这个函数的计算效率比传统的递归方法高得多,因为它避免了重复计算相同的斐波那契数。
2.3 逐步组建复杂系统的有效测试方法:自底向上(执行)
这种方法是动态规划中常用的技术,它通过将问题分解为更小的子问题,然后逐步构建解决方案来解决整个问题。
自底向上(执行)就是一种逐步组建复杂系统的有效测试方法。首先将需要解决的问题分为各个三元进行测试,接着按照自顶向下相反的路径进行操作,然后对各个单元进行逐步组装,直至系统各部分以组装的思路都经过测试和验证。
理解自底向上的执行思维 :模块化集成
自底向上分析思想:
任何时候栈中符号串和剩余符号串组成一个句型,当句柄出现在栈顶符号串中时,就用该句柄进行归约,这样一直归约到输入串只剩结束符、栈中符号只剩下开始符号,此时认为输入符号串是文法的句子,否则报错。
自底向上是⼀种求解动态规划问题的方法,它不使用递归式,而是直接使用循环来计算所有可能的结果,往上层逐渐累加子问题的解。在求解子问题的最优解的同时,也相当于是在求解整个问题的最优解。其中最难的部分是找到求解最终问题的递归关系式,或者说状态转移方程。
(1)自底向上的动态规划(模块化集成)的特点:
1、子问题解决:
bash
动态规划首先解决最小的子问题,并将这些子问题的解存储起来(通常是在表格或字典中)。
2、状态转移:
bash
利用已知的子问题的解来解决更大的子问题,这个过程称为状态转移。
3、避免重复计算:
bash
通过存储已解决的子问题,动态规划避免了重复计算相同的子问题,这是它高效的关键。
4、最终解构建:
bash
从解决最小的子问题开始,逐步构建出整个问题的解。
(2)示例
问题:
你现在想买⼀大堆算法书,有一个容量为 V 的背包,这个商店⼀共有 n 个商品。问题在于,你最多只能拿 W kg 的东西,其中 wi 和 vi 分别表示第 i 个商品的重量和价值。最终的目标就是在能拿的下的情况下,获得最大价值,求解哪些物品可以放进背包。
对于每⼀个商品你有两个选择:拿或者不拿。
自底向上分析
⾸先要做的就是要找到"子问题"是什么。通过分析发现:每次背包新装进⼀个物品就可以把剩余的承重能力作为⼀个新的背包来求解,⼀直递推到承重为0的背包问题。
用 m[i,w] 表示偷到商品的总价值,其中 i 表示⼀共多少个商品,w 表示总重量,所以求解 m[i,w]就是子问题,那么看到某⼀个商品i的时候,如何决定是不是要装进背包,需要考虑以下:
bash
1、该物品的重量大于背包的总重量,不考虑,换下⼀个商品;
2、该商品的重量小于背包的总重量,那么尝试把它装进去,如果装不下就把其他东西换出来,看看装进去后的总价值是不是更高了,否则还是按照之前的装法;
3、极端情况,所有的物品都装不下或者背包的承重能力为0,那么总价值都是0;
由以上的分析,可以得出m[i,w]的状态转移方程为:
bash
m[i,w] = max{m[i-1,w], m[i-1,w-wi]+vi}
程序设计
bash
# 循环的⽅式,自底向上求解
cache = {}
items = range(1,9)
weights = [10,1,5,9,10,7,3,12,5]
values = [10,20,30,15,40,6,9,12,18]
# 最⼤承重能⼒
W = 4
def knapsack():
for w in range(W+1):
cache[get_key(0,w)] = 0
for i in items:
cache[get_key(i,0)] = 0
for w in range(W+1):
if w >= weights[i]:
if cache[get_key(i-1,w-weights[i])] + values[i] > cache[get_key(i-1,w)]:
cache[get_key(i,w)] = values[i] + cache[get_key(i-1,w-weights[i])]
else:
cache[get_key(i,w)] = cache[get_key(i-1,w)]
else:
cache[get_key(i,w)] = cache[get_key(i-1,w)]
return cache[get_key(8,W)]
def get_key(i,w):
return str(i)+','+str(w)
if __name__ == '__main__':
# 背包把所有东西都能装进去做假设开始
print(knapsack())
程序解析及思路
这是一个经典的动态规划问题------0/1 背包问题。这个问题的目标是在不超过背包最大承重的前提下,选择物品以最大化背包中物品的价值总和。
bash
初始化缓存:
cache = {}:定义一个空字典,用于存储每个子问题的解,避免重复计算。
物品和重量:
items = range(1,9):表示有8个物品,编号从1到8。
weights = [10,1,5,9,10,7,3,12,5]:每个物品的重量。
values = [10,20,30,15,40,6,9,12,18]:每个物品的价值。
最大承重:
W = 4:背包的最大承重能力。
动态规划求解函数:
def knapsack():定义一个函数,用于计算背包问题的最大价值。
for w in range(W+1):初始化第一行为0,表示没有物品时的价值。
for i in items:遍历每个物品。
cache[get_key(i,0)] = 0:初始化第一列为0,表示背包承重为0时的价值。
for w in range(W+1):遍历每个可能的承重。
if w >= weights[i]:如果当前承重大于等于物品i的重量,考虑是否将物品i放入背包。
if cache[get_key(i-1,w-weights[i])] + values[i] > cache[get_key(i-1,w)]:比较不放入物品i和放入物品i的价值,选择较大的一个。
return cache[get_key(8,W)]:返回背包问题的最大价值。
生成缓存键函数:
def get_key(i,w):定义一个函数,用于生成缓存键,表示物品i和承重w的状态。
主程序:
if __name__ == '__main__'::确保以下代码只在脚本被直接运行时执行,而不是被导入时执行。
print(knapsack()):调用 knapsack 函数并打印最大价值。
knapsack 函数就是使用自底向上的方法来解决0/1背包问题的。
bash
初始化:
使用 cache 字典来存储每个子问题的解,初始时,考虑没有物品或承重为0的情况。
bash
状态转移:
通过比较将当前物品 i 放入背包和不放入背包两种情况下的价值,
选择价值更大的一种作为当前状态的解。
bash
构建最终解:
从第一个物品到第八个物品,从0承重到最大承重,逐步构建出整个问题的解。
bash
返回结果:
最终,cache[get_key(7, W)] 提供了在最大承重 W 下能够获得的最大价值。
这种方法的优点是时间和空间效率都比较高,因为它避免了递归方法中的重复计算,并且只需要存储与当前决策相关的信息。在实际应用中,这种方法被广泛用于解决各种优化问题,包括背包问题、最短路径问题、最长公共子序列问题等。