文章目录
一.函数简介
- 函数是可重用的程序代码块
- 函数也是对象
- 对代码实现了封装,并增加了函数调用*,传递参数,返回结果等内容*
二.Python函数分类
- 内置函数
str(),list(),len()等
- 标准库函数
通过import语句导入库
- 第三方库函数
下载安装后,通过import语句导入库
- 自定义函数
三.函数定义
- 定义语法:
def 函数名([参数列表]):
‘’'文档字符串‘’‘
函数体/若干语句 - 要点:
a. 执行def()语句时,会创建一个函数对象,并绑定到函数名变量上
b. 如果函数体不包含return语句,返回None值
c. 调用函数前,必须先定义函数,即调用def()创建函数 - 形参和实参
- 文字档字符串
a. 文档的注释,用三个单引号或双引号实现,中间可以有多行文字说明
注意:三个单引号也可以定义多行字符串
b. help(函数名.doc)可以输出函数文档字符串
- 返回值
a. return:结束函数执行并返回值
b. 不包含return:返回None值
c. 要返回多个值,使用元组,列表,字典,集合存起来即可 - 圆括号意味调用函数,没有圆括号时,python会把函数当作普通对象(可以赋值)
- 变量的作用域:
a. 全局变量:
- 定义位置直到模块结束
- 降低了通用性和可读性,尽量避免使用
- 函数要改全局变量,用global声明,否则视为局部变量
b. 局部变量:
- 引用比全局变量快,优先考虑使用
- 如果和全局变量重名,函数内只用局部变量
c. print(locals()):输出局部变量
print(globals()):输出全局变量
四.参数的传递
- 参数的传递都是引用传递,不是值传递,分为两类:
a. 对可变对象(字典,列表,集合,自定义)进行写操作,直接作用于原对象本身
b. 对不可变对象(数字,字符串,元组,函数)进行写操作,产生一个新的对象空间,用新值填充(当传递的不可变对象的子对象是可变对象时,函数修改这个可变对象,原对象也发生变化)
a=(10,20,[5,6])
print("a:",id(a))
def test06(m):
print("m:",id(m))
m[2][0]=888
print(m)
print("m:",id(m)) #m的地址一直没有发生改变,因为m始终指向a指向的元组
test06(a)
print(a) #a也发生了改变
五.浅拷贝与深拷贝
a. 浅拷贝:不拷贝子对象的内容,只拷贝子对象的引用
b. 深拷贝:将子对象的内存全部拷贝,对子对象的修改不会影响原对象
#浅拷贝和深拷贝
import copy #要导入copy
def testcopy():
''' 测试浅拷贝'''
a=[10,20,[5,6]]
b=copy.copy(a)
print("a:",a)
print("b:",b)
b.append(30)
b[2].append(7)
print("浅拷贝")
print("a:",a)
print("b:",b)
def testdeepcopy():
'''测试深拷贝'''
a=[10,20,[5,6]]
b=copy.deepcopy(a)
print("a:",a)
print("b:",b)
b.append(30)
b[2].append(7)
print("深拷贝")
print("a:",a)
print("b:",b)
testcopy()
testdeepcopy()
六.参数的几种类型
- 位置参数
- 默认值参数
调用时未赋值,按照定义时的值运算,若调用时赋值,则改用新值
- 命名参数
无所谓顺序,但注意不可重复赋值
- 可变参数(可放多个参数)
a.一个星号表示元组
b. 两个星号表示字典 - 强制命名参数
在可变参数后的参数,调用时必须命名
def test07(a,b,c,d):
print("a")
def test08(a,b,c=10,d=45):
print("b")
test07(1,2,3,4)
test08(2,3,4)
#test07(7,8,a=4,b=1) #报错,必须都赋值或者按顺序来
test07(7,8,c=4,d=1)
#可变参数
def test09(a,b,*c):
print(a,b,c)
def test10(*a,b,c):
print(a,b,c)
def test11(a,b,*c,**d):
print(a,b,c,d)
test09(1,2,3,4,5,6)
test10(1,2,3,b=4,c=5)
test11(1,2,3,4,5,name="gaogao",job="teacher",sex="female") #注意字典中的键不要用引号!!!
七.lambda表达式
- lambda函数是一种简单的,在同一行中定义函数的方法
- 生成一个函数对象
- 只允许包含一个表达式
- 基本语法:lambda arg1,arg2,arg3…:<表达式>
f=lambda a,b,c:a+b+c
print(f)
print(f(2,3,4))
g=[lambda a:a*2,lambda b:b+2,lambda c:c**2] #g是一个列表,里面每个元素是一个lambda函数生成的函数对象
print(g[0](6),g[1](3),g[2](8))
#测试evla函数
a=1
b=2
c=eval("a+b")
dict1=dict([("a",1),("b",2)]) #注意引号
dict2=dict(zip(["a","b"],[1,2]))
print(dict1)
c=eval("a+b",dict1) #按照字典中的对应键值赋值并计算(是逗号!!)
print(c)
八.eval()函数
- 将字符串str当成有效的表达式求值并返回计算结果
- 可以结合字典进行对应赋值运计算
a=1
b=2
c=eval("a+b")
dict1=dict([("a",1),("b",2)]) #注意引号
dict2=dict(zip(["a","b"],[1,2]))
print(dict1)
c=eval("a+b",dict1) #按照字典中的对应键值赋值并计算(是逗号!!)
print(c)
九.递归函数
- 必备条件:
a. 终止条件
b. 递归步骤(第n步与第n-1步关联) - 递归会创建大量函数对象(每次调用函数,都会创造一个栈帧),消耗内存与运算能力
- 递归是分层思想,但是在列计算式时,要考虑的是完整的关系式
#递归函数
#算阶乘
def factoria(n):
if n==1:
return 1
return n*factoria(n-1)
for x in range(1,5):
print(x,"!=",factoria(x)) #要用逗号隔开!!
十. 嵌套函数
- 子函数的定义和调用都在函数内部完成
- 作用:
a. 封装(数据隐藏):外部无法访问嵌套函数
b. DRY原则:避免代码重复(将操作细节放入代码内部,增加参数)
c. 闭包
#普通
def printChineseName(name,familyName):
print("{0} {1}".format(familyName,name))
def printEnglishName(name,familyName):
print("{0} {1}".format(name,familyName))
#使用嵌套函数,将细节隐藏在函数内部
def printName(isChinese,name,familyName): #多加了一个参数
def inner_print(a,b):
print("{0} {1}".format(a,b))
if isChinese:
inner_print(familyName,name)
else:
inner_print(name,familyName)
printName(True,"xiaoqi","Gao")
十一.nonlocal关键字
- nonlocal声明外部的局部变量;global声明全局变量
- 若不用nonlocal或global,可以读但不能写
- 若用nonlocal声明,在嵌套函数内,该变量不能出现在nonlocal前面
a=100
def outer():
b=10
def inner():
#print(b) #SyntaxError: name 'b' is used prior to nonlocal declaration(当有nonlocal时不可以在nonlocal前使用)
global a
nonlocal b
a=80
b=100
print("a,b is:",a,b)
inner()
outer()
LEGB规则
- local->enclosed->global->built-in
#测试LEGB搜索规则
#str="haha"
def outer():
#str="enclosed"
def inner():
#str="local"
print(str)
inner()
outer()