该笔记主要记录python函数相关内容,包括:
- 函数概念
- 函数参数和返回值
- 匿名函数、偏函数
- 闭包、生成器
以下为主要内容:
一、函数概念
函数,即将一段实现某功能的代码集中在一起,赋予特殊名字,再次使用该功能时,使用函数名字直接调用这个代码块。
函数作用:使代码模块化,逻辑清晰、代码重用;
函数分类:内建函数、自定义函数;
函数定义及参数:
- 单个参数:
def function(parament): {\n} body
。 - 不定长参数:
def function(*parament): {\n} body
,其中的(*参数)
表示函数参数是不定长的,函数体中可以直接以元组变量的方式使用该参数。 - 不定长参数:
def function(**parament): {\n} body
,其中的(**参数)
表示函数参数是不定长的,函数体中可以直接以字典变量的方式使用该参数。
示例:
# 示例1
def prin():
print("this is body")
#调用
prin() #this is body
#示例2
def test(num):
print(num**2)
# 调用
test(2) #4
# 示例3,计算两数之和
def mySum(num1,num2):
print(num1+num2)
mySum(2,3) # 5
不定长参数,函数调用示例:
# 方式一:元组,参数
def mySum(t):
result=0;
for v in t:
result+=v;
print(result)
mySum((2,3,4)) #参数是一个元组
# 方式二:不定长,参数
def mySum(*t): #表示接受不定长参数
result=0;
for v in t:
result+=v;
print(result)
mySum(2,3,4) #注意与方式一的区别
参数拆包
先看两个概念:
- 装包:把传递的参数,包装成一个集合,称之为“装包”;
- 拆包(解包):分解集合参数;
元组变量方式,拆包示例:
def mySum(a,b,c,d):
print(a+b+c+d)
def test(*args):
print(args) #(1 2 3) ,是集合
#拆包
print(*args) # 1 2 3 ,是个体
#调用函数
mySum(args) # error
mySum(args[0],args[1],args[2],args[3])# 等同于 mySum(1,2,3,4),输出10
#拆包方式:
mySum(*args) # 10
test(1,2,3,4)
字典参数方式,拆包示例:
def mySum(a,b):
print(a)
print(b)
def test(**keys):
print(keys) # {'a':1,'b':2}
mySum(**keys)# 1 2 等同于mySum(a=1,b=2)
test(a=1,b=2) # 与函数中的 a,b 必须对应
缺省参数
定义:def 函数名(变量1=默认值,变量2=默认值,...,):函数体
,默认值即缺省参数,调用函数的时候,如果没有参数传递过来,就使用该默认值。
关于参数的一点补充:
先看两个概念:
- 值传递:指传递过来的是一个数据的副本;
- 引用传递:指传递过来的是一个变量的地址,通过该地址,可以直接操作原始数据;
但是,在python
中,只有引用传递(地址传递),但不一定可以修改原始数据,代码验证:
# 如果id相同,则说明为引用传递
def change(num):
print(id(num))
b=10
print(id(b))
change(b)
```
输出:
1642...1168
1642...1168
id相同,说明变量num与变量b的内存地址是相同的。
```
# 虽然引用传递,但不一定能修改原始数据。
def change(num):
print(num)
print(id(num))
num=15 # 虽然引用传递,但不一定能修改原始数据。此处重新开辟空间。
print(num)
print(id(num))
b=10
change(b)
print(b) #10 原始数据没有被改变
函数返回值
看代码:
# 示例1
def muSum(a,b):
resutl=a+b
return result
res=mySum(5,6)
print(res)
# 示例2
def caculate(a,b):
div=a-b;
su=a+b
return (div,su)
res=caculate(5,10) #返回结果是元组,所以对元组进行拆包
print(res[0])
print(res[1])
#或者
print(div)
print(su)
Note:
return
之后的语句不会被执行;- 如果要返回多个数据,可以将数据包装成一个集合;
函数使用描述
在编写第三方函数的时候,描述函数功能和使用方式等信息。在函数体最上面添加三个双引号对注释。
一般,函数描述需要有如下信息:
- 函数功能;
- 参数:类型,是否缺省,默认值;
- 函数返回值
示例:
def caculate(a,b=1):
#会默认生成一些函数说明
"""
# 描述信息示例
param a: 数值1,数值,不可选,没有默认值
param b: 数值2,数值,可选,默认值:1
return : 返回计算结果,元组:(和,差)
"""
div=a-b;
su=a+b
return (div,su)
# 添加一个help
help(caculate) #不是函数调用,所以不需要写为help(caculate())
二、偏函数
写一个参数较多的函数时,有些是固定值,为了简化使用,可以创建一个新函数,指定要使用的函数的某个参数,这个新函数就是"偏函数"。
生成一个偏函数,需要调用模块:functools
;
示例:
import functools
def test(a,b,c,d=1):
print(a+b+c+d)
newFunc=functools.partial(test,c=5) #函数名,不要加()
newFunc(1,2) # 9 即1+2+5+1
偏函数示例
偏函数的一个使用场景:将二进制字符串转为十进制数。
代码:
#二进制字符串,转为十进制
numStr="10010"
result=int(numStr,base=2) # 转为2进制
print(result)
# 偏函数的一个使用场景:将二进制字符串转为十进制数。
import functools
int2=functools.partial(int,base=2) # 指定int函数,偏向base参数
print(int2(numStr))
四、高阶函数
当一个函数的某一个参数接收的是另一个函数的时,则把这个函数成为“高阶函数”。如排序。
示例:
l=[{"name":ss,"age":18},{"name":kk,"age":19},{"name":cc,"age":20}]
def getKey(x):
return x["age"]
result=sorted(l,key=getKey)
print(result)
三、返回函数
返回函数,指函数内部,它返回的是另外一个函数。常用于:根据不同的参数,进行不同的操作,做不同的计算。
看代码:
def getFun(flag):
# 根据不同的flag,返回不同的操作函数
def sum(a,b,c):
return a+b+c
def div(a,b,c):
return a-b-c
if(flag=="+")
return sum
elif flag=="-"
return div
result=getFun("+")
res=result(1,3,5) # 函数
print(res) # 9
四、匿名函数(lambda函数)
语法:lambda para1,para2,...,表达式
,需要注意的几点:
- 不能直接
return
,只能写一个表达式; - 表达式的结果就是返回值;
- 不建议过于复杂;
示例:
lambda x,y : x+y #本身就是函数
lambda x,y : x+y #后面加(parament)表示调用该函数
result=(lambda x,y : x+y)(1,2)
print(result) # 3
# 或者
newFunc=lambda x,y : x+y
print(newFunc(4,5))
lambda
表达式使用场景,以sorted
函数为例:
l=[{"name":ss,"age":18},{"name":kk,"age":19},{"name":cc,"age":20}]
result=sorted(l,key=lambda x:x["name"])
print(result)
五、闭包
闭包,指
- 在函数嵌套的前提下,
- 内层函数引用了外层函数的变量(该变量包括参数),
- 而外层函数又把内层函数当作返回值进行返回。
把内层函数和其引用的外层变量,称为闭包;
闭包示例1:
def test():
a=10
def test2():
print(a) # 内层函数引用了外部变量
return test2 # 将内层函数作为返回值
newFunc=test() # 是个函数了
newFunc() # 10
闭包示例2:
def line_config(content,length):
print("-"*(length // 2 ) + content + "-"*(length //2 )) # // 整除
line_config("line",20)
line_config("line",20)
line_config("line",20)
# 冗余
#使用闭包
def line_config(content,length):
def line():
print("-"*(length // 2 ) + content + "-"*(length //2 )) # // 整除
return line
line1=line_config("line",20) # 函数
# 调用函数
line1()
line1()
line1()
Notes:
-
闭包中,如果要修改引用的外层变量,需要使用
nonlocal
声明,否则会被当作新定义的变量;看示例1; -
闭包中引用了一个可能会发生变化的变量时,要注意。看示例2;
示例1:
def test():
num=10
print(num)
def test2():
# num=66 # 新变量
# 要修改num,需要:
nonlocal num
num=66
print(num)
return test2
print(num)
result=test()
result()
'''
10
66
10
'''
示例2
def test():
a=1
def test2()
print(a)
a=2
return test2
newF=test()
nuwf() # 问:是打印a=1,还是a=2 ?
# 2
#解释:当函数被调用的时候,才会真正的确定,对应变量的值,在此之前,都是以变量标识名称而存在。
在函数闭包中,返回多个函数,可以使用列表,看示例3:
def test():
func=[] # 列表,存放返回的多个函数
for i in range(1,4):
def test2():
print(i)
func.append(test2)
return func
newFuncs=test()
newFuncs() # 3 个 函数,而且地址各不同
newFuncs[0]()
newFuncs[1]()
newFuncs[2]() # 调用函数
'''
输出为:
3
3
3
解释在下一行
'''
解释:函数调用输出值都为3,原因同示例2:当函数被调用的时候,才会真正的确定,对应变量的值(这个示例里面,i
值为3),在此之前,都是以变量标识名称而存在的。
可以使用参数作为函数内部变量,而不是闭包内调用外部参数,看示例4:
def test():
func=[] # 列表,存放返回的多个函数
for i in range(1,4):
def test2(num):
def inner():
print(num)
return inner
func.append(test2(i))
return func
newFuncs=test()
newFuncs[0]()
newFuncs[1]()
newFuncs[2]() # 调用函数
'''输出
1
2
3
'''
六、装饰器(设计模式之一)
先看示例:
#装饰器格式:
def checkLogin(func):
def inner():
print("登录验证")
func() # 使装饰器可变,至关重要。
return inner
# 2个功能性函数
@checkLogin #装饰器
def fss():
print("说说")
@checkLogin # 等价于 ftp=checkLogin(ftp) ,先执行右边
def ftp():
print("图片")
'''
装饰器语句等价于:
def ftp():
print("图片")
ftp=checkLogin(ftp)
'''
# 逻辑代码
btIndex=1
if(btIndex==1):
fss()
else:
ftp()
# 输出
'''
登录验证
图片
'''
Note:
- 装饰器是直接执行;
- 装饰器叠加时,从上到下装饰,但执行是从下往上执行;
对有参函数装饰:
# 定长参数
def orna(func):
def inner(n1,n2):
print("*"*30)
func(n1,n2) # 使装饰器可变,至关重要。
return inner
@orna
def pnum(n1,n2): # 则装饰器中,也要有参数。
print(n1,n2)
pnum(123,234)
# 不定长参数
def orna(func):
def inner(*args,**keys):
print("*"*30)
print(args,keys)
func(*args,**keys) # 使装饰器可变,至关重要。
return inner
@orna
def pnum(n1,n2,n3): # 则装饰器中,也要有参数。 即保持一致
print(n1,n2,n3)
pnum(123,234,n3=66)
'''
******
(123,234) {"n3":66}
'''
七、生成器
生成器,即一个特殊的迭代器,其拥有迭代器的特性:
- 惰性计算数据,省内存;
- 能够记录状态,并通过
next()
函数,访问下一个状态; - 具有可迭代性;
创建方式:
- 生成器表达式:列表推导式
[]
修改为()
,看示例1; - 生成器函数:函数中必须包含
yield
语句,该函数执行结果就是生成器;yeild
可以阻断当前函数执行,当使用next()
函数时,会继续向下执行,直到下一个yeild
,看示例2
示例1:
l=[i for i in range(1,10000) if i%2==0]
print(l)
# [2,4,...]
# 不需要一次性生成所有数据,所以借助于生成器:
l=(i for i in range(1,10000) if i%2==0)
print(l)
# <generator object...>
# 生成器是特殊的迭代器,所以具有状态记录的特性
print(next(l))
# 或
for i in l:
print(i)
示例2:
def test():
yeild 1 # 1 为状态值
print("a")
yeild 2
print("b")
g=test()
print(g)
#<generator object...>
# 访问
print(next(g)) # 1
print(next(g)) # 1 a 2
def test():
for i in range(1,9):
yeild i
g=test()
print(g)
'''
输出
1
2
3
4
5
'''
生成器的几个方法:
send()
方法:
send
方法有一个参数,指定的是上一次被挂起的yeild
语句的返回值;若用于第一次启动函数时使用,则可以传值None
。- 相比于
__next()__
,它可以额外的给yeild
语句传值; - 第一次调用:
send(None)
看代码:
def test():
print("***")
res1=yeild 1
print(res1)
res2=yeils 2
print(res2)
g=test()
print(g.__next()__()) # 开始执行函数,在 res1=yeild 1 挂起
print(g.__next()__())
'''
***
1
None # print(res1) ,但没有得到res1的值
2
'''
# 看seed 方法:
def test():
print("***")
res1=yeild 1 # = "xxx"
print(res1)
res2=yeils 2
print(res2)
g=test()
print(g.send(None))
print(g.send("xxx"))
'''
***
1
xxx
2
'''
生成器关闭:close()
方法:
def test():
yeild 1
print("a")
yeild 2
print("b")
g=test()
print(g.__next()__())
print(g.__next()__())
g.close() #关闭生成器
补充
递归函数示例:
def jiecheng(n):
if n==1:
return 1
else:
return n*jiecheng(n-1)
result=jiecheng(5)
print(result)
# 120
函数作用域
几个概念:
-
变量的作用域:
python
是静态作用域,即变量的作用域源于它在代码中的位置,在不同的位置,可能会有不同的命名空间; -
命名空间:作用域的体现形式,是一个具体的操作范围;
-
局部变量:是在一个函数内部定义的变量,作用域为函数内部。查看方式:
locals()
; -
全局变量:是在一个函数外部(或文件最外部定义的变量),作用域为整个文件;
未完,待续