python第三天 程序的控制 函数和代码复用

程序的控制结构

程序的控制结构有: 顺序结构,分支结构,循环结构

单分支结构 :可以理解为 如果xx 就yyy

以简单的猜数字为例子
(if True 代表始终为真, if not True 代表始终为假)

guess = eval(input())
if guess == 99:
	print("Right!")

双分支

guess = eval(input())
if guess == 99:
	print("Right!")
eles:
	print("False")

双分支的紧凑形式格式:
<表达式1> if <条件> else< 表达式2> > :意思是如果条件成立,那么返回表达式1 ,否则,返回表达式2
表达式是语句的一部分,它不是语句

guess = eval(input())
print("猜{}了".format("对" if guess == 99 else "错"))

多分支结构

score = eval(input())
if score >= 90:
	grade = "A"
elif score >= 80 :
	grade = "B"
elif score >= 70:
	grade = "C"
elif score >= 60:
	grade = "D"
print("成绩等级为{}".format(grade))

注意分支的设计,如果是 60,70,80 ,90 这样判断,那么91分会直接判定为D等级

条件判断符:> ,<, =, >=, <=, ==, !=
逻辑判断 : and,or ,not (简单来讲就是与或非)。 and <=> && , or <=> || ,not <=> !

guess = eval(input())
if guess > 99 or guess < 99:
	print("Right")
else:
	print("False")

异常处理

num = eval(input())
#平方操作
print(num**2)

如果用户没有输入整数怎么办?所以我们要对异常进行处理。
在语句块1 中发生except中指定的异常时,执行语句块2,当然也可以不指定,只要发生异常就执行语句块2
try: 语句块1 except<异常类型> : 语句块2

所以我们对程序进行改造

try:
	num = eval(input())
	print(num**2)
except NameError:
	print("输入不是整数")

另一种异常处理
在这里插入图片描述
这个意思是,try中的语句块1,发生了异常,执行语句块2,不发生异常执行else 的语句块3,最后无论如何都要执行,finally的语句块4

练习:身体质量质数BMI

BMI计算:体重(kg)/ 身高的平方(m)

height,weight = eval(input("请输入身高(米)和体重(公斤)【逗号隔开】:"))
bmi = weight / (height**2)
print("BMI 的数值为:{:.2f}".format(bmi))
who,nat = "",""
if bmi < 18.5:
    who,nat = "偏瘦","偏瘦"
elif 18.5 <= bmi < 24:
    who ,nat = "正常","正常"
elif 24 <= bmi < 28:
    who,nat = "偏胖","偏胖"
elif 28 <= bmi:
    who,nat = "偏胖","肥胖"
print("BMI指标为:国内'{1}',国外'{0}'".format(who,nat))

只要设计多分支,一定要注意范围覆盖的问题!!!

循环

python提供了两种循环方式,遍历循环和无限循环
遍历循环,遍历某个结构形成的循环方式
for <循环变量> in <遍历结构>
<语句块>

常用:
计数循环

循环N次
for i in range(N)
	<语句块>
#以M开始一直到N的前一个数字,步长为K
#range(0,5,2) 那么结果就是 0 2,4 ,K可以省略,默认为1
for i in range(M,N,K):
	<语句块>

字符串遍历

#遍历s字符串
#for c in s:
#	<语句块>
for c in “python”:
	print(c,end=",")

遍历列表

#for item in ls:
#	<语句块>
for item in ["py",1,2,3,"thon"]
	print(item,end=",")

文件遍历

#文件遍历循环
# fi 是文件标识符
#line 文件的每一行
for line in fi
   <语句块>

无限循环

只要条件成立就执行表达式,执行之后再判断条件成立与否,不满足条件时,退出

while 条件:
	<表达式>

循环控制保留字

break,continue
break,跳出当前循环
continue,跳过当前循环,执行下次循环
在这里插入图片描述

循环扩展

for <循环变量> in <遍历结构> :
	<语句块1>
else : 
	<语句块2>


while <条件>:
	<语句块1>
