一、闭包

概念:如果在一个函数中,定义了另外一个函数,并且那个函数使用了外面函数的变量,并且外面那个函数返回了里面这个函数的引用,那么称为里面的这个函数为闭包。

    def greet(name):
        def say_hello():
            print('hello my name is %s' % name)
        return say_hello

 

如果想要在闭包中修改外面函数的变量,这时候应该使用nonlocal关键字,来把这个变量标识为外面函数的变量:

def greet(name):
    def say_hello():
        nonlocal name
        name += 'hello'
        print('hello my name is %s' % name)
    return say_hello

 

案例

闭包设计一个计算器

    def calculator(option):
        operator = None
        if option == 1:
            def add(x,y):
                return x+y
            operator = add
        elif option == 2:
            def minus(x,y):
                return x-y
            operator = minus
        elif option == 3:
            def multiply(x,y):
                return x*y
            operator = multiply
        else:
            def divide(x,y):
                return x/y
            operator = divide

        return operator

    cal = calculator(1)
    ret = cal(1,2)
    print(ret)

 

 

二、装饰器

装饰器利用了函数也可以作为参数传递和闭包的特性,可以让我们的函数在执行之前或者执行之后方便的添加一些代码。

 

理解:网站开发中,经常会碰到一些页面是需要登录后才能访问的。那么如果每次都在访问的视图函数中判断,很麻烦,而且代码很难维护,因此这时候我们可以采用装饰器来解决。

被装饰的函数不带有参数

def login_required(func):

    def wrapper():
        if user['is_login'] == True:
            func()
        else:
            print('跳转到登录页面')

    return wrapper

@login_required                  #相当于 login_required(edit_user)
def edit_user():
    print('用户名修改成功')



@login_required                  #相当于 login_required(add_article)
def add_article():
    print('添加文章成功')

edit_user()                      #相当于 login_required(edit_user)()
add_article()                    #相当于 login_required(add_article)()

首先,我们把这个判断用户是否登录的逻辑就已经单独抽出放到login_required这个装饰器中了,以后如果某个函数想要做登录限制,那么就先传给login_required这个装饰器就可以了。

然后,在函数定义开头的地方,通过@装饰器名就可以了,这样在调用edit_useredit_user的时候,就不需要手动的传给login_required了。

其中login_required是装饰器;edit_user和edit_user是被装饰函数。

 

被装饰的函数带有参数

def login_required(func):

    def wrapper(*args,**kwargs):
        if user['is_login'] == True:
            func(*args,**kwargs)
        else:
            print('跳转到登录页面')

    return wrapper

@login_required
def edit_user(username):
    print('用户名修改成功:%s'%username)

edit_user()

采用*args**kwargs组合起来,包含所有的参数

 

带参数的装饰器:

def login_required(site='front'):
    def outter_wrapper(func):
        def inner_wrappre(*args,**kwargs):
            if site == 'front':
                if user['is_login'] == True:
                    print('进入到前台了')
                    func(*args,**kwargs)
                else:
                    print('跳转到前台的首页')
            else:
                if user['is_login'] == True:
                    print('进入到后台了')
                    func(*args,**kwargs)
                else:
                    print('跳转到后台的首页')
        return inner_wrappre
    return outter_wrapper


@login_required('front')
def edit_user():
    print('用户名修改成功')

@login_required('front')
def add_article():
    print('添加文章成功')

edit_user()

在原有的装饰器上在加一层闭包 outter_wrapper。

 

wraps装饰器:

采用之前的装饰器,会让我们的函数失去一些属性,比如__name__,这样在一些代码中会产生错误,比如Flask开发中。如果我们想要用装饰器,并且仍想保留函数的一些属性,比如__name__,那么可以使用wraps装饰器

from functools import wraps

def login_required(func):
    @wraps
    def wrapper(*args,**kwargs):
        if user['is_login'] == True:
            func(*args,**kwargs)
        else:
            print('跳转到登录页面')

    return wrapper

@login_required
def edit_user(username):
    print('用户名修改成功:%s'%username)


edit_user()
print(edit_user.__name__)
# 打印edit_user