5.1模块化程序设计
5.1.1.基本思想:将一个大型程序按照功能分割成若干个小型模块
5.1.2特点
- 模块相对独立,功能单一,结构清晰,接口简单(内聚,耦合)
- 减少程序复杂性
- 提高元器件的可靠性
- 避免程序开发的重复劳动
5.2.定义函数
5.2.1.格式:
def 函数名(形参列表)
内部功能代码
return 表达式
5.2.2.函数名:函数名区分其他函数作用,本质是该函数在内存所占空间的首地址,是一个常量,命令要求见名知意
5.2.3.形式参数:
作用:函数接收数据的空间
原则:调用函数时,形参遍历会分配空间,调用结束后释放空间
设计:形参名称,个数,多个参数使用逗号分隔
5.2.4.函数体:编写程序实现函数功能
def summer(lis):
'''这里写函数的说明文档.doc的位置
:param:lis:参数列表说明
:return:返回值说明
'''
t=0
for i in lis:
t+=1
return t
5.3.函数调用
5.3.1.作用:使用函数功能,传递参数
5.3.2格式:函数名(实参列表)
5.3.3.调用方式
fun #调用fun函数,将实参m传递给形参
u=fun(m) #调用fun函数,返回值值赋值给n变量
s=s*fun(m)#调用fun函数,返回值参与后续运算
fun() #无返回值,无参,只是执行一次
5.3.4例1:计算C(m,n)=m!/(n!*(m-n)!)
def fac(x):
facx=1
for i in range(1,x+1):
facx*=i
return facx
m=int(input('请输入m的值'))
n=int(input('请输入n的值'))
C=fac(m)/(fac(n)*fac(m-n))
print('结果:',C)
5.4.return语句
5.4.1.作用:返回return后的对象,函数执行到此结束,若无返回值,省略return语句,会返回None
5.4.2.注意:一旦函数执行过程中遇到return语句,之后的函数体代码都不会执行,会跳出函数体
def fac(x):
pass
return #此时,后面的代码都不会执行
pass
5.4.3.return可以返回任意python的对象
5.5函数参数
5.5.1.位置参数:必须参数,必备参数,必须按照正确顺序传到函数中,实参和形参安危做置对齐,个数相同
def fun(str1,str2):
print(str1,str2)
fun('hello','world')
def fun(str1,str2):
print(str1,str2)
fun(str2='world',str1='hello')
def fun(str1,str2='world'):
print(str1,str2)
fun('hello')
默认参数必须写在形参列表的最右边,否则报错
默认参数尽量指向不变对象,面试题!
def func(a=[]):
a.append('A')
return a
print(func()) #1
print(func()) #2
print(func()) #3
答案
['A']
['A', 'A']
['A', 'A', 'A']
而不是
['A']
['A']
['A']
分析:函数体装入内存后,a列表会被创建,内存中有a列表内容,
def func(a=[]):
print('函数内部a的地址: %s' % id(a))
a.append('A')
return a
b = print(func())
print('b的地址:%s' % id(b))
print(b)
c = print(func())
print('c的地址:%s' % id(c))
print(c)
d = print(func())
print('c的地址:%s' % id(d))
print(d)
需要a不变则:使用不变的类型作为默认值,修改为:
5.5.4.不定长参数(可变参数):传入的参数可以使任意多个
格式:*形参,增加一个星号,可以接收多个参数并存储到元组中
例:
def func(str1,*str2):
print(str1,str2)
func('hello','world','china','12345')
格式2:**形参,增加2个星号,以关键字参数形传递,以字典形式存储
def func(str1,**str2):
print(str1,str2)
func('hello',a='world',b='china',c='12345')
注意:若形参列表中只有单独的xi
def func(str1, *,str2):
print(str1, str2)
func('hello', str2='world')
5.6变量的作用域
5.6.1作用域:作用域指变量的有效范围,决定访问权限
5.6.2.编程语言中,变量作用域从代码结构形式来说,分成:块级,类,模块,包(由小到大)python没有块级等同于普通语句
5.6.3.python的作用域共分四层
层 | 作用 |
L(local) | 局部 |
E(enclosing) | 闭包函数外的函数中 |
G(global) | 全局 |
B(built-in) | 内建 |
x=int(2.9) #内建作用域
globa_var=0 #全局作用域
def outer():
out_ver=1 #闭包函数外的函数中
def inner():
inner_var=2 #作用域
5.6.4.Python查找规则:L->E->G->B的顺序查找变量,即在局部找不到变量则回到局部外的局部去找,再找不到则到全局取去找,最后到内建去找变量,若还找不到则报错
5.6.5全局变量和局部变量
定义在函数内部的变量称为局部变量,定义在函数外的变量,一般拥有全局的作用域,称为全局变量
a=1 #全局变量
def func():
b=2 #局部变量
print(a)
def inner():
c=3 #更局部变量
print(a)
print(b)
print(c)
func()
- global和nonlocal关键字
total=1 #全局变量
def plus(arg1,arg2):
total=arg1+arg2
print('函数内局部变量total=',total)
print('函数内局部变量total的地址:',id(total))
return total
plus(10,20)
print('函数内局部变量total=', total)
print('函数内局部变量total的地址:', id(total))
global:指定当前变量使用外部的全局变量
total=1 #全局变量
def plus(arg1,arg2):
total=arg1+arg2
print('函数内局部变量total=',total)
print('函数内局部变量total的地址:',id(total))
return total
plus(10,20)
print('函数内局部变量total=', total)
print('函数内局部变量total的地址:', id(total))
面试真题:
a=10
def test():
a+=1
print(a)
test()
#执行会报错,a+=1相当于a=a+1,python规定如果函数内部需要修改一个变量,那么该变量必须为背部变量,除非使用global声明
应修改为
a=10
def test():
global a
a+=1
print(a)
test()
5.7函数递归调用
5.7.1作用:一个函数在它的函数内部调用自身称为递归调用
5.7.2例:递归算法计算n!
5.7.3.注意:
- 每一层递归,整体问题的值都要比原来要小,并且递归到一定层次后必须给出结果
- 为了防止递归无休止调用,必须在函数内部有终止递归的手段,,一般配合if-else语句
- 递归需要注意防止递归深度溢出,在Python中使用栈这种数据结构实现的,默认的深度为1000层,超出该深度会抛出异常,每当进入下一个递归,栈会增加一层,当函数每返回一次,栈会减少一层
- 递归可以使用程序变得简洁, 增加程序的可读性,每一次递归都要重新开辟内存空间,这是以牺牲空间为代价的算法,所有递归会增加程序执行的事件开效
5.8匿名函数
5.8.1.当创建函数时有时不需要显示的定义函数,直接省略函数名传入参数计算即可,省略函数的命名,通过水平步发送表达式实现函数
5.8.2.使用lambda关键字创建匿名函数
- 所谓匿名即不再使用def关键字来创建函数
- lambda只是一个表达式,不是一个代码块,函数体比def简单
- lambda仅仅封装有限的逻辑语句
5.8.3.格式:lambda参数:表达式
lambda x:x*x
#相当于
def fun(x):
return x*x
- lambda是匿名函数关键字
- lambda之后的x为函数参数,相当于原先的形参
- x*x为执行代码
5.8.4.匿名函数只能有一个表达式,有自己的命名空间,不用再写return语句,表达式的结果就是其返回值
5.9迭代器
5.9.1迭代:通过for循环遍历对象的每一个元素过程
5.9.2.迭代器:是一种可以被遍历的对象,可以作用于next()函数,迭代器对象从第一个元素开始向后进行访问,直至最后一个元素,只能向后遍历,不能向前回溯,与列表最大的区别就是列表遍历方向任意
5.9.3.迭代器的常用方法:iter()和next()
list1 = [1, 2, 3, 4, 5, 6, 7, 8]
lt = iter(list1) # 创建迭代器对象lt
for i in lt:
print(i, end=' ')
5.9.4.迭代器(iterator)与可迭代(iterable)的区别
- 凡是可作用for循环的对象都是可迭代类型
- 凡是可作用于next()函数的对象都是迭代器类型
- list,dictc,str等都是可迭代的但不是迭代器,因为么next()函数无法调用它们
- for循环本质上是通过调用next()函数实现下一个访问的
5.10.生成器
5.10.1.产生原因:由于序列或集合的元素个数非常巨大,如果全部生成制作再一次性装入内存会对计算机造成非常大的存储压力,如果元素结果可以按照某些算法推算出来,需要计算哪一个就生成哪一个,不必完整的创建元素集合,从而节省大量内存空间,所所以在Python中一边循环一边计算的机制称为生成器(generator)
5.10.2.在Python中使用yield可以返回函数,使其变成一个生成器
5.10.3.运行机制:调用函数生成器过程中,每次遇见yield时,函数会暂停执行,并保存当前所有的运行信息,向主调函数返回结果并在下一次执行next()方法时从当前位置继续执行
# 斐波拉契函数
def fib(n):
a, b, c = 0, 1, 0
while 1:
if c > n:
return
yield a
a, b = b, a + b
c = c + 1
f1 = fib(10)
for i in f1:
print(i, end=' ')
print(type(f1))
结果
0 1 1 2 3 5 8 13 21 34 55 <class 'generator'>
5.11.装饰器
5.11.1,先从一个函数开始:
def hello():
print('hello world')
hello()
5.11.2.上例定义了一个hello函数,现在需要增加一个功能:
# 第一种
def hello():
print('===start===')
print('hello world')
print('=== end ===')
hello()
或者
#第二种
def hello():
print('hello world')
print('===start===')
hello()
print('=== end ===')
问题:
第一种方法会改变函数的执行代码
第二章如果多次调用,每一次都需要增加开始和结束的特性,将会非常消耗机器性能,较为玛法
则能不能在不改变函数内部原始代码和参数调用方式等信息,又想给该函数增加新功能
可以使用装饰器,装饰该函数,再不改变函数的情况下,增加公民能功能
5.11.3.函数的高阶使用
- 定义一个函数
def add(a,b):
return a +b
- 调用:
def add(a,b):
return a +b
print(add(2,3))
- add函数是有返回值的,若输出add不加括号:
def add(a,b):
return a +b
print(add(2,3))
print(add)
print(type(add))
结论:一个函数名加上括号就会被执行,函数也和整数,字符串,浮点数一样都是一个对象,基于上述理论:
a=print
a('hello world')
#解释:把printf赋值给a,a('hello world')等价于printf('hello world')
继续改进:
def outut():
return print
outut()('hello world')
#解释:任何返回值都可以再调用时直接代替这个函数
#output()('hello world')中output()会被替换为printf等价于
print('hello world')
继续改进:
def outut():
print('hello world')
def act(func):
func
act(outut)
#解释:定义一个output函数可以输出hello world
#但没有直接调用,而是写了一个act函数,act函数的参数是一个函数名,作为就是调用act函数时会间接调用output()函数从而输出hello world
- 结论:一个函数名称作为有个函数的参数传递给另一个函数时,(实参高阶函数),返回值中包含函数名(返回值高阶函数),这里说的函数名本质为函数首地址把函数名当作一个参数传递给另一个函数,然后再给另一个函数内部做一些操作,则可以实现不修改源代码的情况下从而更改函数
def deco(func):
def inner():
print('===start===')
func()
print('=== end ===')
return inner
def hello():
print('hello world')
hello=deco(hello)
hello()
- 函数deco即hello会被装入内存,等待被执行
- 执行hello=deco(hello),抵用deco函数,将hello函数名作为实参传递过来
- 执行deco(func)函数,其中func等价于hello函数,inner函数装入内存等待内执行,最后返回inner函数名并到hello=deco(hello)函数调用处,hello会被inner覆盖,此时hello就是inner
- 执行inner函数输出修饰语句,输出func()函数结果,由于func指向最早的hello函数则输出hello world
- 结论:本段代码本质上修改了调用函数,但表面上未修改调用方式,实现了附加功能,通俗一点来说就是把函数当作一个盒子,deco是大盒子,inner是中盒子,hello是小盒子,程序中将小盒子hello传递给大盒子deco中的中盒子inner,再把中盒子inner执行一次,这就是装饰器
5.11.5.装饰器:
- 器:值得的工具,可以定义成函数
- 装饰:指的是为其它事物添加额外点缀
- 装饰器:定义一个函数,该函数时用来为其它函数添加额外功能的
- 应用场景:应用于切面操作的场景,如:插入日志,性能测试,事务处理,存储,权限效验等
- 语法糖:装饰器的语法简化写法,如:
def deco(func):
def inner():
print('===start===')
func()
print('=== end ===')
return inner
@deco #等价于hello=deco(dello)
def hello():
print('hello world')
hello()
#@deco 写在被修饰的def hello() 函数之前