Python装潢器详解

Python装潢器详解

装潢器(Decorators)是 Python 的一个重要部分。简朴地说:他们是修正其他函数的功用的函数。他们有助于让我们的代码更简短,也更Pythonic(Python范儿)。在递次拓荒中常常运用到的功用,合理运用装潢器,能让我们的递次为虎傅翼。

1. 函数名运用

函数名是什么?函数名是函数的名字,实质:变量,特别的变量。

1函数名就是函数的内存地址,直接打印函数名,就是打印内存地址

def func1():
    print(123)
print(func1)         # <function func1 at 0x0000029042E02E18>

2函数名能够作为变量

def func1():
    print(111)

f = func1
f()                 # f() 就是func1()    

3函数名能够作为函数的参数

def func1():
    print(111)

def func2(x):
    x()

func2(func1)         #func1作为func2的参数 

4函数名能够作为函数的返回值

def wrapper():
    def inner():
        print('inner')
    return inner
f = wrapper()
f()

5函数名能够作为容器类范例的元素

运用for轮回批量实行函数
def func1():
    print('func1')
def func2():
    print('func2')
def func3():
    print('func3')

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

像上面函数名这类,叫做第一类对象。

 

第一类对象( first-class object)指:

1.可在运行期建立

2.可用作函数参数或返回值

3.可存入变量的实体

*不明白?那就记着一句话,就当平常变量用

 

2. 闭包

1、闭包函数内部函数包括对外部作用域而非全局作用域变量的援用,该内部函数称为闭包函数

2、闭包的作用:爬虫、装潢器

  当递次实行碰到函数实行时,会在内存空间拓荒部分定名空间,当函数实行终了,该定名空间会被烧毁。然则假如这个函数内部构成闭包,则该内存空间不会跟着函数实行完而消逝。

3、怎样推断是不是是闭包:print(函数名.__closure__) 效果是cell申明是闭包,效果是None申明不是闭包。

 

闭包举例

def wrapper():
    name = 'summer'
    def inner():
        print(name)
    inner()

wrapper()     # summer

怎样推断它是不是是一个闭包函数呢? 内层函数名.__closure__  cell 就是=闭包

1.

def wrapper():
    name = 'summer'
    def inner():
        print(name)
    inner()
    print(inner.__closure__)

wrapper()     
实行输出:
summer
(<cell at 0x0000017FC9C90B58: str object at 0x0000017FCA349AD0>,)

2.

name = 'summer'
def wrapper():
    def inner():
        print(name)
    inner()
    print(inner.__closure__)

wrapper() 
效果输出:
summer
None

返回值为None 示意它不是闭包,由于name是一个全局变量,假如函数挪用了外层变量而非全局变量,那末它就是闭包。

3.

name = 'summer'
def wrapper2():
    name1 = 'spring'
    def inner():
        print(name)
        print(name1)
    inner()
    print(inner.__closure__)

wrapper2()
效果输出:
summer
spring
(<cell at 0x030B7310: str object at 0x03043680>,)

只需援用了外层变量最少一次,非全局的,它就是闭包

4:推断下面的函数,是一个闭包吗?******

name = 'summer'
def wraaper2(n):        #相当于n = 'summer' 
  def inner(): print(n) inner() print(inner.__closure__) wraaper2(name)
效果输出:
summer
(<cell at 0x03867350: str object at 0x037F3680>,)

它也是一个闭包. 虽然wraaper2传了一个全局变量,然则在函数wraaper2内部,inner援用了外层变量,相当于在函数inner外层定义了 n = ‘summer’,所以inner是一个闭包函数

 

闭包的优点当函数最先实行时,假如碰到了闭包,他有一个机制,他会永久拓荒一个内存空间,将闭包中的变量等值放入个中,不会跟着函数的实行终了而消逝。

 举一个例子:爬3次,内存开了3次,很占用内存

from urllib.request import urlopen
content1 = urlopen('https://www.cnblogs.com/').read().decode('utf-8')
content2 = urlopen('https://www.cnblogs.com/').read().decode('utf-8')
content3 = urlopen('https://www.cnblogs.com/').read().decode('utf-8')

把它封装成闭包

from urllib.request import urlopen

