一.函数的定义
# 函数的定义 def 函数名(形参): ''' 文档字符串 函数功能: 函数参数: 函数返回值 ''' function_body return [expression] # 函数的使用 函数名(实参)
# 1. 函数的定义 def sayHello(name): print("你好,{},很高兴再次见到你".format(name)) return "{}来北京看我了".format(name) #函数的调用 print(sayHello("dali")) # 输出 你好,dali,很高兴再次见到你 dali来北京看我了
# 2. 函数可以没有参数 def Hello(): a= 3+4 return a print(Hello()) #输出 7
# 3. 定义一个变量,给变量的赋值是个函数名,就相当于把这个变量变成一个函数了 def sayHello2(name): print("你好,{},很高兴再次见到你".format(name)) return 1 message = sayHello2 print(message("xiaochengzi")) # 输出 你好,xiaochengzi,很高兴再次见到你 1
二.函数的参数
1. 必备参数 或者 位置参数
位置参数的特点:必须要给它传值,位置在最前面,定义了几个位置参数,调用函数的时候就必须传几个参数,否在会报错
# x,y 是位置参数,位置参数就是必须参数,函数调用时不能缺少 def add(x,y): return x+y print(add(2,3)) #5
# 错误示例 def add(x,y): return x+y print(add(2)) # TypeError: add() missing 1 required positional argument: 'y'
2. 默认参数
如果不想给某个参数传值,但是参数个数还不减少,那可以在函数声明时设置默认参数,就是给某个参数一个默认值,
如果不传值,函数调用时,默认就用的是设置的默认参数,如果传值了,函数调用时就用的是传的值
特点:默认参数的位置,一定要放到所有的位置参数的后面
# # j 定义时就给了个默认参数,如果j不传值,就默认取0, 默认参数一定要放在所有参数的最后面 def add1(i,j=0): return i+j print(add1(3)) # 3
3. 不定长参数
如果函数声明时,还不能确定到底会有多少个参数,可以定义不定长参数
特点:1) 不定长参数用*args表示, 2) *args的类型是元组 3)它的位置必须在默认参数后面
def add2(x,y=0,*args): print(args) return x + y print(add2(2)) # 输出 () # 此时x=2,y =0, *args没有传参,是个空元组 2 print(add2(4,5)) #输出 () # 此时x=4,y =5, *args没有传参,是个空元组 9 print(add2(1,3,5,7,9)) # 输出 (5, 7, 9) # 此时x=1,y =3, *args是个元组,其值是(5,7,9) 4
def add2(x,y=0,*args): print(args) return x + y + sum(args) # 使用参数,要去掉*号 print(add2(1,3,5,7,9)) #输出 (5, 7, 9) 25
4. 关键字参数
如果想传的参数是字典,就可以用关键字参数
特点:关键字参数 **kargs ,其类型是字典,必须最最后面,以 a = 1的形式传参
def add4(x,y,*args,**kwargs): print(kwargs) return x + y + sum(args) + sum(kwargs.values()) print(add4(1,3,3,4,5,a=1,b=5,c=5,d=-5)) # 输出 {'a': 1, 'b': 5, 'c': 5, 'd': -5} # 此时,x=1,y=3, args=(3,4,5),kwargs = {'a': 1, 'b': 5, 'c': 5, 'd': -5} 22
5. 形参与实参
三.函数的作用域
函数作用域的寻找顺序:Local -> enclosing->global -> build-in
1. L(Local)局部作用域
变量是在函数内定义的
a = 3 # 全局作用域 def scope(): a = 5 # 局部作用域 print(a) scope() # 输出 5
2. E(Enclosing)闭包函数外的函数中
见后面闭包的介绍
3.G (Global)全局作用域
变量是在函数外定义的,是整个文件的
a = 3 def scope(): print(a) scope() #输出 3
4. B(Built-in) 内建作用域
Python 自带的,预留的一些变量,函数名等
Python自带的不太适合举例说明
作用域的查找顺序是先局部,后全局,最后再内建,如果都没有该变量,就会报错
def scope(): print(a) scope() # NameError: name 'a' is not defined
5. 作用域有两个函数:Locals() 和 globals()
a = 3 def scope(): a = 5 print(globals()) # 输出 {'__name__': '__main__', '__doc__': '\n作用域生效机制\n局部 闭包函数外的函数中 全局 内建\n\n', '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x10511a470>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/Users/qiaoersun/Documents/千万别删/测试/Codes/testfanStudyPractice/lesson02/作用域.py', '__cached__': None, 'a': 3, 'scope': <function scope at 0x10529bd08>}
暂时还没弄明白
四.闭包(_closure_)
闭包的特点:
1. 闭包函数必须有内嵌函数
2. 内嵌函数需要引用嵌套函数的变量
3. 闭包函数必须返回内嵌函数
闭包的作用:
1. Python闭包就是装饰器(或叫修饰器)
2. 在不改原有函数任何内容的情况下,给原有函数增加功能
def greeting(name): ''' 1.在greeting函数中内嵌了一个greet函数 2.greeting函数的返回值是一个函数greet ''' def greet(): print(name) return greet # 函数的返回值是greet函数 # 定义一个func变量,调用greeting函数,因为greeting函数的返回值是greet,而greet也是个函数,所以就相当于func是个函数 # 此时只是返回了greet,greet函数并没有被调用,所以也不会有任何返回值 func = greeting('老刘') # 那么再调用func这个函数就等于是调用的下面的部分 ''' def greet(): print(name) ''' func() # 输出 老刘
# greeting()其实就是greet函数,那么也可以用下面的方式调用 greeting("小猫")() # 输出 小猫
对闭包函数的公式总结
def greeting(name): # 1. greeting函数里内嵌,嵌套了一个greet函数 --> 满足了:闭包函数必须有内嵌函数 a = 5 def greet(): # 2.在内嵌函数里引用了嵌套函数(greeting)的变量:a 和 name, 这个变量必须是内嵌函数外层已经定义的,不能是全局变量---> # 满足了:内嵌函数必须要引用嵌套函数的变量 print(name) print(a) # 3. greeting函数里返回了一个内嵌函数greet--->满足了:函数必须返回内嵌函数 return greet # 注意此处不能返回greet() # 4. 由上面3点的同时满足,才使得greeting成为一个闭包函数 # 5. 闭包函数的调用,在上面已经有了,此处略
在Python里闭包其实就是装饰器
五.装饰器
装饰器的作用:就是为已经存在的对象添加额外的功能
装饰器的应用场景:插入日志,性能测试,事务处理,缓存,权限校验等等
示例:计算一个函数执行用了多长时间
''' 闭包的作用: 1. 在Python闭包就是装饰器(或叫修饰器) 2. 在不改原有函数任何内容的情况下,给原有函数增加功能 利用闭包实现修饰器 ''' ''' 1. 计算原有函数wait()的执行时间,但是不能改wait()函数的任何代码 2. add()函数,把add()执行结果写入一个文件,不能改变add()函数的任何值 ''' import time import random ''' 解决办法:用闭包的方式解决,那么就要把原来已经存在的功能,即函数作为参数传递给闭包,此处形参用func代替 闭包一定是写在原还是的前面的 ''' # 2.用闭包的方式不改变原代码的情况下,新增计算时间的功能 def decorator(func): def wrapper(): startTime = time.time() result = func() endTime = time.time() print("测试结束,花费时间{}秒".format(endTime-startTime)) return result # 上面已经执行过了result,这里为什么还要再返回一下result?不返回会有什么影响? return wrapper ''' wait函数是已经存在的一段代码,假设有一万行,突然有个需求,要在wait上加一个功能,计算wait函数执行需要花多长时间 如果直接在wait函数上改,可能会引起其他bug,第二你也懒得看wait的代码,一个好的解决办法就是使用闭包,用装饰器来解决 这个问题 ''' # 1. 已有的功能 def wait(): w = random.randint(5,10) print(w) time.sleep(w) print("执行wait完毕,用了{}秒".format(w)) # 3. 调用闭包,获取执行结果 a = decorator(wait) # 相当于a 就是 wrapper函数里 a() # 再调用wrapper函数, 就能得到计算的结果 # 输出 9 执行wait完毕,用了9秒 测试结束,花费时间9.005208253860474秒
上面利用闭包写好了修饰器,但是闭包的使用不是像上面那样通过a = decorator(wait)()的方式使用的,而是通过在原有函数的上面加@闭包函数名的方式使用
import time import random # 2.用闭包的方式不改变原代码的情况下,新增计算时间的功能 def decorator(func): def wrapper(): startTime = time.time() result = func() endTime = time.time() print("测试结束,花费时间{}秒".format(endTime-startTime)) return result return wrapper # 3.通过@闭包函数名的方式,实现装饰器 @decorator # 1. 已有的功能 def wait(): w = random.randint(5,10) print(w) time.sleep(w) print("执行wait完毕,用了{}秒".format(w)) # 4. 直接调用原函数就可以实现功能 wait() # 输出 10 执行wait完毕,用了10秒 测试结束,花费时间10.002923727035522秒
# 示例2 ''' 已知:现有add函数 需求:add函数执行结果写到一个文件里去 ''' def writeAddtoFile(func): # 闭包的参数func,将来传的就是原有的功能函数add,这是永远不变的,套路,固定写法;外面的函数传函数名 def wrapper(a,b): # 内嵌函数的参数,接受的是原函数add的参数,这也是永远不变的,套路,固定写法;里面的函数传的才是参数 result = func(a,b) with open("add.txt",'a',encoding='utf-8') as f: f.write(str(result)+"\n") # 换行写入,要在写入的内容后面 加 +"\n" f.close() return wrapper @writeAddtoFile def add(x,y): return x+y add(5,8) add(3,5) add(1,1)