执行def定义函数之后,系统创建了函数对象,函数变量存储了函数对象的地址,赋值操作a=函数变量 传递的是对象的地址。通过 函数变量() 实现函数的调用。
1、全局变量的作用域是整个模块,在函数和类定义之外声明,局部变量的作用域是函数内部,在函数体内声明。函数调用的时候会在栈里面创建栈帧,里面包含局部变量,函数中修改全局变量时,通过global 全局变量才可修改(直接使用时不需要,如果局部变量与全局变量同名,优先使用局部变量),否则使用的是局部变量(若不存在则报错),当函数运行完栈帧会被删除。
print(locals())##打印局部变量
print(globals())##打印全局变量
局部变量的效率比全局变量的效率高,优先使用,在大量循环或对效率有要求的地方可以将全局变量转为局部变量。
import math
def test01():
for i in range(1000000):
math.sqrt(i)###math为全局变量
def test02():
b=math.sqrt
for i in range(1000000):
###b为局部变量
b(i)
##test01比test02效率更高
test01()
test02()
2、参数的传递是将实参赋值给形参,形参和实参都存储同一个对象的地址
对可变对象进行写操作时(对形参操作修改),直接作用于原对象。函数运行时在栈帧创建形参并引用对象,函数运行完栈帧删除。
传递不可变对象还是传递对象的地址,因为对象不可变,在函数内部修改对象时需要创建一个新的对象。
3、浅拷贝:b=copy.copy(a),只对第一层的对象进行拷贝,引用的子对象还是一样的,即只拷贝子对象的地址
深拷贝:b=copy.deepcopy(a),对对象、子对象都进行拷贝,对子对象的修改不会影响源对象
例子:
import copy
###浅拷贝
a=[1,2,[5,6]]
b=copy.copy(a)
b.append(30)
b[2].append(7)
print(a) ##输出[1, 2, [5, 6, 7]]
print(b) ##输出[1, 2, [5, 6, 7], 30]
###深拷贝
a=[1,2,[5,6]]
b=copy.deepcopy(a)
b.append(30)
b[2].append(7)
print(a) ##输出[1, 2, [5, 6]]
print(b) ##输出[1, 2, [5, 6, 7], 30]
4、传递不可变对象时,不可变指的是第一层对象,子对象可以是可变的
a=(1,2,[5,6])
def f(m):
m[1]=22##报错
m[2][0]=888
print(m)
f(a)##输出(1,2,[888,6])
print(a)##输出(1,2,[888,6])
5、位置参数:实参默认按位置顺序传入参数,传入个数需与形参个数一致
默认参数:给形参设置默认值,默认参数需在普通参数后面
命名参数:通过形参名称匹配参数
可变参数:*形参名 将多个参数收集到一个元组对象
**形参名 将多个参数收集到一个字典对象
可变参数后面还有其他参数时,在调用函数的时候强制使用命名参数
def f1(a,b,\*c):
print(a,b,c)
f1(8,9,10,11)##输出8 9 (10, 11)
def f2(a,b,\**c):
print(a,b,c)
f2(8,9,m=10,n=11)##输出8 9 {'m': 10, 'n': 11}
6、lambda表达式: a=lambda 形参列表: 表达式 ,定义的是一个匿名函数对象,通过a()调用函数,表达式只能含一个,不能含复杂的语句,函数的计算结果为返回值,可以构建以函数为元素的列表。
7、eval(str,globals):传入字符串表示的表达式或函数,执行并返回结果。globals可选参数,必须是一个字典,定义表达式的上下文(如参数不同取值)
8、递归:函数自己调用自己,
def t1(n):
print('t1',n)###递归调用之后,先调用的部分先执行
if n==0 :
print('over')
else : t1(n-1)
print('t1**',n)###递归调用之后,后调用的部分先执行,后调用的先结束
t1(4)
#输出
t1 4
t1 3
t1 2
t1 1
t1 0
over
t1** 0
t1** 1
t1** 2
t1** 3
t1** 4
递归输出阶乘
def f(n):
if n==1:
return 1
else:
return n*f(n-1)
a=f(5)
print(a)#120