函数
函数
函数介绍
-
什么是函数:函数就是盛放代码的容器,把实现某一功能的一堆代码丢到一个函数中,做成一个工具
-
使用函数
- 原则:先定义后引用
- 定义阶段:函数体代码不执行,只会检测语法
- 调用阶段:先通过函数名找到函数的内存地址,然后'函数的内存地址()'会触发函数体代码的运行
-
返回值:用return来控制函数的返回值
- 函数中可以有多个return,但只要return执行一次,这个函数就会立刻结束,并返回return后跟着的值
- 当函数没有return或者return后不跟值时返回None
- 当函数的return后跟了多个值时,会返回一个元组,可以用解压赋值拆解使用
-
函数定义的三种方式
# 1. 无参函数 def func(): print('1') # 2. 有参函数 def func(x): print(x) # 3. 空函数 def func(): pass -
调用函数的三种方式
# 语句形式 print() # 表达式形式 res = input() # 可以把函数的调用当作参数传给另一个函数 res = int(input())
函数的参数
- 形参:在函数定义阶段括号内定义的变量,称之为形式参数,简称形参
- 实参:在函数调用阶段括号内 传入的值,称之为实际参数,简称实参
- 两则的关系:实参的值会绑定给形参名,让后可以在函数内使用,函数调用结束后,解除绑定
-
位置形参:在函数定义阶段按照从左往右的顺序依次定义的形参
-
默认形参:在函数定义阶段就已经为某个参数赋值。在调用阶段可以不必为其赋值,默认形参可以与位置形参混合使用,但位置形参必须在默认形参前面。默认形参的值只在函数定义阶段被赋值一次,默认形参的值通常是不可变类型
-
位置实参:在函数调用阶段按照从左往右的顺序依次传入的值,必须与形参一一对应
-
关键字实参:在函数调用阶段按照key=value的格式传入的值,可以打乱顺序,可以与位置实参混合使用,但位置实参必须在前,且不能为同一个参数赋值
-
可变长参数, 在函数调用阶段,实参的个数不固定,而实参是为了形参赋值的,所以对应着必须有一种特殊格式的形参
- 可变长形参
- 形参中的 * :*会接收输入的位置实参,然后将其组成元组,赋值给紧跟其后的变量名
- 形参中的 ** :** 会接收输入的关键字实参,将其组成字典,赋值给紧跟其后的变量名
- 可变长实参:
- 实参中的 * :* 后面跟的必须是一个可迭代对象, *会将其打散成位置实参
- 实参中的 ** :** 后面必须跟的是一个字典,** 会将其打散郑关键字实参
===========*args=========== def foo(x,y,*args): print(x,y) print(args) foo(1,2,3,4,5) def foo(x,y,*args): print(x,y) print(args) foo(1,2,*[3,4,5]) def foo(x,y,z): print(x,y,z) foo(*[1,2,3]) ===========**kwargs=========== def foo(x,y,**kwargs): print(x,y) print(kwargs) foo(1,y=2,a=1,b=2,c=3) def foo(x,y,**kwargs): print(x,y) print(kwargs) foo(1,y=2,**{'a':1,'b':2,'c':3}) def foo(x,y,z): print(x,y,z) foo(**{'z':1,'x':2,'y':3}) ===========*args+**kwargs=========== def foo(x,y): print(x,y) def wrapper(*args,**kwargs): print('====>') foo(*args,**kwargs) - 可变长形参
-
命名关键字参数,在 * 后定义的参数,必须被传值(哟默认值的除外),且必须按照关键字实参的形式传值
def foo(x,y,*args,a=1,b,**kwargs): print(x,y) print(args) print(a) print(b) print(kwargs) foo(1,2,3,4,5,b=3,c=4,d=5) 结果: 1 2 (3, 4, 5) 1 3 {'c': 4, 'd': 5}
类型提示
在定义函数是可以加上参数或则返回值的类型的提示
def func(s:int,y:str)->int:
print(s,y)
return 0
函数对象
函数是第一类对象,即函数可以当作数据传递
- 可以被赋值,引用
- 可以当作参数
- 可以当作函数返回值
- 可以当作容器对象
def func():
pass
f=func
f()
print(func)
def foo(func):
return func
l=[1,func]
- 该特性可以用于取代多分支的if
函数嵌套
- 有嵌套调用和嵌套定义
- 在函数内的调用和定义
# 函数的嵌套定义
def func():
def func2()
pass
func2()
func()
# 函数的嵌套调用
def max1(a,b):
return a if a>b else b
def max2(x,y,i,j):
return max1(max1(x,y),max1(i,j))
名称空间与作用域
-
名称空间;存放名字与其对应的内存地址的地方,三种名称空间及其加载顺序
- 内置名称空间:存放的是python解释器自带的名字,python解释器启动时产生,解释器关闭就销毁
- 全局名词空间:存放的是顶级的名字,py程序运行时产生,程序结束就销毁
- 局部名称空间:在执行文件过程中如果调用函数,才产生的
-
名字的查找顺序:局部名称空间 --> 全局名称空间 --> 内置名称空间
- 在全局无法查看局部内的名称,而在局部可以查看到全局的
-
作用域
#1、作用域即范围 - 全局范围(内置名称空间与全局名称空间属于该范围):全局存活,全局有效 - 局部范围(局部名称空间属于该范围):临时存活,局部有效 #2、作用域关系是在函数定义阶段就已经固定的,与函数的调用位置无关,如下 x=1 def f1(): def f2(): print(x) return f2 x=100 def f3(func): x=2 func() x=10000 f3(f1()) #3、查看作用域:globals(),locals() LEGB 代表名字查找顺序: locals -> enclosing function -> globals -> __builtins__ locals 是函数内的名字空间,包括局部变量和形参 enclosing 外部嵌套函数的名字空间(闭包中常见) globals 全局变量,函数定义所在模块的名字空间 builtins 内置模块的名字空间
闭包函数
-
闭函数:被封闭起来的函数 -->定义函数内部的函数,闭函数的特点是只能在函数内使用
-
包函数:该函数引用了一个名字,该名字来自于e这一层
-
总结:闭包函数指的是"定义在函数内部的函数",它引用了一个"来自于外层函数作用域中的名字"
-
闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域
-
应用领域:延迟计算(原来我们是传参,现在我们是包起来)
方案一:直接以参数的形式传入 def wrapper(x): print(x) wrapper(10) 方案二: def outer(x): def wrapper(): print(x) return wrapper f=outer(10) f()
装饰器
-
装饰器就是闭包函数的一种应用场景
-
装饰器是指我们为被装饰对象添加新功能的一个工具,遵循以下两个原则
- 不修改被装饰对象源代码
- 不修改被装饰对象的调用方式
-
装饰器必须遵循“开放封闭原则”
- 开放指的是对拓展新功能的开放,封闭指的是对修改源代码以及调用方式是封闭的
-
装饰器模板
# 无参装饰器 def outer(func): def wrapper(*args,**kwargs): res = func(*args,**kwargs) return res return wrapper # 有参装饰器 def outer2(mode): def outer(func): def wrapper(*args,**kwargs): res = fun(*args,**kwargs) mode return res return wrapper return outer -
装饰器使用:语法糖 @装饰器名字
无参装饰器 @outer # index = outer(index) def index(): pass # 有参装饰器 @outer(x=10) def index(): pass -
wraps装饰器:对装饰器装饰,不改变被装饰对象的函数结构,将装饰后的函数伪装成被装饰前
def outer(func): @wraps(func) def wrapper(*args,**kwargs): res = func(*args,**kwargs) return res return wrapper
迭代器
-
迭代器是迭代取值的工具
-
优点:提供了一种新的、统一的取值方式,不依赖于索引和key取值;惰性计算,不耗费内存
-
缺点:取值不够灵活,一次性的,只能往后取值,无法预知数据的个数
-
可迭代对象:iterable
- 内置有
__iter__方法
- 内置有
-
迭代器对象:iterator
- 内置有
__iter__方法 - 内置有
__next__方法
- 内置有
-
调用:
- 可迭代对象
.__iter__()--> 返回迭代器对象 - 迭代器对象
.__next__()-->返回的是下一个值
- 可迭代对象
-
注意点:迭代器对象一定是可迭代对象,而可迭代对象不一定是迭代器对象
-
自定义迭代器
- 函数内但凡出现
yield关键字,再调用函数不会执行函数体代码,而是会得到一个生成器对象 - 生成器就是一种自定义的迭代器
yieldVSreturn- 相同点:返回值层都一样
- 不同点:return只能返回一次值,函数就立刻结束,而yield可以进行多次返回
def func(): print('hello 1') yield 1 print('hello 2') yield 2 print('hello 3') yield 3 def my_range(start,stop,step=1): while start < stop: yield start start +=step my_range(1,7,2) # 1 3 5 - 函数内但凡出现
生成器
列表生成式:nums = [i for i in range(5) if i%2==0]
# num = [0,2,4]
字典生成式:res = {i:i**2 for i in range(5)}
# res = {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
集合生成式:res = {i for i in range(5)}
# res = {0,1,2,3,4}
生成器:(expression for item in iterable if condition)
三元表达式
- res = 条件成立时的返回值 if 条件 else 条件不成立时的返回值
面向过程编程
- 面向过程编程时一种编程思想or编程范式
- 面向过程核心是”过程“二字,过程即流程
- 优点:复杂的问题流程化,进而简单化
- 缺点:牵一发而动全身,扩产性差,即高耦合
函数递归
-
函数的递归调用是函数嵌套调用的一种特殊形式
- 具体指的是在调用函数的过程有直接或间接调用自己,称之为函数的递归调用
-
函数的递归调用其实就是用函数实现的循环
例: def f1(): print('from f1') f1() f1() 函数递归调用实现二分法: def search(find_num,nums): print(nums) if len(nums) == 0: print('not exists') return mid_index = len(nums)//2 if find_num>nums[mid_index]: new_num = nums[mid_index+1:] search(find_num,new_nums) elif find_num<nums[mid_index]: new_num = nums[:mid_index-1] search(find_num,new_num) else: print('没找到哦')
匿名函数
匿名函数就是没有名字的函数
正常的:
def func(x,y,z):
return x+y+z
匿名:
lambda x,y,z:x+y+z
# 与函数有相同的作用域,但是匿名意味着引用计数为0,使用一次即释放,除非让其拥有名字
func = lambda x,y,z:x+y+z
func(1,2,3)
使用匿名函数实现 max,min,sorted 对字典比较时比较value值
salaries = {'axx' : 18888, 'zxx' : 1888, 'yxx' : 3888}
max(salaries, key=lambda k: salaries[k])
min(salaries, key=lambda k: salaries[k])
sorted(salaries, key=lambda k: salaries[k])
了解
names = ['egon_nb', "lxx_sb", "hxx_sb"]
filter(lambda name: name.endswith('sb'), names)
map(lambda name: name + "vip", names)
from functools import reduce
reduce(lambda x, y: x + y, [1, 2, 3, 4, 5, 6], 100)
reduce(lambda x, y: x + y, ["a", "b", "c"])

浙公网安备 33010602011771号