1.函数的概念
- 概念
- 作用
- 方便代码复用
- 分解任务,简化程序逻辑
- 使代码更加模块化
- 函数分类
- 内建函数
- 三方函数
- 自定义函数
2.函数的基本使用
-
简单定义
def 函数名(): 函数体
-
函数的调用:函数名()
3.函数的参数
-
单个参数
-
场景:需要动态的调整函数体中某一个处理信息时,则可以以参数的形式接受到相关数据
-
定义:
def 函数名(形参): 函数体
-
函数的调用:函数名(实参)
-
形参和实参的概念
-
-
多个参数
-
场景:需要动态的调整函数体中多个处理信息时,则可以以逗号作分割,接受多个参数
-
定义:
def 函数名(形参1,形参2): 函数体
-
函数的调用
- 方式一:函数名(实参1,实参2,实参3…):形参实参一一对应
- 方式二:函数名称(形参1=实参1,形参n=实参n…):可以指明形参名称(关键字参数);不需要严格按照顺序
-
-
不定长参数
-
场景:如果函数体中,需要处理的数据不确定长度,则可以以不定长参数的方式接受数据
-
方式一
-
定义
def 函数名(*args):#元组 函数体 #函数体中,可以直接以元组变量的方式使用该参数
-
使用:函数名(实参1,实参2,实参3…)
-
-
方式二
-
定义
def 函数名(**dict):#字典 函数体 #函数体中,可以直接以字典变量的方式使用该参数
-
使用:函数名(参数名称1=参数1,参数名称2=参数2…)
-
-
参数拆包
-
装包:把传递的参数,包装成一个集合,称之为”装包“
-
拆包:把集合参数,再次分解成单独的个体,称之为”拆包“
def mySum(a,b,c,d): print(a+b+c+d) def test(*args): print(args)#装包 print(*args)#拆包 mySum(*args) test(1,2,3,4)
def mySum(a,b): print(a) print(b) def test(**kwargs): print(kwargs) #print(**kwargs):拆包操作,使用**进行拆包操作,这样输出会出错,但可以直接使用,如下: mySum(**kwargs)#相当于拆成了a=1,b=2(若test(a=1,c=2),则会报错) test(a=1,b=2)
-
-
-
缺省参数(默认值)
-
场景:当我们使用一个函数时,如果大多数情况下,使用的某个数据是一个固定值或属于主功能之外的小功能实现,则可以使用默认值(这种参数称为”缺省参数“)
-
定义
def 函数名(形参1=默认值1,形参2=默认值2...): 函数体
-
使用:函数名(实参1,实参2),此处如果是缺省参数,可以不写
-
-
参数注意
-
值传递和引用传递
- 值传递:是指传递过来的,是一个数据的副本;修改副本对原件没有任何影响
- 引用传递:是指传递过来的,是一个变量的地址;通过地址,可以操作同一份原件
-
但是注意
-
在python中,没得选,只有引用传递(地址传递)
-
但是
如果数据类型是可变类型,则可以改变原件
如果数据类型是不可变类型,则不可以改变原件
-
-
4.函数的返回值
-
场景:当我们通过某个函数,处理好数据之后,想拿到处理的结果
-
语法
def 函数(): 函数体 return 返回值
-
注意事项
如果想要返回多个数据,可先把多个数据包装成一个集合,整体返回(列表、元组、字典…)
5.函数的使用描述
-
场景:当我们编写三方函数,为了便于他人使用,就需要描述清楚我们所写的函数功能以及使用方式等信息
-
定义格式:直接在函数体的最上面,添加三个双引号对注释
def 函数(): """帮助信息""" 函数体 【return 返回值】
-
查看函数使用文档:help(函数名称)
-
经验:一般函数的描述需要说明以下几个信息:
- 函数功能
- 参数
- 含义
- 类型
- 是否可以省略
- 默认值
- 返回值
- 含义
- 类型
6.偏函数
-
概念&场景
当我们写一个参数比较多的函数时,如果有些参数,大部分场景下都是某一个固定值,那么为了简化使用,就可以创建一个新函数,指定我们要使用的老函数的某个参数,为某个固定的值;这个新函数就是“偏函数”。
-
语法
-
方式一:自己写一个新的
def test(a,b,c,d=1): print(a+b+c+d) def test2(a,b,c=1,d=2): test(a,b,c,d) test2(1,2)
-
方式二:借助functools模块的partial函数
import functools newFunc = functools.partial(函数名称,特定参数=偏爱值) def t(a,b,c,d=1): print(a+b+c+d) t2 = functools.partial(t,c=1) t2(1,2)
-
-
使用场景:int()
nmuStr = "100010" result = int(numStr,base=2) print(result)#34 #在往后的一段时间内,都需要将一个二进制的字符串,转换成为对应的十进制数字 import functools int2 = functools.partial(int,base=2) print(int2(numStr))
7.高阶函数
-
概念:当一个函数A的参数,接收的是另一个函数时,则把这个函数A称为“高阶函数”
-
例如:sorted函数
l = [{"name":"sz","age":18},{"name":"sz2","age":13},{"name":"sz3","age":19}] def getKey(x): return x["age"] reslut = sorted(l,key=getKey) print(reslut)
-
案例:动态计算两个数据
def caculate(num1, num2, caculateFunc): print(caculateFunc(num1, num2)) def sum(a, b): return a + b def jianfa(a, b): return a - b caculate(6, 2, jianfa)
8.返回函数
-
概念:是指一个函数内部,它返回的数据是另外一个函数;把这样的操作称为“返回函数”
-
案例:根据不同参数,获取不同操作,做不同的计算
def getFunc(flag): def sum(a,b,c): return a+b+c def jian(a,b,c): return a-b-c #根据不同的flag值,来返回不同的操作函数 if flag == "+": return sum elif flag == "x": return jian function = getFunc("+") result = function(1,3,5) print(result)
9.匿名函数
-
概念:也称为“lambda函数”;顾名思义,就是指没有名字的函数
-
语法
- lambda 参数1,参数2…:表达式
- 限制
- 只能写一个表达式:不能直接return
- 表达式的结果就是返回值
- 只适用于一些简单的操作处理
-
测试
result = (lambda x,y:x+y)(1,2) print(result) newFunc = lambda x,y:x+y print(newFunc(1,2))
l = [{"name": "sz", "age": 18}, {"name": "sz2", "age": 13}, {"name": "sz3", "age": 19}] reslut = sorted(l, key=lambda x: x["age"]) print(reslut)
10.闭包
-
概念:下面的内层函数+所引用的外层变量,称为“闭包”
- 在函数嵌套的前提下
- 内层函数引用了外层函数的变量(包括参数)
- 外层函数又把内层函数当作返回值进行返回
-
标准格式
def test1(a): b=10 #其他函数定义代码 def test2(): print(a) print(b) return test2
def test(): a = 10 def test2(): print(a) return test2 newFunc = test() newFunc()
-
应用场景:外层函数会根据不同的参数,来生成不同作用功能的函数
-
案例:根据配置信息,生成不同的分割线函数
def line_config(content,length): def line(): print("-"*(length//2)+content+"-"*(length-length//2)) return line line1 = line_config("闭包",40) line1() line1() line2 = line_config("xxx",80) line2() line2()
-
注意事项
-
闭包中,如果要修改引用的外层变量:需要使用nonlocal声明,否则当作是闭包内新定义的变量
def test(): num = 10 def test2(): nonlocal num num = 666 print(num) print(num) test2() print(num) return test2 test() #10 #666 #666
-
当闭包内,运用了一个后期会发生变化的变量时,一定要注意:
def test(): num = 10 def test2(): print(num) num = 20 return test2 newFunc = test() newFunc()#20
def test(): funcs = [] for i in range(1,4): def test2(): print(i) funcs.append(test2) return funcs newFuncs = test() newFuncs[0]()#3 newFuncs[1]()#3 newFuncs[2]()#3
def test(): funcs = [] for i in range(1,4): def test2(num): def inner(): print(num) return inner funcs.append(test2(i)) return funcs newFuncs = test() newFuncs[0]()#1 newFuncs[1]()#2 newFuncs[2]()#3
-
11.装饰器
-
作用:在函数名以及函数体不发生改变的前提下,给一个函数附加一些额外代码。
-
语法
-
案例:
-
发说说,发图片
#定义两个功能函数 def fss(): print("发说说") def ftp(): print("发图片") #相关的逻辑代码 buttenIndex = 1 if buttenIndex == 1: fss() else: ftp()
-
”开放封闭“原则
-
已经写好的代码,尽可能不要修改
-
如果想要新增功能,在原先代码基础上,单独进行扩展
#发说说,发图片,必须在用户登录之后 #定义两个功能函数 def fss(): checkLogin() print("发说说") def ftp(): checkLogin() print("发图片") def checkLogin(): print("登陆验证。。。") #相关的逻辑代码 buttenIndex = 1 if buttenIndex == 1: fss() else: ftp()
推荐如下: #发说说,发图片,必须在用户登录之后 #定义两个功能函数 def fss(): print("发说说") def ftp(): print("发图片") def checkLogin(func): def inner(): print("登陆验证。。。") func() return inner fss = checkLogin(fss) #相关的逻辑代码 buttenIndex = 1 if buttenIndex == 1: fss() else: ftp()
语法糖写法如下:推荐 #发说说,发图片,必须在用户登录之后 def checkLogin(func): def inner(): print("登陆验证。。。") func() return inner #定义两个功能函数 @checkLogin def fss(): print("发说说") @checkLogin def ftp(): print("发图片") #fss = checkLogin(fss) #相关的逻辑代码 buttenIndex = 1 if buttenIndex == 1: fss() else: ftp()
-
-
单一职责
-
-
注意:装饰器的执行时间是立即执行。
# 给发说说函数增加一些额外的功能 # 1.函数名字不能发生改变 # 2.函数体内部的代码不能发生改变 def check(func): print("装饰器的执行时间是立即执行") def inner(): print("登陆验证操作。。。") func() return inner @check def fss(): print("发说说") #结果:装饰器的执行时间是立即执行
-
进阶
-
装饰器叠加
-
从上到下装饰(从外层到内层)
-
从下到上执行
def zhuangshiqi_line(func): def inner(): print("-"* 40) func() return inner def zhuangshiqi_start(func): def inner(): print("*"* 40) func() return inner @zhuangshiqi_line @zhuangshiqi_start # == print_content=zhuangshiqi_start(print_content) def print_content(): print("被装饰内容") print_content() 结果如下: ---------------------------------------- **************************************** 被装饰内容
-
-
对有参数函数进行装饰
-
无论什么场景,保证函数调用参数个数一致
-
为了通用,可以使用不定长参数,结合拆包操作进行处理
def zsq(func): def inner(*args,**kwargs): print("-"*40) print(args,kwargs) func(*args,**kwargs) return inner @zsq def pnum(num1,num2,num3): print(num1,num2,num3) @zsq def pnum2(num): print(num) pnum(123,333,num3=999) pnum2(999)
-
-
对有返回值的函数进行装饰:无论什么场景,保证函数返回值一致
-
带有参数的装饰器
def getzsq(char): def zsq(func): def innner(): print(char*40) func() return innner return zsq @getzsq("-") def f1(): print("666") f1()
-
12.生成器函数
-
生成器
- 是一个特殊的迭代器(迭代器的抽象层次更高)
- 拥有迭代器的特性
- 惰性计算数据,节省内存
- 能够记录状态,并通过next()函数,访问下一个状态
- 具备可迭代特性
- 但是打造一个自己的迭代器比较复杂
- 需要实现很多方法:后续在”面向对象“编程中会进行讲解
- 所以,就有一个更加优雅的方式”生成器“
-
创建方式
-
生成器表达式:把列表推导式的[]修改成()
g = (i for i in range(1,100) if i%2==0) print(next(g))#2 print(next(g))#4 print(g.__next__())#6
-
生成器函数
-
函数中包含yield挂起语句
-
这个函数的执行结果就是”生成器“
#yield可以阻断当前函数的执行;当使用next()函数或--next--(),都会让函数继续执行,当执行到下一个yield语句结束时,又会被暂停 def test(): print("xxx") yield 1 print("aaa") yield 2 print("bbb") yield 3 print("ccc") g = test() print(g) #<generator object test at 0x000002B677DE3C80> print(next(g)) #xxx #1 print(next(g)) #aaa #2 print(next(g)) #bbb #3
-
-
-
产生数据的方式
- 生成器具备可迭代特性
- next()函数:等价于:生成器._next_()
- for in
-
send()函数
-
send方法有一个参数,指定的是上一次被挂起的yield语句的返回值
def test(): res1 = yield 1 print(res1) res2 = yield 2 print(res2) g = test() print(g.__next__()) print(g.send("000")) 1 000 2
-
相比于._next_():可以额外的给yield语句传值
-
注意第一次调用:g.send(None)
-
-
关闭生成器
- g.close()
- 后续如果继续调用,会抛出StopIteration异常提示
-
注意
- 如果碰到return:会直接终止,抛出StopIteration异常提示
- 生成器只会遍历一次
13.递归函数
- 体现
- 函数A内部继续调用函数A
- 概念
- 传递
- 回归
- 注意事项
- 案例
14.函数作用域
- 基本概念
- 变量的作用域
- 变量的作用范围:可操作范围
- python是静态作用域,也就是说在python中,变量的作用域源于它在代码中的位置;在不同的位置,可能有不同的命名空间
- 命名空间
- 是作用域的体现形式
- 不同的具体的操作范围
- Python-LEGB
- L-Local:函数内的命名空间,作用范围:当前整个函数体范围
- E-Enclosing function locals:外部嵌套函数的命名空间,作用范围:闭包函数
- G-Global:全局命名空间,作用范围:当前模块(文件)
- B-Builtin:内建模块命名空间,作用范围:所有模块(文件)
- 注意:python中没有块级作用域;块级作用域:代码块中,如if、where、for后面的代码块
- LEGB规则:按照L->E->G->B的顺序进行查找
- 变量的作用域
- 基于命名空间的常见变量类型
- 局部变量
- 在一个函数内部定义的变量
- 作用域为函数内部
- 查看函数变量:locals()
- 全局变量
- 在函数外部,文件最外层定义的变量
- 作用域为整个文件内部
- 查看全局变量:globals()
- 注意点
- 访问原则:从内到外
- 结构规范
- 全局变量
- 函数定义:使用、修改
- 后续代码
- 全局变量和局部变量重名
- 获取:就近原则
- 修改:global全局变量声明
- 命名
- 局部变量