闭包:
如果在一个外函数中,定义了一个内函数,并且内函数使用了外函数的变量,且外函数返回了内函数的引用,那么内函数称为闭包。例如:
def greet(name):
def say_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)
nonlocal
关键字:
如果想要在闭包中修改外函数的变量,这时候应该使用nonlocal
关键字,来把这个变量标识为外函数的变量:
以下代码修改了name
,但没有使用nonlocal
关键字,会报错:
def greet(name):
def say_hello():
name += 'hello'
print('hello my name is %s' % name)
return say_hello
以下代码修改了name
,使用了nonlocal
关键字,不会报错:
def greet(name):
def say_hello():
nonlocal name
name += 'hello'
print('hello my name is %s' % name)
return say_hello
装饰器
装饰器利用了函数也可以作为参数传递和闭包的特性,可以让我们的函数在执行之前或者执行之后方便的添加一些代码。这样就可以做很多事情了,比如@classmethod
装饰器可以将一个普通的方法设置为类方法,@staticmethod
装饰器可以将一个普通的方法设置为静态方法等。所以明白了装饰器的原理以后,我们就可以自定义装饰器,从而实现我们自己的需求。
理解:
拿网站开发的例子来说。网站开发中,经常会碰到一些页面是需要登录后才能访问的。那么如果每次都在访问的视图函数中判断,很麻烦,而且代码很难维护,示例如下:
user = {
"is_login": False
}
def edit_user():
if user['is_login'] == True:
print('用户名修改成功')
else:
print('跳转到登录页面')
def add_article():
if user['is_login'] == True:
print('添加文章成功')
else:
print('跳转到登录页面')
edit_user()
add_article()
以上现在是只有两个函数,如果以后网站越来越大,需要做判断的地方越来越大,那么这种判断将显得非常低效并且难以维护,因此这时候我们可以采用装饰器来解决:
def edit_user():
print('用户名修改成功')
@login_required
def add_article():
print('添加文章成功')
def login_required(func):
def wrapper():
if user['is_login'] == True:
func()
else:
print('跳转到登录页面')
return wrapper
edit_user() == login_required(edit_user)() == wrapper()
add_article() == login_required(add_article)() == wrapper()
被装饰的函数带有参数:
也有可能被装饰的函数中,参数是不固定的,因此这时候写一个固定的参数,不能成为一个普遍的装饰器,这时候可以采用*args
和**kwargs
组合起来,包含所有的参数:
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()
带参数的装饰器:
装饰器也可以传递参数。只不过如果给装饰器传递参数了,那么就要在这个装饰器中写两个方法了,示例代码如下:
def login_required(site='front'):
def outter_wrapper(func):
def inner_wrapper(*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()
wraps装饰器:
采用之前的装饰器,会让我们的函数失去一些属性,比如__name__
,这样在一些代码中会产生错误,比如Flask
开发中。如果我们想要用装饰器,并且仍想保留函数的一些属性,比如__name__
,那么可以使用wraps
装饰器,以下是没有使用wraps
装饰器的代码:
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()
print(edit_user.__name__)
# 打印wrapper
以下再使用wraps
装饰器,来优化代码:
from functools import wraps
def login_required(func):
@wraps(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()
print(edit_user.__name__)
# 打印edit_user