python之函数的进阶闭包装饰器

Posted 独孤。。。

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python之函数的进阶闭包装饰器相关的知识,希望对你有一定的参考价值。

本节内容:函数的进阶、闭包、装饰器

  1.名称空间

  2.作用域

  3.取值顺序

  4.函数的嵌套

  5.内置函数

  6.关键字

       7.函数的应用

       8.闭包

       9.装饰器

1、名称空间                                                                                                   

1.1 名称空间

当程序运行时,代码从上之下一次执行,他会将变量与值的关系存储在一个空间中,这个空间叫做名称空间,命名空间,全局名称空间。

1.2 局部名称空间

当程序遇到函数时,他会将函数名存在内存中,函数体莫不关心。

当函数执行时,内存会临时开辟一个空间,存放函数体里面的代码(变量,代码等),

函数外面访问不到临时空间的内容,随着函数的执行完毕,临时名称空间会释放掉,

向这个临时开辟的空间叫临时名称空间,也叫局部名称空间。

  

1.3 Python中名称空间种类

    内置名称空间。

    全局名称空间。

    局部名称空间。

2、作用域                                                                                                       

2.1 全局作用域

    内置名称空间

    全局名称空间

2.2 局部作用域

    局部名称空间

2.3 加载顺序

内置名称空间 ---> 全局名称空间(当程序执行时) ---> 局部名称空间(当函数调用时)

3、取值顺序                                                                                                  

取值顺序:单向不可逆

局部名称空间(当函数调用时) -->全局名称空间(当程序执行时) -->内置名称空间

3.1 先从局部找

name1 = \'oldboy\'
def func2():
    name1 = \'alex\'
    print(name1)
func2()
=====>输出
alex
View Code

3.2 再从全局找

name1 = \'oldboy\'
def func2():
    print(name1)
func2()
=====>输出
oldboy
View Code

3.3 最后从内置空间找

def func2():
    print(input)
func2()
=====>输出
<build-in function input>
View Code
4、函数的嵌套                                                        
def func1():
    print(666)

def func2():
    func1()
    print(333)

def func3():
    func2()
    print(222)
print(111)
func3()
print(555)
=====>输出
111
666
333
222
555
==========
def func1():
    name = \'alex\'
    print(name)
    def func2():
        name1 = \'太白\'
        print(333)
    print(444)
    func2()
func1()
=====>输出
alex
444
333
View Code

5、内置函数                                                                                                

5.1 globals()

返回一个字典,字典里面的内容是全局作用域的内容。

name = \'alex\'
age = 1000
sex = \'\'
def func1():
    name1 = \'oldboy\'
    age = 10000
func1()
print(globals())
print(locals())
=========>输出
None, \'name\': \'alex\', \'age\': 1000, \'sex\': \'\', \'func1\': <function func1 at 0x0000000001CF2E18>}
None, \'name\': \'alex\', \'age\': 1000, \'sex\': \'\', \'func1\': <function func1 at 0x0000000001CF2E18>}
View Code

5.2 locals()

返回一个字典,当前位置 的所有变量。

def func1():
    name1 = \'oldboy\'
    age = 10000
    def func2():
        name2 = \'wusir\'
        age2 = 25
        print(globals())
        print(locals())
    func2()
