开放---封闭原则:在软件上线后,对修改是封闭的(包括修改源代码以及调用方式),对新功能的扩展是开放的
装饰器:在不修改源代码及其调用方式的前提下,为被装饰对象添加新功能
def home(): print('进入观影模式...') home()
对于上述函数,现在需要给他加一个验证功能.
方式一:
def home(): uname = input('输入用户名:') upwd = input('输入密码:') if uname == 'bob' and upwd == '123456': print('进入观影模式...') else: print('密码错误!') home()
但是这改变了源代码,是违背封闭原则的...那么
方式二:
def home(): print('进入观影模式...') uname = input('输入用户名:') upwd = input('输入密码:') if uname == 'bob' and upwd == '123456': home() else: print('密码错误!')
这个方法虽然没有违背开放---封闭原则, 但是如果是很多地方需要添加认证功能呢,这种方法无疑会造成代码冗余,
那么任何解决冗余呢,当然是函数啦,而且可读性好,又易于扩展
方式三:
def home(): print('进入观影模式...') def login(fun): uname = input('输入用户名:') upwd = input('输入密码:') if uname == 'bob' and upwd == '123456': fun() else: print('密码错误!') login(home)
但是问题又来了,这里虽然解决了代码冗余,而且也没有修改源代码,但是以前进入观影模式的接口是home(),现在变成了
login(),这无疑是改变了调用接口的...
#你可以这样... def home(): print('进入观影模式...') def login(fun): uname = input('输入用户名:') upwd = input('输入密码:') if uname == 'bob' and upwd == '123456': return fun else: print('密码错误!') home=login(home) home() #也可以这样... def home(): print('进入观影模式...') def outter(func): def login(): uname = input('输入用户名:') upwd = input('输入密码:') if uname == 'bob' and upwd == '123456': return func() else: print('密码错误!') return login home=outter(home) home()
but,万一home()函数是有参函数呢,第一种方法显然不可能了,so
def home(name): print('您好%s\n现在开始进入观影模式...'%name) def outter(func): def login(*args,**kwargs): uname = input('输入用户名:') upwd = input('输入密码:') if uname == 'bob' and upwd == '123456': return func(*args,**kwargs) else: print('密码错误!') return login home=outter(home) home('bob')
这个方法应用了闭包函数,在调用外部函数outter的时候,home这个变量名是指向内部函数login的内存地址 ,只有当加上(),才会执行login的函数体代码,并且,无论调用home()时实参是什么类型,什么值,*以及**都能原封不动的将其传给login内的func()函数--->实现了在遵循开放---封闭的原则下,又没有代码冗余
而装饰器正是运用了这一点:
def outter(func): def login(*args,**kwargs): uname = input('输入用户名:') upwd = input('输入密码:') if uname == 'bob' and upwd == '123456': return func(*args,**kwargs) else: print('密码错误!') return login @outter def home(name): print('您好%s\n现在开始进入观影模式...'%name) home('bob')
装饰器语法糖:@代替了home=outter(home),解释器将@下面的函数名作为参数传给@后面的函数,并将返回值赋值给home这个变量名,是其指向home函数体的内存地址
注:装饰器要在被装饰者之上,而且嵌套装饰是有顺序的
有参装饰器:
def wapper(way): def outter(func): def login(*args,**kwargs): uname = input('输入用户名:') upwd = input('输入密码:') if uname == 'bob' and upwd == '123456': if way == 'QQ': print('QQ登录...') return func(*args,**kwargs) else: print('密码错误!') return login return outter @wapper('QQ') def home(name): print('您好%s\n现在开始进入观影模式...'%name) home('bob')