闭包和装饰器

学习笔记:

闭包closure:

1.函数名是指向函数的变量。函数名=变量。

def lazy_sum(*args):
    def sum():
        ax = 0
        for n in args:
            ax = ax + n
        return ax
    return sum
f = lazy_sum(1, 3, 5, 7, 9)
print(f)   #<function lazy_sum.<locals>.sum at 0x000002744971F828>

备注:此时的lazy_sum的输出为函数sum,因此目前f指向函数sum。打印f时结果应为<function lazy_sum.<locals>.sum at 0x000002744971F828>。因此f()也就是sum()才能具体计算结果。

2.闭包就是能够读取其他函数内部变量的函数。可以理解为定义在一个函数内部的函数。

注意:由于返回的是一个函数,此时该函数并没有立即执行,只有调用了f()时才执行。在调用f()执行时,内部变量可能会发生变化。

错误

def count():                                
    fs = []                               
    for i in range(1, 4):                        
        def f():                              
             return i*i                            
        fs.append(f)                      
    return fs                           
f1, f2, f3 = count()                       
f1     #<function count.<locals>.f at 0x0000017BDCEED828>                        
f1()  #9                              
f2()  #9                                
f3()  #9                                  

正确                                      

def count():                              
    def f(j):                                    
        def g():                               
            return j*j                        
        return g                           
    fs = []                                  
    for i in range(1, 4):                        
        fs.append(f(i))                           
    return fs                                 
f1, f2, f3 = count()                        
f1  #<function count.<locals>.f.<locals>.g at 0x00000189F9936A68>                  
f1()  #1                                     
f2()  #4                                       
f3()  #9                                  

个人理解:

1)首先fs作为一个list,可以被分别赋值给f1、f2、f3,此时相当于f1=fs[0]。

2)错误版本运行完后f=i2,但调用时查找到i已经变为3,因此所有输出结果均为9。

   正确版本运行时将i分别固定给了j,即将i和f1建立了关联。

3)如下版本虽然也将i和f1建立了关联,但返回值不是函数,为固定值。

def count():
    fs = []
    for i in range(1, 4):
        def f(i):
             return i*i
        fs.append(f(i))
    return fs
f1, f2, f3 = count()
print(f1)  #1
print(f2)  #4
print(f2)  #9

4)正确版本缩减:使用lambda函数简化。

def count():
    def f(j):
        return lambda:j*j
    fs = []
    for i in range(1, 4):
        fs.append(f(i))
    return fs
f1, f2, f3 = count()

3.使用闭包时,对外层变量赋值前,需要先使用nonlocal声明该变量不是当前函数的局部变量。

def inc():
    x = 0
    def fn():
        nonlocal x
        x = x + 1
        return x
    return fn
f = inc()
print(f()) # 1
print(f()) # 2

小结:

一个函数可以返回一个计算结果,也可以返回一个函数。

返回一个函数时,牢记该函数并未执行,返回函数中不要引用任何可能会变化的变量。

装饰器decorator:装饰器本质上就是一个函数,它用来给别的函数增加功能。

装饰器对被装饰函数是完全透明的,也就是说,装饰器不会对要加功能的函数的源代码进行修改,而且也不会改变函数的调用方式。

装饰器可以在代码运行期间动态地增加函数的功能,当我们想要给多个函数增加相同的功能,一个一个地去修改这些函数效率很低,而且代码混乱,就可以采用装饰器。

def 装饰器名称(被装饰函数func):
    def 装饰器作用函数wrapper(如果func有参数那么输入*args,**kw):
        执行语句xxxxxxxxxxxxxxx
        func(如果被func有参数那么输入*args,**kw)
        # 如果被装饰函数中含有return则需要给func赋值
        # value_func=func(*args,**kw)
        执行语句xxxxxxxxxxxxxxx
        # 如果被装饰函数中含有return则需要返回value_func
        # return value_func
    return 装饰器作用函数wrapper
@装饰器名称
def 被装饰函数():
    执行语句
    # return