else:
	<语句块2>

可以这么理解,else 就是正常完成循环的奖励!如果被break了,那么就不执行。

通过else 我们可以直接看出这个循环是否正常结束!
在这里插入图片描述

random库

random是使用随机数的Python的标准库
底层使用的是梅森选装算法生成的伪随机序列的元素。

基本随机函数: seed() , random

扩展随机数函数:randint() ,getrandits() ,uniform(),
randrange() , choice() , shuffle()

随机数种子,梅森选装算法根据随机数种子,产生随机数数序列。也就是说种子和随机序列是一对一的关系。
在这里插入图片描述
那么确定种子,就要使用seed(a = None) 函数,初始化给定的随机数种子,默认为当前系统时间。
random.seed(10) : 当前的随机值种子为10。
如果要再现随机过程,那么就设定一个随机数,如果不需要,那么就使用默认的即可。

扩展随机数

randint(a,b) : 生成一个【a,b】之间的随机整数

randrange(m,n,[k]) : 生成一个【m,n)以K为步长的随机整数。例如random.randrange(10,100,10) 产生【10,100)之间,以10为步长,取随机值。

getrandbits(k) : 生成一个k比特长的随机整数,例如 random.getrandbits(8) 就会生成0-255之间的整数

uniform(a,b) : 生成【a,b】之间的随机小数

choice(seq) : 从序列seq 中随机选取一个元素。例如 random.choice([1,2,3,4,5,6,7]) 就是在这个列表中随机选取一个值。

shuffle(seq) :将序列 seq 中元素随机排列,返回打乱后的序列。

圆周率的计算

1.近似公式计算
在这里插入图片描述
朴素方法计算

pi = 0
N = 1000
for k  in range(N):
    pi += 1/pow(16,k) * ( \
        4/(8*k+1) - 2/(8*k+4) \
        - 1/(8*k+5)-1/(8*k+6))
print("圆周率的值是:{}".format(pi))

2.工程学的撒点方式,蒙塔科罗方法
在这里插入图片描述

在正方形的内部随机撒点,当点的数量足够多,点的洒落方式足够随机,那么这个方式就是可取的。撒点之后,我们对点的位置进行观察,圆内部的点 / 园外的点, 就是圆周率

from random import random
import time
Darts = 1000 * 1000 # 撒一百万的点
hits = 0 # 命中点
start = time.perf_counter()
for i in range(1,Darts+1):  #随机撒100w个点从【1,100w+1】
    x, y = random(),random() #横纵坐标
    #计算到圆心的距离
    dist = pow(x**2+y**2,0.5)
    if dist <= 1.0:
        hits +=1
    #以上都只是计算了半个圆,所以也要乘以4
pi = 4 * (hits / Darts)
print("圆周率的值是:{}".format(pi))
print("运行时间是:{:.5f}s".format(time.perf_counter()-start))

课后练习:四位玫瑰树

在这里插入图片描述

for i in range(1000,10000):
    t = str(i)
    if pow(eval(t[0]),4)+pow(eval(t[1]),4)+pow(eval(t[2]),4)+pow(eval(t[3]),4) == i:
        print(i)

100以内素数和

在这里插入图片描述

def Isprime(n):
    for i in range(2,n):
        if n%i == 0:
            return False
    return True
    
res = 0;
for i in range(2,101):
    if Isprime(i):
        res += i 
print(res)

函数和代码复用

函数的定义和使用

函数可以在Java中对应方法,理解起来非常简单。

定义一个函数

def 函数名(参数1,参数2...参数n):
	函数体
	return 返回值

例如:计算 n!

def fact(n):
	res = 1;
	for i in range(1,n+1):
		res *= i
	return res

如果要执行函数,必须要调用,调用直接使用函数名即可;

可选参数

python的函数特性,可选参数传递,可选参数一定要放在必选参数的后面!!

