闭包函数,装饰器

闭包函数

什么是闭包函数

闭包函数把 闭包函数内的变量 + 闭包函数内部的函数, 这两者包裹起来,然后通过返回值的形式返回出来。

  • 定义在函数的内函数
  • 该函数体代码包含对该函数外层作用域中变量的引用
  • 函数外层指的不是全局作用域
def outter():
    x = 10
    def inner():
        print(x)
    return inner

f = outter()  # f=inner
f()
print(f)

# 打印结果:
10
<function outter.<locals>.inner at 0x00000201011A7840>

上述代码中,f是一个全局的名字,但f拿到了inner的内存地址。将一个定义在内部的函数返回出来,在全局拿到f,这个f是全局变量,这样就打破了函数的层级限制,能在任意位置使用内部的函数

闭包函数的应用

以参数的方式传值

import requests

def get(url):
    response = requests.get(url)
    print(response.text)

get('https://www.baidu.com')

这里写了一个爬虫函数,爬取百度的首页。但这样的问题就是每次想爬百度,或者其他网站都要传一堆网址,比较麻烦,所以可以用闭包函数解决。

传值另一方式: 包给函数

import requests

def spider(url):
    def get():
        response = requests.get(url)
        print(response.text)
    return get

baidu = spider('https://www.baidu.com')
baidu()

taobao = spider('https://www.taobao.com')
taobao()

这样就很方便,以后调baidu,直接baidu()就行了

装饰器

什么是装饰器

装饰器就是 为需要装饰的函数新增了一个额外的功能,装饰器的本质就是一个 给函数增加功能的函数。

为什么要装饰器

装饰器,增加功能需要注意以下几点:

  • 不改变原函数的原代码
  • 不改变原函数的调用方式

使用无参装饰器

import time

def index():
    '''被装饰函数'''
    time.sleep(1)
    print('welcome to index')

index()

需要为上面的函数新增加一个功能,能够统计函数运行的时间

在原代码上修改

import time

def index():
    start = time.time()
    time.sleep(1)
    print('welcome to index')
    end = time.time()
    print(f'run time is {end - start}')

index()

这样就违反了不能修改原代码这一原则

import time

def index():
    time.sleep(1)
    print('welcome to index')

start = time.time()
index()
end = time.time()
print(f'run time is {end - start}')

这样写就不是装饰器,因为装饰器是一个函数

利用函数传参方式

import time

def index():
    '''被装饰函数'''
    time.sleep(1)
    print('welcome to index')

def time_count(func):
    start = time.time()
    func()
    end = time.time()
    print(f'run time is {end - start}')

time_count(index)

虽然实现了,但改变了函数调用方式

利用闭包

import time

def index():
    '''被装饰函数'''
    time.sleep(1)
    print('welcome to index')

def deco(func):  # func = index 最初的index
    def time_count():
        start = time.time()
        func()
        end = time.time()
        print(f'run time is {end - start}')
    return time_count

# f = deco(index)   

index = deco(index)   # index = time_count
index()

这样就简单实现了一个装饰器函数,调用index不是调用最初的index了,而是调用time_count函数,但用户不知道,看起来就和原来使用一样

装饰器完善

上述的装饰器,最后调用index()的时候,其实是在调用time_count(),因此如果原始的index()有返回值的时候,time_count()函数的返回值应该和index()的返回值相同,也就是说,我们需要同步原始的index()和time_count()方法的返回值。

import time

def index():
    '''被装饰函数'''
    time.sleep(1)
    print('welcome to index')
    return 1234

def deco(func):
    def time_count():
        start = time.time()
        res = func()
        end = time.time()
        print(f'run time is {end - start}')
        return res

    return time_count

# index = deco(index)   # index = time_count
# index()
res = index()
print(res)

给原始index传参

import time

def index(x):
    '''被装饰函数'''
    time.sleep(1)
    print('welcome to index')
    return 1234

def deco(func):
    def time_count(*args,**kwargs):
        start = time.time()
        res = func(*args,**kwargs)
        end = time.time()
        print(f'run time is {end - start}')
        return res

    return time_count

index = deco(index)   # index = time_count
index(10)

使用装饰器语法糖

import time


def deco(func):
    def time_count(*args, **kwargs):
        start = time.time()
        res = func(*args, **kwargs)
        end = time.time()
        print(f'run time is {end - start}')
        return res

    return time_count


@deco    # 这里就相当于 index = deco(index)
def index(x):
    '''被装饰函数'''
    time.sleep(1)
    print('welcome to index')
    return 1234


index(10)

装饰器模板

def deco(func):
    def wrapper(*args,**kwargs):
        # 这里加功能
        res = func(*args,**kwargs)
        return res
    
    return wrapper

装饰器小练习

# 写一个登录装饰器,装饰猜年龄,登录了才能玩猜年龄

username_list = []

def guess(age):
    print('welcome to guess age')
    age_inp = input('请猜年龄').strip()
    age_inp = int(age_inp)

    if age_inp == age:
        print('bingo')
    elif age_inp < age:
        print('猜小了')
    else:
        print('猜大了')


def login(func):
    def wrapper(*args,**kwargs):
        if username_list:
            print('已登录,请勿重复登录')
            res = func(*args, **kwargs)
            return res

        username_inp = input('请输入用户名:').strip()
        pwd_inp = input('请输入密码:').strip()

        with open('user_info.txt', 'r', encoding='utf-8') as fr:
            for user_info in fr:
                user_info = user_info.strip()
                username, pwd = user_info.split(':')

                if username_inp == username and pwd_inp == pwd:
                    print('登录成功')
                    username_list.append(username_inp)
                    res = func(*args, **kwargs)
                    return res

                else:
                    print('登录失败')

        res = func(*args,**kwargs)
        return res

    return wrapper

guess = login(guess)
guess(19)

有参装饰器

import time

def outter(age):
    def deco(func):
        def wrapper(*args,**kwargs):
            if age >= 18:
                print('成年了')
            else:
                print('未成年')

            start = time.time()
            res = func(*args,**kwargs)
            end = time.time()
            print(f'run time is {end - start}')
            return res

        return wrapper

    return deco

@outter(19)  #   相当于 deco = outter(19)   index = deco(index)
def index():
    '''被装饰函数'''
    time.sleep(1)
    print('welcome to index')

index()
posted @ 2019-09-23 21:50  SetCreed  阅读(442)  评论(0编辑  收藏  举报