func1()
=====>输出
None, \'func1\': <function func1 at 0x0000000001D02E18>}
{\'age2\': 25, \'name2\': \'wusir\'}
View Code

6、关键字                                                                                                  

6.1 global

6.1.1 引用并改变一个全局变量

count = 1
def func1():
    global count
    count = count + 1
    # count = count + 100
    print(count)
func1()
print(count)
=====>输出
2
2
View Code

6.1.2 在局部作用域声明一个全局变量

def func1():
    global name
    name = \'alex\'
    print(name)
func1()
print(name)
=====>输出
alex
alex
View Code

6.2 nonlocal

1、不能操作全局变量

从哪层引用的该变量,从那层开始全部改变。

def func1():
    count = 1
    def inner():
        nonlocal count
        count = count + 3
        print(count)
        def inner2():
           pass
    inner()
    print(count)
func1()
=====>输出
4
4
View Code
取值:引用而不是改变
取值是从小到大取值 LEGB
想改变上层空间的变量 要用到global nonlocal
2、对于可变的数据类型 list dict set 不用global nonlocal
list = []
def func1():
    list.append(666)
func1()
print(list)
=====>输出
[666]
View Code

3、如果默认参数是一个可变的数据类型,那么他在内存中永远是一个

def extendList(val,list=[]):
    list.append(val)
    return list
list1 = extendList(10)
print(\'list1=%s\'%list1)  
list2 = extendList(123,[])
print(\'list2=%s\'%list2)
list3 = extendList(\'a\')
print(\'list3=%s\'%list3) 

print(\'list1=%s\'%list1)
print(\'list2=%s\'%list2)
print(\'list3=%s\'%list3)
=======>输出
list1=[10]
list2=[123]
list3=[10, \'a\']
list1=[10, \'a\']
list2=[123]
list3=[10, \'a\']
View Code

7、函数名的应用                                                     

def func1():
    print(666)
1 打印函数名
print(func1)  # <function func1 at 0x000000000258F9D8>

2 函数名可以作为容器类数据的元素
def func1():
    print(111)

def func2():
    print(222)

def func3():
    print(333)

l1 = [func1, func2, func3]
for i in l1:
    i()

3 函数名可以作为函数的参数

def func1():
    print(111)

def func2(x):
    print(x)
    x()
    print(222)

func2(func1)

4 函数名可以作为函数的返回值

def func1():
    return 111

def func2(x):  # x = func1
    print(222)
    return x

ret = func2(func1)  # func1
print(ret())
print(ret)
View Code

7.1 第一类对象

def func3(x):
    print(x)
    return x

def func1():
    print(1111)
    return 111
f1 = func1
f2 = f1
f3 = f2
print(f3)
f3()
=========>输出
<function func1 at 0x00000000025120D0>
1111
View Code

8、闭包                                                                                                       

内层函数对外层函数非全局变量的引用,就叫做闭包。

def wrapper():
    name = \'alex\'
    def inner():
        print(name)
    inner()
wrapper()
======>输出
alex
=======
name = \'alex\'
def wrapper():
    def inner():
        print(name)
    inner()
    print(inner.__closure__)  # None
wrapper()
=======>输出
alex
None
View Code

8.1 通过函数名.__closure__

name = \'alex\'
def wrapper(x):
    x = name
    def inner():
        print(x)
    inner()
    print(inner.__closure__) 
wrapper(name)
=====>输出
alex
(<cell at 0x0000000001E16498: str object at 0x000000000049C110>,)
View Code
如果 python 解释器遇到了闭包,他有一个机制,这个闭包不会随着函数的结束而释放。
9、装饰器                                                                                                   
import time
print(time.time()) 

def func1():
    time.sleep(0.3)
    print(\'非常复杂......\')

start_time = time.time()
func1()
end_time = time.time()
print(\'此函数的执行效率%s\' %(end_time-start_time))
===========>输出
1527471870.8405445
非常复杂......
此函数的执行效率0.3000171184539795
==============
# 改版1:我要封装到一个函数中
import time
print(time.time()) 
def func1():
    time.sleep(0.3)
    print(\'非常复杂......\')

def func2():
    time.sleep(0.3)
    print(\'特别复杂......\')

func1()
func2()
========>输出
1527471978.33138
非常复杂......
特别复杂......
==============
# 改版2:被测试函数当参数传入,可以测试多个函数的执行效率
def timmer(f):
    start_time = time.time()
    f()
    end_time = time.time()
    print(\'此函数的执行效率%s\' %(end_time-start_time))

timmer(func1)
timmer(func2)
=================
改版3::测试函数执行效率的同时,不要改变原函数的调用方式。
def func1():
    time.sleep(0.3)
    print(\'非常复杂......\')
def func2():
    time.sleep(0.3)
    print(\'特别复杂......\')
# func1()
# func2()
def timmer(f):
    start_time = time.time()
    f()
    end_time = time.time()
    print(\'此函数的执行效率%s\' % (end_time - start_time))
f1 = func1
func1 = timmer  #
func1(f1)  # timmer(func1)
=================
改版4::改版3虽然大体上满足了我的要求,但是增加两行代码,
而且多了参数,不好,继续改,尽量不添加其他代码,而且做到调用时一模一样
最简单的装饰器。
def func1():
    time.sleep(0.3)
    print(\'非常复杂......\')
def func2():
    time.sleep(0.3)
    print(\'特别复杂......\')
# func1()
# func2()

def timmer(f):  # f = func1 函数名
    def inner():
        start_time = time.time()
        f()
        end_time = time.time()
        print(\'此函数的执行效率%s\' % (end_time - start_time))
    return inner

func1 = timmer(func1)  # inner
func2 = timmer(func2)  # inner
func1()  # inner()
func2()

========================
改版5::改版4每次测试一个函数的执行效率时,都需要加一行 func1 = timmer(func1)代码,麻烦
python提出了一个语法糖 @。


def timmer(f):  # f = func1 函数名
    def inner():
        start_time = time.time()
        f()
        end_time = time.time()
        print(\'此函数的执行效率%s\' % (end_time - start_time))
    return inner

@timmer  # func1 = timmer(func1)  inner
def func1():
    time.sleep(0.3)
    print(\'非常复杂......\')

func1()  # inner()
========================
改版6:被装饰的函数肯定要有参数的,你现在不能满足,解决这个问题。
被装饰的函数带参数的装饰器
def timmer(f):  # f = func1 函数名
    def inner(*args,**kwargs):  # args = (1,2),kwargs {sex:\'nv\',name:\'alex\'}
        start_time = time.time()
        f(*args,**kwargs)  # f(1,2,,sex=\'nv\',name=\'alex\')
        end_time = time.time()
        print(\'此函数的执行效率%s\' % (end_time - start_time))
    return inner

@timmer  # func1 = timmer(func1)  inner
def func1(a,b):
    time.sleep(0.3)
    print(a,b)
    print(\'非常复杂......\')


@timmer  # func1 = timmer(func1)  inner
def func2(a,b,name,sex=\'man\'):  # f(1,2,,sex=\'nv\',name=\'alex\')
    time.sleep(0.3)
    print(a,b,sex,name)
    print(\'非常复杂......\')

func2(1,2,sex=\'nv\',name=\'alex\')  # inner()
====================
改版7:被装饰的函数肯定要有返回值的,解决这个问题。
被装饰的函数带参数且有返回值的装饰器

def timmer(f):  # f = func2 函数名
    def inner(*args,**kwargs):  # args = (1,2),kwargs {sex:\'nv\',name:\'alex\'}
        start_time = time.time()
        ret = f(*args,**kwargs)  # f(1,2,,sex=\'nv\',name=\'alex\')
        end_time = time.time()
        print(\'此函数的执行效率%s\' % (end_time - start_time))
        return ret
    return inner
@timmer  # func1 = timmer(func1)  inner
def func2(a,b,name,sex=\'man\'):  # f(1,2,,sex=\'nv\',name=\'alex\')
    time.sleep(0.3)
    print(a,b,sex,name)
    print(\'非常复杂......\')
    return 666

print(func2(1,2,sex=\'nv\',name=\'alex\'))  # inner()

def timmer(f):
    def inner(*args,**kwargs):
        start_time = time.time()
        ret = f(*args,**kwargs)
        end_time = time.time()
        print(\'此函数的执行效率%s\' % (end_time - start_time))
        return ret
    return inner
@timmer
def func2(a,b,name,sex=\'man\'):
    time.sleep(0.3)
    print(a,b,sex,name)
    print(\'非常复杂......\')
    return 666

ret1 = func2(1,2,sex=\'nv\',name=\'alex\')
print(ret1)


def wrapper(f):
    def inner(*args,**kwargs):
        """被装饰函数执行之前的操作"""

        ret = f(*args,**kwargs)
        """被装饰函数执行之后的操作"""
        return ret
    return inner
View Code
装饰器 本质就是闭包
装饰器根本作用:在不影响原函数执行的基础上,增加一些额外的功能登录认证,打印日志等等。

以上是关于python之函数的进阶闭包装饰器的主要内容,如果未能解决你的问题,请参考以下文章

函数进阶之闭包函数和装饰器

python 基础篇 11 函数进阶----装饰器

Python函数进阶:闭包装饰器生成器协程

Python进阶之[非局部变量,闭包,装饰器]

Python 进阶

Python之闭包装饰器