def 函数名(<非可选参数>,<可选参数>:
	函数体
	return 返回值

例如计算 n! // m

#这里m =1 意味着,如果传递了第二个参数,
#那么就使用我们传递的,如果没有,那么就直接使用默认的m=1
def fact(n , m=1):
	s = 1
	for i in range(1,n+1):
		s*=i
	return s //m

可变参数

当我们不确定具体的参数有几个的时候,在Java中提供的是fun(int …) 代表传递 多个int 类型的参数
在Python 中,使用 *变量名 来解决可变的参数问题
例如 计算 n!*b

#格式
def  fact(n,*b):
		s = 1
	for i in range(1,n+1):
		s*=i
		#将接受的参数以列表的方式存储,所以我们需要遍历列表
	for item in b:
		s*=item
	return s
def fact

参数传递的另一种方式,通过名字来赋值
一般来说,给函数传递参数的时候,都是按照参数的位置传递的,同时我们也可以使用参数的名称

#这里m =1 意味着,如果传递了第二个参数,
#那么就使用我们传递的,如果没有,那么就直接使用默认的m=1
def fact(n , m=1):
	s = 1
	for i in range(1,n+1):
		s*=i
	return s //m

# 调用时,直接使用位置
fact(10,5)
#使用名称传递
fact(m = 5 , n = 10)

返回值

对于无返回值的函数来说,可以使用return 来结束函数,也可以不使用,
并且,python中,返回值可以是 1 个 ,也可以是多个!

还是之前的代码,计算 n! // m, 但是此时我们不仅返回 n! // m ,并且把参数也一并返回,返回的就是一个元组,元组是一种组合数据类型,与列表相似。

def fact (n , m =1):
	s = 1
	for i in range(1,n+1):	
		s*=i
		#用,隔开
	return s//m,n,m
fact(10,5)
#结果-》(724760,10,5)
#直接将对应的参数返回给 a,b,c
a,b,c = fact(10,5)

局部变量和全局遍量

全局变量就是程序所使用的参数,局部变量就是函数内部所使用的参数
使用规则1: 局部变量和全局变量是不同的变量,局部变量和全局变量可能重名,但是并不相同,函数调用完,局部变量就会被释放。

我们也可以用 global 关键字在函数内部使用全局变量

举个例子

#定义两个全局变量
n,s = 10,100
def fact(n):
	#函数内部使用了s变量,与全局变量s 完全不同
	s = 1
	for i in range(1,n+1):
		s* = i
	return s
#这里使用的s 是全局变量
print(fact(n),s)
#如果在函数内部要使用外部的全局变量,就使用global关键字
#定义两个全局变量
n,s = 10,100
def fact(n):
	#使用外部的全局变量
	global s
	for i in range(1,n+1):
		s* = i
	return s
#这里使用的s 是全局变量,但是已经被函数运算给修改了
print(fact(n),s)

规则2 : 局部变量为组合数据类型且没有被创建,它的名字还是一个全局变量,那么它就等同于全局变量

ls = ["F","f"]

def func(a):
	#这个ls 是列表类型,但是在函数内部并没有声明和初始化
	#但是调用的时候发现有一个全局变量就叫ls,所以这里的ls 就是在使用全局变量
	ls.append(a)
	return 

func("C")
print(ls)
#结果:["F","f","C"]
ls = ["F","f"]

def func(a):
	#初始化一个局部变量
	ls = []
	#这里使用的就是局部变量
	ls.append(a)
	return 

func("C")
print(ls)
#结果:["F","f"]

lambda 函数

python语言特性,函数式编程,在Java中使用的是一个接口,这接口只能有一个抽象方法。

lambda是一种匿名函数,即没有名字的函数
使用 lambda 关键字进行定义,函数名是返回结果

定义格式:

函数名 = lambda 参数 : 表达式
			||
			等价于
			||
		def 函数名 (参数):
			函数体
			return 返回值			

举个例子

f = lambda x,y : x + y
#意思是,有两个参数x和y,进行的运算是 x + y,把结果返回给 f 

#当然也可以没有参数
f = lambda:"lambda函数"
#当调用f() 时,就会返回 "lambda函数" 字符串
print(f())

对于lambda要谨慎使用,他是特殊使用函数的一种方式,对于特定的计算,使用没有任何问题,但是常规的函数定义还是要用def。

练习:七段数码管绘制

在这里插入图片描述
不同灯管的亮灭,就可以表示数字。
效果图:
在这里插入图片描述

思路,先绘制一个数字的对应的数码管
再绘制一串数字,绘制对应的数码管
再获得当前的系统时间,绘制对应的数码管

在这里插入图片描述

import turtle
#绘制一条线
def drawLine(draw):
    #如果传入的参数为真,那么就将画笔放下绘制
    #否则,就将画笔抬起,不在画板上绘制
    turtle.pendown()if draw else turtle.penup()
    #每一个线段都长40,最后让他右转90度
    turtle.fd(40)
    turtle.right(90)

# 绘制一个数字
def drawDigit(digit):
    #根据图片描述的线段,我们要判断第一个线段,那些数字需要用到,
    #2,3,4,5,6,8,9的显示都要用到,所以对传入的digit数字进行判断
    #如果是其中一个,那么就绘制这条线,否则就把画笔抬起,不绘制这条线
    drawLine(True) if digit in [2, 3, 4, 5, 6, 8, 9] else drawLine(False)
    drawLine(True) if digit in [0, 1, 3, 4, 5, 6, 7, 8, 9] else drawLine(False)
    drawLine(True) if digit in [0, 2, 3, 5, 6, 8, 9] else drawLine(False)
    drawLine(True) if digit in [0, 2, 6, 8] else drawLine(False)
    #第四条线绘制完,回到原点,此时要绘制第5条线,需要让他转向
    turtle.left(90)
    drawLine(True) if digit in [0, 4, 5, 6, 8] else drawLine(False)
    drawLine(True) if digit in [0, 2, 3, 5, 6, 7, 8, 9] else drawLine(False)
    drawLine(True) if digit in [0, 1, 2, 3, 4, 7, 8, 9] else drawLine(False)
    #最后将画笔的方向调整为标准的方向
    turtle.left(180)
    turtle.penup()
    #为后续的数字做准备,每一个数字间隔20像素
    turtle.fd(20)

#绘制日期
def drawDate(date):
    #遍历每一个字符所对应的数字,去绘制先
    for c in date:
        drawDigit(eval(c))

#方法入口
def main():
    #窗口大小为长800宽350,在屏幕的200,200像素点
    turtle.setup(800,350,200,200)
    turtle.penup()
    #先将海龟调整到窗口的最左边开始绘制
    turtle.fd(-300)
    #海龟的宽度为5
    turtle.pensize(5)
    drawDate("20181010")
    #让海归消失,
    turtle.hideturtle()
    turtle.done()

#调用 main 来绘制
main()

结果:
在这里插入图片描述

现在我们还差一步,直接给定系统时间去绘制。并且美化一下这个图。
我们然那个线和线之间有一些空白,我们可以写一个drawGap()函数

def drawGap():
    #把画笔抬起,不要留下痕迹
    turtle.penup()
    turtle.fd(5) 

在绘制线段的方法中fd之前和之后调用它,就可以做到间隔了。

在这里插入图片描述
好看一些了。

使用Time库获取系统时间,增加年月日的标记,让年月日的显示的颜色不同。
那么我们就需要改写一下drawDate()函数,我们将参数规定为: %Y-%m=%d+
意思是,遍历字符串时,获取日期的年2019这样的数字,我们就直接绘制,当遍历到 - 时,我们就绘制年, = 绘制月,+ 绘制天

全部代码:

#绘制一个小数点
def drawcomnt():
    turtle.penup()
    turtle.fd(20)
    turtle.right(90)
    turtle.fd(39)
    turtle.pendown()
    turtle.dot(1)
    turtle.penup()
    turtle.fd(-40)
    turtle.left(90)
    turtle.fd(20)

#绘制一条线
def drawGap():
    #把画笔抬起,不要留下痕迹
    turtle.penup()
    turtle.fd(5)

def drawLine(draw):
    drawGap()
    #如果传入的参数为真,那么就将画笔放下绘制
    #否则,就将画笔抬起,不在画板上绘制
    turtle.pendown()if draw else turtle.penup()
    #每一个线段都长40,最后让他右转90度
    turtle.fd(40)
    drawGap()
    turtle.right(90)

# 绘制一个数字
def drawDigit(digit):
    #根据图片描述的线段,我们要判断第一个线段,那些数字需要用到,
    #2,3,4,5,6,8,9的显示都要用到,所以对传入的digit数字进行判断
    #如果是其中一个,那么就绘制这条线,否则就把画笔抬起,不绘制这条线
    drawLine(True) if digit in [2, 3, 4, 5, 6, 8, 9] else drawLine(False)
    drawLine(True) if digit in [0, 1, 3, 4, 5, 6, 7, 8, 9] else drawLine(False)
    drawLine(True) if digit in [0, 2, 3, 5, 6, 8, 9] else drawLine(False)
    drawLine(True) if digit in [0, 2, 6, 8] else drawLine(False)
    #第四条线绘制完,回到原点,此时要绘制第5条线,需要让他转向
    turtle.left(90)
    drawLine(True) if digit in [0, 4, 5, 6, 8] else drawLine(False)
    drawLine(True) if digit in [0, 2, 3, 5, 6, 7, 8, 9] else drawLine(False)
    drawLine(True) if digit in [0, 1, 2, 3, 4, 7, 8, 9] else drawLine(False)
    #最后将画笔的方向调整为标准的方向
    turtle.left(180)
    turtle.penup()
    #为后续的数字做准备,每一个数字间隔20像素
    turtle.fd(20)

#绘制日期
def drawDate(date):
    turtle.color("red")
    #格式: %Y-%m=%d+
    for c in date:
        if c == "-":
            #使用write 绘制字符串,font设置字体和大小
            turtle.write('年',font=("Arial",20,"normal"))
            turtle.pencolor("green")
            #设置40像素的间隔
            turtle.fd(40)
        elif c == "=":
            turtle.write("月",font =("Arial",20,"normal"))
            turtle.pencolor("blue")
            turtle.fd(40)
        elif c == "+":
            turtle.write("日",font=("Arial",20,"normal"))
        else:
            drawDigit(eval(c))

#方法入口
def main():
	#将海龟隐藏
    turtle.hideturtle()
    # 修改绘图的速度
    turtle.speed(0)
    #窗口大小为长800宽350,在屏幕的200,200像素点
    turtle.setup(800,350,200,200)
    turtle.penup()
    #先将海龟调整到窗口的最左边开始绘制
    turtle.fd(-300)
    #海龟的宽度为5
    turtle.pensize(5)
    #使用gmtime时间结构体,并且使用strptime进行格式化时间字符串
    drawDate(time.strftime("%Y-%m=%d+",time.gmtime()))
    #让海龟消失,
    drawcomnt()

    turtle.done()

#调用 main 来绘制
main()


代码复用与函数递归

代码复用实际上就是一种面向对象的设计思想,就是代码的封装与调用,将功能封装为模块。模块内部设计要足够聚合,也就是功能越单一越好;模块与模块之间,连接的越少越好;这就是我们常说的高聚合低耦合
至于递归,我在之前的Java文章中,专门有递归的练习和专题,python中我们已经学会了函数的创建以及基础数据类型,那么学习递归是非常简单的。
递归以及练习

#简单的小例子,算n 的阶乘
def fact(n):
	if n == 0 : 
		return 1
	else :
		return n* fact(n-1)

经典问题,汉诺塔

#操作数
count = 0
# n 个盘子,src:源柱子, dst 目标柱子, mid 中间借助的柱子
def hanoi(n,src,dst,mid):
	if n == 1:
		print("{}:{}->{}".format(1,src,dst))
		count+=1
	else :
		#把n-1个盘子从src 移动到 mid 柱子上,借助dst柱子
	 	hanoi(n-1,src,mid,dst)
	 	print("{}:{}->{}".format(n,src,dst))
	 	count+=1
	 	#然后把n-1个盘子从mid柱子上移动到dst柱子上,借助src柱子
	 	hanoi(n-1,mid,dst,src)

PyInstaller 库的使用

我们在未来的某一天,一定会有这样的需求,将编写的源代码,转换为无需源代码的可执行文件。这样这个文件无论在哪一个平台,无论它到底装了Python环境与否,都可以运行。

所以我们就需要将源代码打包为一个可执行文件,所以就需要PyInstaller库来帮助我们,使用PyInstaller将.py文件转换为各个平台的可执行格式,例如windows 就是 .exe文件

PyInstaller 是第三方库,我们需要自己手动安装。
直接使用cmd 进行安装 ,
输入 pip install pyinstaller 即可

安装之后,使用命令行切换到要打包的 py文件目录下
使用命令 : pyinstaller -F <文件名>
之后我们再打开目录,就会多了三个文件夹

在这里插入图片描述
build 和 pychache 可以删除,在 dist 目录中可以看到一个可执行文件在这里插入图片描述
这个就是打包之后的文件

pyinstaller 常用的参数

在这里插入图片描述
推荐使用 -F
至于 - i 举个例子
(图标文件的名字为 xx.ico, py 文件名为 yy.py)
pyinstaller -i xx.ico -F yy.py
这样可以直接生成以 xx.ico 为图标 可执行文件

练习:科赫雪花小包裹

简单来说绘制一个雪花,将一个线段分为三等分,把中间这段去掉,替换为等边三角形的两条边。这就是一次变换,也叫一阶
在这里插入图片描述

然后我们再把这些等分的线段看成单独的一条线段,在进行划分。
在这里插入图片描述

这就是科赫曲线的实现过程。

先绘制科赫曲线:

import turtle
#size 线段长度,n为阶数
def koch(size,n):
    if n == 0:
        turtle.fd(size)
    else:
        #确定绘制的角度
        #先是平的,再是突起,再凹陷,再平,角度是固定的
        for angle in [0,60,-120,60]:
            turtle.left(angle)
            #绘制曲线为当前线段1/3 的 n-1阶曲线
            koch(size/3,n-1)

def main():
    turtle.setup(800,400)
    turtle.penup()
    turtle.goto(-300,-50)
    turtle.down()
    turtle.pensize(2)
    koch(600,3)
    turtle.hideturtle()
    turtle.done()

main()

在这里插入图片描述

koch 就是核心,既然会了科赫曲线的绘制,那么就改变main 来绘制科赫雪花

def main():
	turtle.setup(600,600)
	#把海龟调整到合适的位置
	turtle.penup()
	turtle.goto(-200,100)
	turtle.pendown()
	#设置画笔的宽度
	turtle.pensize(2)
	level = 3
	for i in range(3):
		koch(400,level)
		turtle.right(120)
	#理解不了循环
	#那么就可以想象一下一个雪花就是奇数条科赫曲线的连接
	#每一个科赫曲线绘制完成后,要保证多个科赫曲线连城环,所以要保证内角
	#所以说,我们也可以绘制5边的,那么内角就是120度,所以外角为60度,那么就right()
	turtle.hideturtle()
	turtle.done()

结果:
在这里插入图片描述

测验:
在这里插入图片描述

import random

def genpwd(length):
    if length == 0 :
        return 0
    Min = 10**(length-1)
    Max = (10 ** length) -1
    return random.randint(Min,Max)

length = eval(input())
random.seed(17)
for i in range(3):
    print(genpwd(length))

在这里插入图片描述

def prime(m):
    for i in range(2,m-1):
        if m % i == 0 : return False
    return True

n = eval(input())
res = ""
start = int(n)
start = start+1 if start < n else start
count = 5
while count > 0:
   if prime(start):
       res +=  str(start) + ","
       count-=1
   start+=1
print(res[:-1])

猜你喜欢

转载自blog.csdn.net/weixin_44916741/article/details/106303425
今日推荐