柚子快报激活码778899分享:装饰器

http://yzkb.51969.com/

装饰器(Decorator):在代码运行期间动态增加功能的方式,称之为装饰器。

本质上,装饰器就是一个返回函数的高阶函数。函数也是一个对象,而且函数对象可以被赋值给变量,因此通过变量也能调用该函数。

一、装饰器定义

装饰器(Decorator):在代码运行期间动态增加功能的方式,称之为装饰器。

  本质上,装饰器就是一个返回函数的高阶函数。函数也是一个对象,而且函数对象可以被赋值给变量,因此通过变量也能调用该函数。

>>> def now():

... print('2018-3-20')

...

>>> f = now

>>> f()

2018-3-20

>>> now.__name__ # 函数对象__name__属性,拿到函数名

'now'

>>> f.__name__

'now'

  定义一个能打印日志的装饰器:

import datetime

def log(func): # 装饰器接受一个函数作为参数,并返回一个函数

def wrapper(*args, **kw):

print('call %s(): ' % func.__name__)

return func(*args, **kw)

return wrapper

@log # 运用@语法把装饰器放置在函数定义处

def now():

print(datetime.datetime.now())

now()

"""

call now():

2018-03-20 23:00:05.201096

"""

  把@log放到now()函数定义处,相当于执行语句now = log(now)。now()函数仍然存在,只是现在同名的now变量指向了新的函数log(now)。

  因此在调用now()时,将执行log(now),在log()函数中返回的wrapper()函数。

  wrapper()函数的参数定义是(*args, **kw),可以接受任意参数的调用。

二、装饰器详解

  一家视频网站有以下几个板块:

def home():

print("首页".center(40, '-'))

def america():

print("欧美专区".center(40, '-'))

def japan():

print("日韩专区".center(40, '-'))

def henan():

print("河南专区".center(40, '-'))

  上线初期所有视频都是免费观看,用户量增多后,准备对一些板块收费。因此需要添加登陆认证功能。

user_status = False # 用户登录就把这个改为True

def login():

_username = 'alex' # 假装这是DB里存的用户信息

_password = 'abc!23' # 假设这是DB里存的用户信息

global user_status

if user_status is False: # 用户状态为True时可以访问视频

username = input("user:")

password = input("password:")

if username == _username and password == _password:

print("welcome login....")

user_status = True

else:

print("用户已经登录,验证通过!")

def home():

print("首页".center(40,'-'))

def america():

login() # 登陆认证

print("欧美专区".center(40,'-'))

def japan():

print("日韩专区".center(40,'-'))

def henan():

login() # 登陆认证

print("河南专区".center(40,'-'))

henan()

america()

  上述代码虽然实现了登陆认证功能,但是却违反了软件开发中一个重要原则:“开发—封闭”原则。规定代码不允许被修改但可以被扩展。

  封闭:已实现的功能代码块不应该被修改。

  开放:对现有功能的扩展开放。

  高阶函数:把一个函数当做一个参数传给另一个函数。应用该知识点改写如下:

user_status = False #用户登录了就把这个改成True

def login(func): #把要执行的模块从这里传进来

_username = "alex" #假装这是DB里存的用户信息

_password = "abc!23" #假装这是DB里存的用户信息

global user_status

if user_status == False:

username = input("user:")

password = input("pasword:")

if username == _username and password == _password:

print("welcome login....")

user_status = True

else:

print("wrong username or password!")

if user_status == True:

func() # 看这里看这里,只要验证通过了,就调用相应功能

def home():

print("---首页----")

def america():

#login() #执行前加上验证

print("----欧美专区----")

def japan():

print("----日韩专区----")

def henan():

#login() #执行前加上验证

print("----河南专区----")

home()

login(america) #需要验证就调用 login,把需要验证的功能 当做一个参数传给login

# home()

# america()

login(henan)

  以上改写也不好,修改了调用方式,需要认证的模块都需要修改调用方式。需要不改变原功能代码,又不改变原有调用方式,还能加上认证的代码改写。

user_status = False

def login(func):

_username = "alex"

_password = "abc!23"

global user_status

if user_status == False:

username = input("user:")

password = input("password:")

if username == _username and password == _password:

print("welcome login...")

user_status = True

else:

print("wrong username or password!")

if user_status == True:

func()

home()

henan = login(henan)

america = login(america)

henan()

"""

-------------------首页-------------------

user:alex

password:abc!23

welcome login...

------------------河南专区------------------

------------------欧美专区------------------

Traceback (most recent call last):

File "/Users/.../装饰器.py", line 91, in

henan()

TypeError: 'NoneType' object is not callable

"""

  上述改写不改变调用方式也不违反“开放——封闭”,但是america = login(america)时就会把america执行。

  可以运用嵌套函数,在login中再定义一层,使得america = login(america)只调用到外层,不触发认证。login只返回里层函数的函数名,下次执行america()时再调用里层函数。

user_status = False

def login(func):

def inner(): #添加一层inner,装饰器套路

_username = "alex"

_password = "abc!23"

global user_status

if user_status == False:

username = input("user:")

password = input("password:")

if username == _username and password == _password:

print("welcome login...")

user_status = True

else:

print("wrong username or password!")

if user_status:

func() # henan()——老的河南函数

return inner # 加括号执行,不加括号返回内存地址

home()

henan = login(henan)

america = login(america)

henan()

"""

-------------------首页-------------------

user:alex

password:abc!23

welcome login...

------------------河南专区------------------

"""

  还可以将上述代码改写,把america = login(america)改写为@login,并加在america函数前。

@login # 等价于henan = login(henan)

def hubei():

#login() # 改写删除该行

print("湖北专区".center(40,'-'))

hubei()

"""

user:alex

password:abc!23

welcome login...

------------------湖北专区------------------

"""

三、带参数装饰器

  上述代码要是在调用函数的时候直接传参数是会报错的。因为调用hubei时,实际上是调用login。  hubei = login(hubei),login返回inner内存地址。第二次用户自己调用hubei("3p"),实际上相当于调用的是inner,因此给inner设置参数,可以解决问题。

user_status = False

def login(func):

def inner(*args,**kwargs): # 添加参数eg.3p

_username = "alex"

_password = "abc!23"

global user_status

if user_status == False:

username = input("user:")

password = input("password:")

if username == _username and password == _password:

print("welcome login...")

user_status = True

else:

print("wrong username or password!")

if user_status:

# func(args,kwargs) # 这样写是固定两个参数

func(*args,**kwargs) # henan() 适配任意多个参数

return inner # 加括号执行,不加括号返回内存地址

def home():

print("首页".center(40,'-'))

def america():

print("欧美专区".center(40,'-'))

@login

def japen(name):

print("日韩专区".center(40, '-'), name)

@login # henan = login(henan)

def henan(style):

print(("河南专区" + style).center(40, '-'))

home()

henan('3p')

japen('oddry')

"""

-------------------首页-------------------

user:alex

password:abc!23

welcome login...

-----------------河南专区3p-----------------

------------------日韩专区------------------ oddry

"""

 

 

柚子快报激活码778899分享:装饰器

http://yzkb.51969.com/

相关链接

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。