def index():
    url = "https://www.cnblogs.com/"
    def get():
        return urlopen(url).read()
    return get        #return的是get,就是一个函数名

cnblog = index()
print(cnblog)               # <function index.<locals>.get at 0x02F46978>
content = cnblog()
print(content)              # 页面源码

这个例子,只要第一遍,是从网站抓取的。以后的实行,直接从内存中加载,节约内存空间

 

3. 装潢器

3.1 装潢器初识

装潢器实质就是一个python函数,他能够让其他函数在不须要做任何代码更改的前提下,增添分外的功用,装潢器的返回值也是一个函数对象。

 

装潢器的运用场景:比方插进去日记,机能测试,事务处理,缓存等等场景。

import time
def timmer(f):                           
    def inner():
        start_time = time.time()             
        f()                                                         
        end_time = time.time()             
        print('此函数的实行时刻为{}'.format(end_time - start_time))         
    return inner                       

def func1():                           
    print('in func1')               
    time.sleep(1)                    

func1 = timmer(func1)               
print(func1)
func1()           # 这里的func1是全新的func1,就是上面的赋值,此时相当于实行 inner函数
输出效果:
<function timmer.<locals>.inner at 0x03822DF8>
in func1
此函数的实行时刻为1.0003533363342285

代码从上至下实行

 

语法糖:想测试谁,前面加@装潢器函数,即可。写装潢器,约定俗成,函数名为wrapper

def wrapper(func):
    def inner(*args,**kwargs):
        '''被装潢函数之前'''
        ret = func(*args,**kwargs)
        '''被装潢函数以后'''
        return ret
    return inner

@wrapper
def func(*args,**kwargs):
    print(args,kwargs)
    return 666

print(func())
输出效果:
() {}
666

装潢器应用return制作了一个假象,func()实行,实际上是实行inner()func()把本来的func()给覆盖了

3.2 装潢器传参

1:上面装潢器的例子,func1,要传2个参数a,b

import time
def timmer(f):
    def inner(a,b):
        start_time = time.time()
        f(a,b)
        end_time = time.time()
        print('此函数的实行时刻为{}'.format(end_time - start_time))
    return inner

@timmer
def func1(a,b):
    print('in func1 {}{}'.format(a,b))
    time.sleep(1)  # 模仿递次逻辑

func1(1,2) 
实行输出:
in func1 12
此函数的实行时刻为1.0006024837493896

2:假如有多个参数呢?改成动态参数

import time
def timmer(f):
    def inner(*args,**kwargs):
        start_time = time.time()
        f(*args,**kwargs)
        end_time = time.time()
        print('此函数的实行时刻为{}'.format(end_time - start_time))
    return inner

@timmer
def func1(*args,**kwargs):
    print('in func1 {}{}'.format(args,kwargs))
    time.sleep(1)  # 模仿递次逻辑

func1(1,2,a='3',b=4) 
实行输出:
in func1 (1, 2){'b': 4, 'a': '3'}
此函数的实行时刻为1.000101089477539

函数的实行时,*打散

函数的定义时,*聚合。

from functools import wraps
def wrapper(f):                  # f = func1
    def inner(*args,**kwargs):               #聚合,args (1,2,3)
        '''实行函数之前的相干操纵'''
        ret = f(*args,**kwargs)               # 打散 1,2,3
        '''实行函数以后的相干操纵'''
        return ret
    return inner

@wrapper  # func1 = wrapper(func1)  func1 = inner
def func1(*args):                           #args (1,2,3) 聚合
    print(666)
    return args

print(func1(*[1,2,3]))  
实行输出:
666
(1, 2, 3)

3*****

import time                                 #1.加载模块

def timmer(*args,**kwargs):                     #2.加载变量  5.吸收参数True,2,3

    def wrapper(f):                             #6.加载变量  8.f = func1

        print(args, kwargs)                     #9.吸收timmer函数的值True,2,3

        def inner(*args,**kwargs):                 #10.加载变量. 13.实行函数inner
            if flag:                         #14 flag = True
                start_time = time.time()             #15 猎取当前时刻
                ret = f(*args,**kwargs)             #16 实行func1
                time.sleep(0.3)                 #19 守候0.3秒
                end_time = time.time()             #20 猎取当前时刻
                print('此函数的实行效力%f' % (end_time-start_time)) #21 打印差值
            else:
                ret = f(*args, **kwargs)

            return ret                         #22 返回给函数挪用者func1()
        return inner                         #11 返回给函数挪用者wrapper
    return wrapper                         #7.返回给函数挪用timmer(flag,2,3)

