28 函数
(一)函数定义
函数时刻可重复用的程序代码块,更能实现代码一致性
- 内置函数:直接使用
- 标准库函数:用import调用
- 第三方库函数:下载安装后,用import导入
- 用户自定义函数
def 函数名(参数列表):
#函数说明
函数体
例子:
def add(a,b,c):
'''完成三个数的加法,并返回它们的和'''
sum = a+b+c
print("{0}、{1}、{2}三个数的和是:{3}".format(a,b,c,sum))
return sum
add(10,20,30)
函数的定义和调用:
1.使用def来定义函数,创建一个函数对象,并绑定到函数名变量上
2.形参列表不用声明类型,实参要与形参一一对应
3.return返回值会结束函数并返回值,没有return会返回none
4.调用函数之前要先定义函数,先调用def创建函数对象
(二)返回值
为了方便函数参与二次运算,我们让函数体内不输出任何结果,而是把函数本身就当做一种结果,输出这种结果的方式就可以理解为返回函数的结果,python用return关键词来返回。
如果要返回多个值,将其全放在列表里面返回
def printShape(n):
s1 = "#"*n
s2 = "$"*n
return[s1,s2]
s = printShape(5)
print(s)
(三)函数内存底层分析
1.函数后面的圆括号意味着调用函数,没有圆括号的情况,Python会把函数当成普通对象
2.将一个函数赋值给一个变量,本质上是两者指向同一个函数对象
(四)变量
1.变量的作用域
全局变量:在函数和类定义之外声明的变量。作用域为定义的模块。在函数内改变全局变量的值,要使用global声明。
局部变量:在函数体内声明的变量。局部变量的引用较快应当优先考虑使用。如果与全局变量同名,要在函数内隐藏全局变量,局部变量引用更快
a = 100
def f1():
b = 3
global a
a = 4
print(a+b) #7 没加global这里相当于a又是局部变量,只是在函数内改变其值
f1()
print (a) #没加global:函数外在引用a时,a的值没有改变还是100;加上global后,a的值改变为4
输出局部变量locals(),输出全局变量globals()
(五)参数
参数传递本质是从实参到形参的赋值操作。
1.对可变对象进行“写”操作,就是修改原对象(字典,列表,集合,自定义对象)
b = [10,20]
def f1(m):
m.append(30)
print("m:",id(m),m)
f1(b)
print("b:",id(b))
print(b)
'''m: 1826844954880 [10, 20, 30]
b: 1826844954880
[10, 20, 30]'''
2.对不可变对象进行“写"操作,会产生一个新的对象空间,用新值填充(数字,字符串,元组,function)
传递进来是b对象的地址,由于b是不可变对象,因此创建新的对象m,m变成了新的对象
b = 10
def f1(m):
m+=2
print("m:",id(m),m)
f1(b)
print("b:",id(b))
print(b)
'''m: 140724757121816 12
b: 140724757121752
10'''
3.浅拷贝copy()和深拷贝deepcopy()
浅拷贝:拷贝对象,不拷贝子对象的内容,只拷贝子对象的引用
深拷贝:是所有子对象全部拷贝
浅拷贝例子:b拷贝了a的子对象【5,6】的引用,因此a,b共用这一个子对象,改变子对象,a也会随之改变,但改变b原对象是复制的a的不是同一个,所以a的原对象不会改变
深拷贝后,b把a的子对象也拷贝了,a和b没有任何共享的子对象,再改变b对a就没有任何影响了
4. 参数传递不可变对象包含可变子对象
修改原不可变对象内的可变子对象,则原来的不可变对象也会发生一定改变
5.参数的类型
1>位置参数
函数调用时,实参默认按位置顺序传递,需要个数和形参匹配。按位置传递的参数叫位置参数
2>默认值参数
为参数设置默认值,在传递时就是可选的,要是有位置参数会先选择位置参数
def f1(a,b,c=10,d=20):
print(a,b,c,d)
f1(8,9)
#8 9 10 20
3>命名参数
按照形参的名字传递参数,位置不重要
def f1(a,b,c,d):
print(a,b,c,d)
f1(c=10,a=20,b=50,d=100)
#20 50 10 100
4>可变参数
可变数量的参数,一般都要放在最后面.
*param,将多个参数收集到一个“元组”内
def f1(a,b,*c):
print(a,b,c)
f1(1,2,3,4,5,6)
#1 2 (3, 4, 5, 6)
**param,将多个参数收集到一个“字典”对象内
def f1(a,b,**c):
print(a,b,c)
#f1(1,2,3,4)会发生报错,没有传递字典的形式,默认将2,3,4给了b
f1(1,2,m=1,n=2,q=3)
#1 2 {'m': 1, 'n': 2, 'q': 3}
5>强制命名参数
针对可变参数放在前面的情况,强制给之后的形参传递参数
def f1(*a,b,c):
print(a,b,c)
f1(1,2,3,4,5,6,b=10,c =39)
#(1, 2, 3, 4, 5, 6) 10 39
(六)lambda表达式和匿名函数
lambda表达式用来声明匿名函数。是在同一行中定义函数的方法
lambda表达式只允许包含一个表达式,不能包含复杂句,表达式的计算结果就是函数的返回值
lambda arg1,arg2,arg3...: <表达式>(arg是参数)
g = [lambda a:a*2,lambda b:b*3,lambda c:c*4]
print(g[0](6),g[1](7),g[2](4)) #g[0]g[1]g[2]分别是三个不同的函数对象
print(id(g[0]))#1608517965664
print(id(g[1]))#1608517964384
print(id(g[2]))#1608518380864
(七)eval函数
把字符串里的内容当做有效的表达式执行,并返回结果
a = 10
b = 20
c = eval("a+b")
print(c) #30
eval函数要慎用,因为假如字符串里含有个del,也会被执行,会有安全隐患
(八)递归函数
递归的基本思想就是自己调自己,递归函数必须包括终止条件
def f(n):
print("start:"+str(n))
if n==1:
print("recrusion over!")
else:
f(n-1)
print("end:"+str(n))
f(3)
'''先传递参数3,print一下start,然后执行else,调用f(2),再传递参数2,print一下start,然后执行else,调用f(1),再传递参数1,
print一下start,执行if的recrusion over,跳出循环后,print一下end1,f(2)中包含的调用f(1)执行完了,该执行f(2)的最后一句print一下end2,
f(3)中包含的调用f(2)执行完了,该执行f(3)的最后一句print一下end3,
start:3
start:2
start:1
recrusion over!
end:1
end:2
end:3'''
递归函数练习:计算阶乘
def f(n):
#计算阶乘的函数
if n==1:
return 1
else:
return n*f(n-1)
print(f(5)) #120
(九)嵌套函数
函数中定义函数
def outer():
print("outer running!")
def inner():
print("inner running")
inner() #内部函数只能在外部函数内部进行调用,无法在外部进行调用
outer()
1>可以避免在函数内部重复代码
2>封装数据,隐藏,外部无法访问“嵌套函数”