python函数闭包和装饰器

关于问题的注意事项

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
原文地址:https://www.cnblogs.com/ahliucong/p/9183274.html