函数
函数基础
函数定义
def 函数名(参数1,参数2,...)
"""文档描述"""
函数体
return 值
定义函数时发生的事情:
- 申请内存空间保存函数体代码
- 将内存地址保存到函数名
- 不会执行函数体代码,但会检测函数体语法
函数的三种形式:
- 无参函数
- 有参函数
- 空函数
函数体代码为 pass 或 ...
常用于构思软件
函数的三种返回形式
- 无返回或者返回None
- 返回一个值
- 返回多个值。此时,解释器会自动处理为tuple 元组的形式
形参与实参
形参:在定义函数阶段定义的参数称为形式参会素,简称形参
实参:在调用函数阶段传入的值称为实际参数,相当于变量值
在调用阶段,实参会绑定给形参
- 位置参数
按照从左到右的顺序依次定义的参数称之为位置参数,
位置形参:定义函数时,从左到右顺序定义的'变量名',调用时必须相应数量传值。
位置实参: 从左到右顺序传入 的值。
关键字参数
关键字实参: 函数调用时,按照 key=value 的形式传入的值,不需要参考形参的顺序。
关键字实参与位置实参混合使用:
- 位置实参必须放在关键字实参前
- 不能为同一个形参重复传值
默认参数
默认形参:在定义函数阶段,就已经被赋值的形参称为默认形参。调用时,可以不赋值。
位置形参与默认形参混用:
- 位置形参必须在默认形参的左边
- 默认参数值不建议使用可变类型
可变长度的参数
指在调用函数时,传入的实参个数不固定,因此需要针对溢出的实参有形参接受。
实参与形参中使用 、*
用来接受溢出的位置实参
def func(x,y,*args): //溢出的位置实参会被*保存为元祖的格式,然后赋值给args. ...
*后可以任意变量,一般约定变量名为args
func(*[1,2,3]) //实参中也可以使用*,会将其后的值拆分成位置实参。
实参与形参中都使用*:近似于将实参转为元组
用来接受溢出的关键字实参
def func(x,y,**kwargs) //溢出的位置实参会被**保存为字典的形式,然后赋值给kwargs
** 一般约定变量名为kwargs
func(**{'x':1,'y':2,'x':3}) //**会将实参后的内容拆分为关键字实参
#### 混用 与 *
args必须在*kwargs之前
def func(x,*args,*kwargs) //当不能确定接受的值时,*接受位置实参,**接受关键字实参
//以下写法调用wrapper时,会原封不动的将参数传给index
def wrapper(*args,**kwargs):
index(*args,**kwargs)
### 命名关键字参数
定义函数时,*后定义的参数,即为命名关键字参数,必须使用关键字进行传值。
def func(x,y,*,a,b): //a,b 称为命名关键字参数
...
### 类型提示
在python中可在参数项中增加参数类型的提示和返回值的提示,类型提示并非强制
def test(name:str,age:int = 18)->int
#可以使用内置方法__annotations__查看函数的提示信息
test.__annotations__
函数对象
可以把函数当成变量使用(不加括号)
def func():
...
//1.可以赋值
func1 = func
//2.可以当作函数的参数传入
foo(func)
//3.可以做为另外一个函数的返回值
def foo(func1)
return func1
//4.可以作为容器类型的一个元素
{'k1':func}
函数的嵌套
- 嵌套调用
- 嵌套定义
## 闭包函数
名称空间与作用域、函数嵌套、函数对象的综合使用
有以下特点:
- 为嵌套函数
- 内部嵌套的函数包含对外层函数作用域名字的引用
def f1():
x = 111
def f2():
print('函数2:',x)
return f2
f = f1()
f() // 执行的为F2,f2的x只和f1内的x有关.
装饰器
通过装饰器可以在不修改装饰器对象源代码以及调用方式的前提下,为被装饰对象添加新功能
在函数层面的解释:指定义一个函数,为其他函数添加额外的功能
为何要用装饰器:开放封闭原则
开放:指的是对拓展功能是开放的
- 封闭:指的是对修改源代码是封闭的
通过闭包函数,可以实现不修改原代码的情况下,为函数增加新的功能
无参装饰器
def test(x): print(x) def test2(func1): def test3(*args,**kwargs): print(2) func1(*args,**kwargs) return test3 test4 = test2(test) test = test4 test(1)
装饰器语法糖:
def test2(func1): def test3(*args,**kwargs): print(2) func1(*args,**kwargs) return test3 @test2 //使用@后,再次调用test则自动调用装饰器,语法糖会默认传入一个参数 def test(x): print(x) test(1)
如果叠加多个装饰器,只需要将后边的装饰器写到最上边即可
在使用装饰器时,如果想将原函数的属性也赋值给装饰函数可以使用:
//写法一: def test2(func1): def test3(*args,**kwargs): print(2) func1(*args,**kwargs) test3.__name__ = func1.__name__ test3.__doc__ = func1.__doc__ return test3 //写法二: from functools import wraps def test2(func1): @wrap(func1) def test3(*args,**kwargs): print(2) func1(*args,**kwargs) return test3
有参装饰器
当修改函数功能时,如果需要增加参数的传入,则需要在函数外层加一层嵌套
def test4(y): def test2(func1): print(y) def test3(*args, **kwargs): func1(*args, **kwargs) return test3 return test2 @test4(2) //改函数的执行结果为 test2 def test(x): print(x) test(1)
迭代器
通过迭代器可以进行循环取值
可迭代对象:内置有__ iter __ 方法的都可称为可迭代对象
包括:列表,字符串、元祖、字典、集合、打开文件
使用方式:
- 调用可迭代对象下的 iter 方法,会将其转化为迭代器对象
- 调用 ——next—— 即可获取迭代对象的每个元素,如果超出可迭代的次数,那么会抛出异常
for 循环的原理
- 调用对象的 iter 方法
- 循环使用next
- 捕捉到异常后停止
优缺点
有统一的取值方式
无法按照索引取值
生成器 (generator)
在函数内使用 yield 关键字,那么函数执行后,会返回一个迭代器对象,使用一次 next 那么就会运行一次到yield的位置
def func():
yield 1
yield 2
yield 3
x = func()
x.__next__()
x.__next__()
可以使用yield的表达式形式:
通过表达式形式,可以使用send()进行传值,在传值时需要注意:
- 首次send()需要传 None,不能传其他参数
- 首次send(None),效果与next()一样
- 每次send()会自动执行一次代码
def test():
print('运行一次')
while 1:
num = yield None
print(num)
x = test()
next(x)
x.send( )
三元表达式
格式:
条件成立的值 if 条件 else 条件不成立的值
生成式
列表生成式
可以将代理for 对列表进行遍历处理。
格式:
new_l = [ 需要追加的值 for 每次循环的变量名 in 待循环的列表 if 条件 ]
字典生成式
new_dict =
{k:v for k,v in 待循环的字典 if 条件 }
集合生成式
new_dict =
{value for value in 待循环的集合 if 条件 }
生成器表达式
生成的为生成器,每次需要使用next() 调用
new_dict =
(value for value in 待循环的内容 if 条件 )
递归调用
函数自己调用自己,一定要注意停止递归的条件。在python 中没有尾递归优化。
递归调用的两个阶段:
- 回溯:一层一层的调用
- 递推:递归条件结束后,一层层的返回
匿名函数
匿名函数用于临时调用一次的场景,一般与其他函数配合使用。
定义:
//不需要写return,会自动返回函数体执行的结果
res = lambda 需要传入的参数:函数体
使用场景:比较字典中值得大小,返回字典的key。如:max(),min()
,map(),filter(),reduce()
test_dic = {
'num1':1,
'num2':2,
'num3':3
}
res = max(test_dic,key=lambda k:tese_dic[k])