被装饰函数()

函数作用:查找2-10000内的素数。

import time   # 引入计时模块
def display_time(func): 
# 装饰器 作用为计时
    def wrapper():
        t1 = time.time()
        func()
        t2 = time.time()
        print(t2-t1)
    return wrapper
def is_Prime(num):
    if num < 2:
        return True
    elif num ==  2: 
        return False
    else:
        for i in range(2,num):
            if num % i == 0:
                return False
        return True

@display_time 
def find_Prime():
    for i in range(2,10000):
        if is_Prime(i):
            print(i)
find_Prime
# find_Prime=display_time(find_Prime)
# <function display_time.<locals>.wrapper at 0x000001D60C837AF8>
find_Prime() # 等价于wrapper()

find_Primereturn时:                                                       

import time
def display_time(func):
    def wrapper():
        t1 = time.time()
        result = func()
        t2 = time.time()
        print(t2-t1)
        return result
    return wrapper
def is_Prime(num):
    pass

@display_time
def find_Prime():
    count = 0
    for i in range(2,10000):
        if is_Prime(i):
            count += 1
    return count

find_Prime有参数时:                                                         

def display_time(func):
    def wrapper(*args,**kw):
        t1 = time.time()
        result = func(*args,**kw)
        t2 = time.time()
        print(t2-t1)
        return result
    return wrapper
def is_Prime(num):
    pass

@display_time
def find_Prime(n):
    count = 0
    for i in range(2,n):
        if is_Prime(i):
            count += 1
    return count
count = find_Prime(10000)
print (count)

注意:需要把原始函数的__name__等属性复制到wrapper()函数中,否则,有些依赖函数签名的代码执行就会出错。

import time
import functools
def display_time(func):
    @functools.wraps(func)
    def wrapper(*args,**kw):
        t1 = time.time()
        result = func(*args,**kw)
        t2 = time.time()
        print(t2-t1)
        return result
    return wrapper
def is_Prime(num):
    pass

@display_time
def find_Prime(n):
    count = 0
    for i in range(2,n):
        if is_Prime(i):
            count += 1
    return count
count_prime=find_Prime(10000)
print(count_prime)
find_Prime      # <function find_Prime at 0x00000232638FBAF8>

装饰器有参数时:需要编写一个返回decorator的高阶函数。(三层嵌套)              

def log(text):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator
@log('execute')
def now():
    print('2015-3-25')
now()  #now = log('execute')(now) = decorator(now) = wrapper
# execute now():
# 2015-3-25

装饰器含参数(两层嵌套)                                                            

运行逻辑有错:

def log(text):
    def wrapper(func):
        print('%s %s():' % (text, func.__name__))
        return func()
    return wrapper
@log('execute')
def now():
    print('2021')
print(now)
# execute now():
# 2021
# None

正确的两层嵌套:

def log(text):
    def wrapper(func):
        print('%s %s():' % (text, func.__name__))
        return func
    return wrapper
@log('execute')
def now():
    print('2021')
print(now)
now()
# execute now():
# <function now at 0x0000023DE4AD0A68>
# 2021

错误原因:在执行@log('execute'def now():时,等价于执行了log('execute')(now) = wrapper(now) = now(),因此会在这两部分直接输出execute now():2021,而运行print(now)时,由于now变量未定义,故输出为None

修改后在执行@log('execute'def now():时,等价于执行了log('execute')(now) = wrapper(now) = now,因此会输出execute now():,而后运行print(now),输出<function now at 0x0000023DE4AD0A68>,而后运行now(),输出2021

装饰器有可变参数时:                                                                

def log(*text):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            if len(text)==0:
                print('no text %s():' % (func.__name__))
                return func(*args, **kw)
            for i in text:
                print('%s %s():' % (i, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator

@log()
def now(x,y):
return x + y
print(now)  #<function now at 0x000001B0A029FAF8>
#也就是<function log.<locals>.decorator.<locals>.wrapper at 0x000001DABDE4FAF8>
print(now(1,2))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值