函数,函数嵌套,装饰器,装饰器语法糖,装饰器嵌套


基本使用

盛放功能的容器,用功能时直接来调就可以,增强代码的可读性

先定义,后调用

定义函数的三种格式

有参函数,无参函数,空函数(站位)

return 返回值

可以返回一个值,可以没有返回值,没有值就返回None

return可以返回多个值,返回的类型是个元组

并且可以返回后,可以解压赋值给变量

return也可以结束函数,可以有多个return,并且用于if判断,结束函数

可以提示他传入的‘值’的类型

def foo(x:int,y:int) ->'返回值是int'#函数定义
   res=x+y
   print(res)
foo(10,20)#函数调用
Type hinting  类型提示功能
?

定义阶段及调用原理:

1,只检测语法,不执行代码 ,申请内存空间,把函数体放进去,然后函数名指向内存地址

2,调用函数就是,指向内存地址

函数的参数

形参:函数定义阶段,括号内指定的参数
实参:函数调用阶段,括号内传入值(变量值)
在函数调用阶段,实参可以传值给形参,函数体就可以用了
调用结束后,实参和形参解除绑定,传入的值就不存在了
?

位置形参和位置实参

位置形参:
在函数定义阶段,按照从左到右的顺序一次定义的参数,称之为位置参数
def foo(x,y,z):
   print(x,y,z)
特点:必须被传值,一一对应
位置实参:
在函数调用阶段,按照从左到右的顺序一次传入的值,称之为位置实参
foo(10,20,)
特点:按照位置依次给形参赋值
?

关键字实参

在函数的调用阶段,按照key=value的格式来传值,称之为关键字实参
def foo(x,y,z):
   print(x,y,z)