flag = True                                 #3 加载变量
@timmer(flag,2,3)      # 4.实行函数timmer(flag,2,3) 17.实行函数func1 两步:1,timmer(flag,2,3) 相当于实行wrapper                                                     2.@wrapper 装潢器 func1 = wrapper(func1)
def func1(*args,**kwargs):
    return 666                             #18 返回给函数挪用者f(*args,**kwargs)

print(func1())                             #12 实行函数 

写装潢器,平常嵌套3层就能够了

3.3 多个装潢器,装潢一个函数

def wrapper1(func):                  # func ==  f函数名
    def inner1():
        print('wrapper1 ,before func')          # 2
        func()
        print('wrapper1 ,after func')          # 4
    return inner1

def wrapper2(func):  # func == inner1
    def inner2():
        print('wrapper2 ,before func')          # 1
        func()
        print('wrapper2 ,after func')          # 5
    return inner2

@wrapper2                      #  f = wrapper2(f)  内里的f==inner1  表面的f == inner2
@wrapper1                      #  f = wrapper1(f)   内里的f==函数名f  表面的f == inner1

def f():                          # 3
    print('in f')

f()                              # inner2() 
实行输出:
wrapper2 ,before func
wrapper1 ,before func
in f
wrapper1 ,after func
wrapper2 ,after func

哪一个离函数近,哪一个先盘算最底下的先实行

实行递次如下图:

 

 多个装潢器,都是根据上图的递次来的

4. 装潢器的__name____doc___

__name__:函数名

__doc___:函数的诠释  

平常函数

def func1():
    """
    此函数是完成上岸的功用,参数分别是...作用。
    return: 返回值是上岸胜利与否(True,False)
    """
    print(666)

func1()
print(func1.__name__)         #猎取函数名
print(func1.__doc__)         #猎取函数名解释申明 

实行输出:
666
func1
此函数是完成上岸的功用,参数分别是...作用。
return: 返回值是上岸胜利与否(True,False)

这个有什么用呢?比方日记功用,须要打印出谁在什么时刻,挪用了什么函数,函数是干啥的,花费了屡次时刻,这个时刻,就须要猎取函数的有效信息了

带装潢器的函数

def wrapper(f):      # f = func1

    def inner(*args,**kwargs):             #聚合, args (1,2,3)
        '''实行函数之前的相干操纵'''
        ret = f(*args,**kwargs)              # 打散 1,2,3
        '''实行函数以后的相干操纵'''
        return ret
    return inner

@wrapper
def func1():
    """
    此函数是完成上岸的功用,参数分别是...作用。
    return: 返回值是上岸胜利与否(True,False)
    """
    print(666)
    return True

func1()
print(func1.__name__)
print(func1.__doc__) 
实行输出:
666
inner
实行函数之前的相干操纵

函数装潢以后,相当于实行了inner函数,所以输出inner

 

为了处理这个题目,须要挪用一个模块wraps

wraps将 被润饰的函数(wrapped) 的一些属性值赋值给润饰器函数(wrapper) ,终究让属性的显现更相符我们的直觉

from functools import wraps

def wrapper(f):                  # f = func1
    @wraps(f)                 #f是被装潢的函数
    def inner(*args,**kwargs):         #聚合args (1,2,3)
        '''实行函数之前的相干操纵'''
        ret = f(*args,**kwargs)          # 打散 1,2,3
        '''实行函数以后的相干操纵'''
        return ret
    return inner

@wrapper
def func1():
    """
    此函数是完成上岸的功用,参数分别是...作用。
    return: 返回值是上岸胜利与否(True,False)
    """
    print(666)
    return True

func1()
print(func1.__name__)
print(func1.__doc__) 
实行输出:
666
func1
此函数是完成上岸的功用,参数分别是...作用。
return: 返回值是上岸胜利与否(True,False)
Up Next:

浅谈大规模高并发效劳的伸缩题目

浅谈大规模高并发效劳的伸缩题目