python-闭包函数和装饰器

闭包函数

内部函数包含对外部作用域而非全剧作用域名字的引用,该内部函数称为闭包函数
我们都知道函数内的变量我们要想在函数外部用,可以直接返回这个变量,那么如果我们想在函数外部调用函数内部的函数呢? 将函数的名字进行返回就好了,则是闭包函数的常见的用法。

可以通过__closure__可以进行判断是不是闭包函数
def func():
    def inner():
        print("hello")
    print(inner.__closure__)  # None  不是闭包函数
    return inner

f = func()
f()
****************************************************************************
def func():
    str_ = "test" 
    def inner():
        print(str_) # 变量的引用
    print(inner.__closure__)  # (<cell at 0x0000022AA8A6B4C8: str object at 0x0000022AA8A6A768>,)
    return inner


f = func()
f()  # test

***********************************************************************************

str_ = "test"
def func():
    def inner():
        print(str_)
    print(inner.__closure__)  # None
    return inner


f = func()
f()  # test

**************************************************************************************
def func():
    name = "Yang"
    def inner1():
        def inner2():
            print(name)
        print(inner2.__closure__)  # (<cell at 0x000001B750AFB4C8: str object at 0x000001B750AE7880>,)
        return inner2

    print(inner1.__closure__)  # (<cell at 0x000001B750AFB4C8: str object at 0x000001B750AE7880>,)
    return inner1


f = func()
f1 = f()
f1()

闭包函数参数位置的测试

def test():
    def test1():
        print("参数")
    print(test1.__closure__,)  # None
    return test1


test()()  # 参数

def test(b):  # 参数
    def test1():
        print("参数", b)  
    print(test1.__closure__,)  # (<cell at 0x000001617D11B4C8: int object at 0x000000006FA9D060>,)
    return test1  # 参数 10


test(10)()

def test():
    def test1(b):  # 参数
        print("参数", b)
    print(test1.__closure__,)  # None
    return test1


test()(10)  # 参数 10

def test(a):
    def test1(b):
        print("参数", b)
    print(test1.__closure__,)  # (<cell at 0x0000014008F9B4C8: int object at 0x000000006FA9D060>,)
    return test1


test(10)(10)  # 参数 10

总结:

命名空间:

  一共有三种命名空间从大范围到小范围的顺序:内置命名空间、全局命名空间、局部命名空间

作用域(包括函数的作用域链):

小范围的可以用大范围的
但是大范围的不能用小范围的
范围从大到小

在小范围内,如果要用一个变量,是当前这个小范围有的,就用自己的
如果在小范围内没有,就用上一级的,上一级没有就用上上一级的,以此类推。
如果都没有,报错
函数的嵌套:

  嵌套调用

  嵌套定义:定义在内部的函数无法直接在全局被调用

函数名的本质:

  就是一个变量,保存了函数所在的内存地址

闭包:

  内部函数包含对外部作用域而非全剧作用域名字的引用,该内部函数称为闭包函数

装饰器

装饰器的形成过程

import time

def func1():
    print('in func1')

def timer(func):
    def inner():
        start = time.time()
        func()
        print(time.time() - start)
    return inner

func1 = timer(func1)
func1()

装饰器的语法糖

前面的代码可以进行简化,就是进行语法糖,直接在被装饰的函数的上方加上@装饰函数,如下所示:

import time

def timer(func):
    def inner():
        start = time.time()
        func()
        print(time.time() - start)
    return inner

@timer # 语法糖
def func1():
    print("asdns")
# timer(func1)()

func1()

装饰器的本质的闭包函数。装饰器的功能:在不修改原函数即其调用方式的情况下对原函数的功能进行了扩展。

带参数的装饰器

import time


def func(fun):
    def inner(x, y):  # 带参数的装饰器  inner的参数是也被装饰的函数的个数一样
        start = time.time()
        fun(x, y)
        print(time.time() - start)

    return inner


@func
def test(x, y):
    print("传入参数{}".format(x))


test(10, 10)

针对一个对所有参数都接收的装饰器

def func(fun):
    def inner(*args, **kwargs):  #  将形式参数 变成 *args, **kwargs
        start = time.time()
        fun(*args, **kwargs)
        print(time.time() - start)

    return inner


@func
def test(*args, **kwargs):
    print("传入参数{}{}".format(args, kwargs))

test(10, a=10)

带返回值的装饰器

import time


def func(fun):
    def inner(*args, **kwargs):  #  将形式参数 变成 *args, **kwargs
        start = time.time()
        res = fun(*args, **kwargs)
        print(time.time() - start)
        return res

    return inner


@func
def test(*args, **kwargs):
    print("传入参数{}{}".format(args, kwargs))
    return "返回值"


ret = test(10, a=10)
print(ret)  # 返回值

# 或者
f = func(test)
ret = f(10, a=10)
print(ret)  # 返回值

装饰器在查看函数的一些信息的方法在此处都会失效

def index():
    """
    测试注释语句
    :return: None
    """
    print("from index")

print(index.__name__)  # 查看函数名
print(index.__doc__)  # 查看函数的注释

进行改善

from functools import wraps
def func(func):
    @wraps(func)
    def inner():
        print("inner")
        func()
        print("end")

    return inner
@func
def index():
    """
    测试
    :return:
    """
    print("test")

print(index.__name__)
print(index.__doc__)

开放封闭原则

一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改。所以我们必须允许代码扩展、添加新功能。

1.对扩展是开放的

为什么要对扩展开放呢?

我们说,任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改。所以我们必须允许代码扩展、添加新功能。

2.对修改是封闭的

为什么要对修改封闭呢?

就像我们刚刚提到的,因为我们写的一个函数,很有可能已经交付给其他人使用了,如果这个时候我们对其进行了修改,很有可能影响其他已经在使用该函数的用户。

3.装饰器的主要功能和装饰器的固定结构

装饰器的主要功能:

在不改变函数调用方式的基础上在函数的前、后添加功能。

装饰器的固定格式:

def func(func):
    def inner(*args, **kwargs):
        "执行函数之前要做的"
        re = func(*args, **kwargs)
        "执行函数之后要做的"
        return re
    return inner

装饰器的固定格式——wraps

from functools import wraps

def deco(func):
    @wraps(func) #加在最内层函数正上方
    def wrapper(*args,**kwargs):
        return func(*args,**kwargs)
    return wrapper

带参数的装饰器

def outer(flag):
    def timer(func):
        def inner(*args,**kwargs):
            if flag:
                print('''执行函数之前要做的''')
            re = func(*args,**kwargs)
            if flag:
                print('''执行函数之后要做的''')
            return re
        return inner
    return timer

@outer(False)
def func():
    print(111)

func()

多个装饰器装饰同一个函数

def wrapper1(func):
    def inner():
        print('wrapper1 ,before func')
        func()
        print('wrapper1 ,after func')
    return inner

def wrapper2(func):
    def inner():
        print('wrapper2 ,before func')
        func()
        print('wrapper2 ,after func')
    return inner

@wrapper2
@wrapper1
def f():
    print('in f')

f()
posted @ 2019-08-04 20:55  yangchangjie  阅读(98)  评论(0编辑  收藏  举报