foo(x=1,y=2,z=3
foo(y=2,z=3,x=1
特点:可以打乱传值的顺序,仍能为指定的形参赋值
可以混用位置实参和关键字实参,但是位置实参必须在关键字前面
不能为同一个形参重复赋值
def foo(x,y,z):
    print(X,Y,Z)
foo(10,z=30,y=20)

默认形参

在函数的定义阶段,就已经为形参赋值了,意味着在调用阶段可以不用为其传值,称之为默认形参
def foo(name,age,gender='male'):
   print(name,age,gender)
register('egon',18)
register('tony',23)
register('jack',25)
resister('lili',19,'female')
可以混用位置形参和默认形参,但是默认形参必须跟在位置形参的后面
默认形参的值推荐指向不可变类型,让它为可控的形参
默认形参的值只在函数定义阶段赋值一次,如果不传值,默认为默认赋值
如果一定要默认参数为可变类型,请按照一下模式:
def resgister1(name,hobby,hobbies=None):
   if hobbies is None:
       hobbies=[]
   hobbies.append(hobby)
   print('%s的爱好是%s'%(name,hobbies))
resgister1('egon','read')
resgister1('lili','music')
?

 

可变长参数

***
在形参中带*
*会将溢出的位置实参值汇总成元组,然后赋值给紧跟其后的形参名
不能将溢出的关键字实参,赋值给*y(可变长形参)
def foo(x,*y): #()
   print(x,y)
foo(1)
foo(1,2,3,4,5,6)
?
在形参中带**
会将溢出的关键字实参汇总成字典,然后赋值给紧跟其后的形参名
def foo(x,**y):
   print(x,y)
foo(1,a=111,c=555,b=666)
?
?
在实参中带*
def foo(x,z,y)
   print(x,z,y)
foo(*[11,22,33])#相当于for循环遍历,注意在实参中加*后的参数,能被for循环遍历的猜可以加
在实参中带**
def foo(x,z,y)
   print(x,z,y)
foo(**{'x':1,'z':3},'y':2)
**的实参只能是字典
?
def wrapper(*args,**kwargs)       #不过穿什么进去,都会原封不动的传给index函数
   index(*args,**kwargs)    

函数对象

函数是第一类对象,函数是第一公民

1 函数可以被赋值(函数的内存地址)

def func(aaa)
   print(aaa)
res=func
res()
?

2 函数可以当做参数传给函数

def foo():
   print('from foo')
?
f=foo      #注意不要加括号,我们是把函数的地址给了f 而不是调用它
?
def func(f)
   f()
func(fo)
   

3 函数可以当做函数的返回值

def foo():
  print('from foo')
?
f = foo   # 注意不要加括号,我们是把函数的地址给了f 而不是调用它
?
def func(x):
  return x #函数可以当做函数的返回值
func(foo)

4 函数可以当做容器类型的元素

def foo()
   print('from foo')
?
l=[foo]
l[0]()
?
?

5 非常优雅的处理if判断

dic={
'1':['登入',login],
'2':['注册',resister],
'3':['体现',withdeaw],
'4':['转账',transfer],
'5':['购物',shopping],
'6':['充值',recharge]
}
while True:
print('0 退出')
for i in dic:
print('%s %s'%(i,dic[i][0]))
choice = input('请输入编号').strip()
if choice in dic:
dic[choice][1]()
break
elif choice=='0':
break
else:
print('编号错误')
?

函数的嵌套

函数 嵌套调用:在调用过程中,该函数内部又调用了其他函数

def bar()
   print('from bar')
def foo()
   print('from foo')
   bar()
foo()
用于比较最大值
在编写大功能的时候,里面可以嵌套一些小功能,来实现的我们的大功能

函数嵌套自己,实现循环

函数嵌套定义:在函数内又定义了其他函数--------封闭效果

?
def foo()
   def bar():
       print('小虎')
   bar()
foo()
?
bar() #不能在函数外调用在函数内定义的函数,如果想打破限制,想在函数调用,必须将它的内存地址return 出来,注意我们是想在外层调用,而不是直接调用
def foo()
   x=10
   def bar():
       print('小虎')
   
   return bar
res=foo()
   
   

名称空间与作用域

名称空间Namespace:存放名字与内存地址的绑定关系

内置名称空间
放的python自带的名字
   生命周期:解释器启动产生,解释器关闭则销毁
全局名称空间
放的顶级的名字
   生命周期:运行python文件则立即产生,文件运行完毕则结束
局部名称空间
放的函数内的名字
   生命周期,函数调用则产生,函数调用完毕则销毁
?

名字的查找优先级:

局部》全局》内置

基于自己当前所在的位置向外查找: L E G B 行业黑话

重要结论:名称空间是在函数定义阶段为准生成的,与调用无关

global 局部变全局,声明我这是全局名称

x=100
def foo():
   global x
   x=200
   print(x)
foo()   #200
print(x)#200
声明x为全局变量
?
?
?
?
在局部修改全局变量
X=[111,222,333]
def foo()
   X.append(444)
foo()
print(x)# [111,222,333,444]

nonlocal

def foo():
   x = 10
   def func():
       nonlocal x
       x = 30
   print(x)
foo()  #30
声明 x 为外层的名称(变量)如果外层没有,将报错!

总结:

全部作用域:全局存活,全局有效

内置名称空间,全局名称空间

局部作用域:临时存活,局部有效

局部名称空间,函数内的空间

闭包函数

闭包函数=函数对象+函数嵌套定义+名称空间与作用域

闭函数:定义在函数内部的函数‘

包函数:该内部函数引用了一个外层函数的名字

def foo(x):
   
   def wrapper():
       print(x)
   return wrapper
f=foo(111)
f()
def xxx():
   x=222
   f()
xxx()
在调用闭包函数它访问的永远是它外层的名称
闭包的函数的作用:闭包函数是一种为函数传参的方案
def outter():
   X=111
   def wrapper():
       print(x)
   return wrapper   #千万不要加括号,我们是把它扔出来,而不是调用它
wrapper=uotter()
wrapper()
?
更进一步的处理
def outter(x):
   def wrapper():
       print(x)
   return wrapper   #千万不要加括号,我们是把它扔出来,而不是调用它
wrapper=uotter(111)
wrapper()
?
毕竟还是自己写的
def foo(x):
   def werapper():
       print(x)
   return werapper
werapper=foo(100)
werapper()
'''当一个函数不能在添加形参的时候,而我们还需要给一个函数传值
,只能用函数的嵌套,让他先成为闭包函数(可以引用外层的名字),
再使用return把它仍出来,重新赋值给它原来的名字,让他看起来还是全局函数,并且可以为其传值'''

装饰器

什么是装饰器?
   用来为被装饰者添加功能的工具
   被装饰者:函数
   装饰器:可以用函数实现装饰器这种工具
   也就是说该函数可以为其他函数添加额外的功能
   
为什么要用装饰器?
   不改动被装饰者的源代码及调用方式的前提下
   为被装饰着添加上新功能
   

实现为一个函数添加运行时间 并且不修改源代码 并且不能修改函数的调用方式

?
import
def index(x)
   print('welcome to index page %s'%x)
   time.sleep(3)
方案一
import time
def index(x)
   res=time.time()
   print('welcome to index page %s'%x)
   time.time
   res1=time.time()
   print(res1-res)
index(111)  
#虽然我们实现了查看代码的运行时间新功能,但是修改了源代码,否定
?
方案二
import time
def index(x)
   print('welcome to index page %s'%x)
   time.sleep(3)
res=time.time ()
index(111)
res1=time.time
print(res1-res)
#虽然实现了不动源代码,实现了查看代码运行时间,但是我们以后要在不同的时间段,还要使用该函数,难道要一直复制吗?代码冗余,是对程序员的侮辱,方案二 否定
?
?
方案三
如果我们不让代码冗余,只有一种方案,就是函数
import time
def wrapper(funk)        
   res=time.time ()
   index(111)      >   @1funk()
   res1=time.time()
   print(res1-res)  
   #但是这样我们的查看代码运行时间,只能给index用,这样就函数死了
   #我们需要把它写活
   #调用index的函数就必须得是一个变量(@1)了,我们通过后续在使用时,直接穿其他函数也能使用该功能
   #那么此时的wrapper()函数就需要传值了
   #形参传值
wrapper(index)直接传参

   #实现了代码不冗余,也没有修改源代码,但是我index是直接可以调用的,你现在告诉我要修改,它的调用方式,这是不行的,但是它解决了代码的冗余问题,我们还需要使用该方案,实现在调用它,不需要修改调用方式
那么直接传参,就不适合了,要通过闭包函数,包给它
?
方案四
?
import time
def index(x)
   print('welcome to index page %s'%x)
   time.sleep(3)
?
?
import time
def outter(func):
   def wrapper()        
       res=time.time()
       funk(111)
       res1=time.time()
       print(res1-res)
   return wrapper       #我们的wrapper函数是全局函数,必须把它用return返回
index = outter(index)  
index()
#现在我们可以直接调index了,但是你看起来像是在调index,其实你是在调wrapper,只不过是wrapper赋值给了index,但是我们之前调用index是可以传值给定义阶段的形参的,现在名字像了,还不能传值,
当一个函数需要给它的函数内的函数原封不动的传值时,***是最好的方案
方案5   闭包函数
import time
def index(x)
   print('welcome to index page %s'%x)
   time.sleep(3)
   return 666
?
?
import time
def outter(func):
   def wrapper(*args,**kwargs)        
       res=time.time()
       funk(*args,**kwargs)
       res1=time.time()
       print(res1-res)
   return wrapper
index=outter(index)
index(1111)
现在我们把被装饰对象的 名字 传参 都装饰好了,但是还不够,如果被装饰对象有返回值得话,我们装饰器却没有返回值,让我们让它有返回值
import time
def outter(func):
   def wrapper(*args,**kwargs)        
       res=time.time()
       car=funk(*args,**kwargs) #不要立刻返回值,我们还需要执行代码,在最后返回就可以了
       res1=time.time()
       print(res1-res)
       return car
   return wrapper
index=outter(index)
res=index(1111)
print(res)
?
?
但是装的还不够像 有一些内置方法,可以让它更像
wrapper.__name__=index__name__
wrapper.__doc__=wrapper__doc__
有人帮你做了这件事,你可以导入模块
?
from functools import wraps
?
方案六  装饰器语法糖   更进一步
?
饰器语法糖
用起来很方便
?
   
?
@outter    #index=outter(index的内存地址)
def index(x):
   print('welcome to index page%s'%x)
   tinme.sleep
   return 777
当python语法检查到了有@后面跟了函数名字,并且是已经定义的函数
它会把它下一行的函数的内存地址,传到@后面的函数的括号里,并且赋值给原来的函数名
 index=outter(index)
让它把细节做的更像原来的index,好用牛逼
?
固定语法
from functools import wraps #导入wraps模块
def outter(func):
   @wraps(func)   #我在这
   def wrapper......
把边边角角装饰的更好
?
?
?
重要!,无参装饰器  模板
?
def outter(func):
   def wrapper(*args,**kwargs)
       res=funk(*args,**kwargs)
       return res
   return wrapper
?
?
有参装饰器 模板  
def car(x):
   def outter(func):
       def wrapper(*args,**kwargs)
       print"小虎真水啊%s"%x)
           res=func(*args,**kwargs)
           return res
       return wrapper
   return outter

叠加多个装饰器

def outter1(func1):
   def wrapper1(*args,**kwargs)
       res=funk1(*args,**kwargs)
       return res
   return wrapper1
?
def outter2(func2):
   def wrapper2(*args,**kwargs)
       res=funk2(*args,**kwargs)
       return res
   return wrapper2
?
def outter3(func3):
   def wrapper3(*args,**kwargs)
       res=funk3(*args,**kwargs)
       return res
   return wrapper3
?
         # index=wrapper1的内存地址
@outter1  #调用outter1=outter2内存地址 》wrapper1
@outter2  #调用outter2=outter3的内存地址 》wrapper2
@outter3  #调用outter3=index地址   》 wrapper3
def index():
   print'小虎'
?
   
index() # = wrapper1》wrapper》2》wrpper》3 》index
?
?
也就是说最上面的装饰器,装饰的全部
?
上面的加载顺序和执行顺序
叠加装饰器的加载顺序自下而上
叠加装饰器的运行顺序自上而下
遇到叠加装饰器。他的运行顺序是,首先从上到下,在从下到上。
?
?

 

有参装饰器

?
def outter(engine='file'):
   def deco2(funk):
       def wrapper(*args,**kwargs):
           if engine == 'file':
               print('基于file认证')
               res = funk(*args,** kwargs)
               return res
           elif engine == 'ladp':
               print('基于ladp认证')
               
       return wrapper
   return deco2
?
?
@outter(engine='file')
def index():
   print('小虎')
index()
?
?

 

相关