关于问题的注意事项
nonlocal 的问题
nonlocal的变量范围问题,可以获取上层函数的变量,如果上层函数没有这个变量,那么会到上上层找,但不会到全局找.
示例如下:
def fun(): a = 1 def fun2(): a = 2 def fun3(): nonlocal a print(a) fun3() fun2() fun() """ 运行结果 不注释a=2时话是2,注释后是1 """
关于全局作用域和局部作用域的问题
示例:
def fun(): a = 1 def fun2(): a += 1 print(a) fun2() fun() """ 这里会报错的,因为如果在局部一但赋值操作,python解释器就会认为是局部变量,局部变量的话,需要先赋值 """
关于dic增加的j时dic.setdefault("key","value")
这个dic.setdefault("key","value")有增另键值对的功能 ,这里需要注意到的是,这个会有返回.返回的值是value的值.
由于setdefault这个值,当字典中存在这个key时,这次的增加是无效的,但是这个会返回现在字典中这个key的value值.如果不存在key会返回setdefault设置的value值.
示例如下:
dic = {} dic.setdefault("key","value") print(dic) """ 这个是增加字典的键值 """
关于**kwargs 这个值,不能传入**{3:4}这样的值,因为传参相当于变量,变量不能接受纯数字当变量的.
示例:
def func(**kwargs): print(kwargs) def(**{"a":2,"b":3},**{3:4})
关于函数默认赋值的深坑
def func(a,lst = []):
lst.append(a)
return lst
如下问题:
lst1 = func("10")
lst2 = func("20",[])
lst3 = func("30")
最后print(lst1,lst2,lst3)
下面是运行结果
['10', '30'] ['20'] ['10', '30']
原因是当形参传入可变值时,python解释器会分配一个对像,这个对像是不变的,当多次调这个函数时,指向的是同一个地址.所以函数默认传参应该尽量传入可hash的,也就是不可变类型的.
上面是补充,补充完毕
本节主要内容: 1. 函数名的运⽤, 第⼀类对象 2. 闭包 3. 装饰器初识
1. 函数名的运⽤, 第⼀类对象
函数名是一个变量,但它是一个特殊的变量加个()是可以执行的变量
函数名有 以下特性:
1)函数名是一个内存地址,
示例:
def fun(): print(100) print(fun)
上面的代码是打印函数的内存地址.
2)函数名可以赋值给其他变量
示例:
示例是把函数名赋值给b,然后b执行了这个函数
def fun(): print(100) b = fun b()
3)函数名可以当做容器类的元素
函数名可以当做其他容器类的无素,容器类如,列表,字典等
示例:
def fun1(): print("1") def fun2(): print("2") def fun3(): print("3") lst = [fun1,fun2,fun3] for e in lst: print(e) """ 变量名只是内存地址,循环调用的话,只是显示了内存地址 """
4.函数名可以当做函数的参数
示例:
def fun(): print("呵呵") def fun1(fn): fn() fun1(fun)
5. 函数名可以作为函数的返回值
示例:
def fun(): def inner(): print("123") return inner f = fun() f() """ 函数的返回 """
⼆. 闭包
什么是闭包? 闭包就是内层函数, 对外层函数(非全局)的变量的引⽤. 叫闭包
示例:
def fun(): a = 1 def inner(): print(a) inner() fun()
可以通过__closure__来查看内层函数是否闭包 __closure__ 返回none表示不是闭包,返回cell是闭包
如何在函数外部调用闭包,下面是示例
def fun(): a = 1 def inner(): print(a) inner() return inner f = fun() print(f.__closure__)
这里会涉及到函数的多层嵌套,如下
def func1(): def func2(): def func3(): print("嘿嘿") return func3 return func2 func1()()()
由它我们可以引出闭包的好处. 由于我们在外界可以访问内部函数. 那这个时候内部函 数访问的时间和时机就不⼀定了, 因为在外部, 我可以选择在任意的时间去访问内部函数. 这 个时候. 想⼀想. 我们之前说过, 如果⼀个函数执⾏完毕. 则这个函数中的变量以及局部命名 空间中的内容都将会被销毁. 在闭包中. 如果变量被销毁了. 那内部函数将不能正常执⾏. 所 以. python规定. 如果你在内部函数中访问了外层函数中的变量. 那么这个变量将不会消亡. 将会常驻在内存中. 也就是说. 使⽤闭包, 可以保证外层函数中的变量在内存中常驻. 这样做 有什么好处呢? 非常⼤的好处. 我们来看⼀个关于爬⾍的代码:
三. 装饰器初识
装饰器的作⽤就是在不修改原有代码的基础上, 给函数扩展功能.
示例:
如果现在有函数:
def create_people(): print("⼥娲很厉害. 捏个泥⼈吹⼝⽓就成了⼈了")
想要在不修改这个函数调用的基础上实现增加功能.这个可以采用下面的方法
def wrapper(fn):
def inner():
#开始增加的功能
fn()
#结束要增加的功能
return inner
create_people = wrapper(create_people)
这边来描述一下装饰器执行的原理
1.先写好装饰器函数,
2.调用装饰器,传入函数名,
3,把原有的函数改名
4.调用原有的函数名,
5.实际上是调用wrapper(func)()
6.也就是装饰器的内置函数
结论: 我们使⽤warter函数把create_people给包装了⼀下. 在不修改create_people的前提下. 完成了对create_people函数的功能添加
下面是语法糖格式
示例:
def wrapper(fn): def inner(): #开始增加的功能 print("夏天来了") fn() print("冬天来了") return inner @wrapper def create_people(): print("⼥娲很厉害. 捏个泥⼈吹⼝⽓就成了⼈了") create_people()
下面是装饰器完整模型代码,
def wrapper(fn):
def inner(*args,**kwargs):
#前面要加的代码
ret = fn(*args,**kwargs)
#后面要加的代码
return ret
return inner
@wrapper
示例
def wrapper(fn): def inner(*args,**kwargs): #前面执行的代码 ret = fn(*args,**kwargs) #后面执行的代码 return ret return inner @wrapper