在Python中,当我们需要对函数进行内部引用时,使用@语法糖创建装饰器decorator可以让程序更加简洁,可读性更强。
不用装饰器
比如以下用户名校验函数
def login_user(username, password):
print('Login success')
def user_check(func):
def wrapper(username, password):
if username != 'root':
raise Exception("Permission denied")
elif password != 'admin':
raise Exception("Pass incorrect")
else:
return func(username, password)
return wrapper
要想完成校验,我们需要采用如下调用方式:
login = user_check(login_user)
login('root', '123')
获得结果:
Traceback (most recent call last):
File "/home/silan/test.py", line 16, in <module>
login('root', '123')
File "/home/silan/test.py", line 10, in wrapper
raise Exception("Pass incorrect")
Exception: Pass incorrect
可以发现按照一般的调用方式,我们需要在正是调用前添加
login = user_check(login_user)
这和我们使用Python的一般习惯不符,所以此时我们可以使用装饰器来简化这一过程。
使用装饰器
我们对代码进行如下改动:
def user_check(func):
def wrapper(username, password):
if username != 'root':
raise Exception("Permission denied")
elif password != 'admin':
raise Exception("Pass incorrect")
elif len(password) < length:
raise Exception("Password should have at least {} characters".format(length))
else:
return func(username, password)
return wrapper
@ user_check #添加装饰器,@后跟随需要调用的函数名
def New_login_user(username, password):
print('Pass')
在正式调用时我们只需要运行
New_login_user('root', 'admin') # 结果:Pass
除此以外,定义装饰器时也可以传递参数。
还是使用之前的例子,我们添加一个密码长度验证功能,同时假设对应不同的场景,需要设定不同的密码长度。
def check(length):# length为调用时需要输入的长度
def user_check(func):
def wrapper(username, password):
if username != 'root':
raise Exception("Permission denied")
elif password != 'admin':
raise Exception("Pass incorrect")
elif len(password) < length:# 添加长度验证
raise Exception("Password should have at least {} characters".format(length))
else:
return func(username, password)
return wrapper
return user_check
@ check(6)# 正式调用时设定长度为6
def New_login_user(username, password):
print('Pass')
同样直接执行
New_login_user('root', 'admin')
# Exception: Password should have at least 6 characters
Python内置装饰器
除了自定义装饰器,Pyhton还有一些内置的装饰器,可以直接使用。
@property | 将方法转化为属性,只能读不能写,可以通过调用属性的方式来调用方法 |
@.setter | 与@property配合,出现在@property后,用于指定可以修改@property属性值的方法 |
@.deleter | 与@property配合,出现在@property后,用于指定可以清除@property属性值的方法 |
@classmethod | 类方法,可以通过这个方法传递类的属性和方法(不能传实例的属性和方法) |
@staticmethod | 静态方法,与类方法类似,但是方法体中不能使用类或实例的任何属性和方法。静态方法是个独立的、单纯的函数,它仅仅托管于某个类的名称空间中,便于使用和维护。 |
举个例子
class Student():
def __init__(self):
self._score = None
@property
def score(self):
return self._score
@score.setter
def score(self, value):
self._score = value
@score.deleter
def score(self):
del self._score
if __name__ == "__main__":
s = Student()
print (s.score) # 通过@property读取self._score,结果:None
s.score = 60 # 通过@.setter设置self._score
print (s.score) # 结果:60
del s.score # # 删除self._score属性
class Date(object):
day = 0
month = 0
year = 0
def __init__(self, year=0, month=0, day=0):
self.day = day
self.month = month
self.year = year
@classmethod # 第一个参数必须是当前类对象,该参数名一般约定为“cls”,通过它来传递类的属性和方法(不能传实例的属性和方法)
def from_string(cls, date_as_string):
year, month, day = date_as_string.split('-')
date = cls(year, month, day)
return date
@staticmethod
def is_date_valid(date_as_string):
year, month, day = date_as_string.split('-')
return int(year) <= 3999 and int(month) <= 12 and int(day) <= 31
date1 = Date.from_string('2012-05-10') # 使用类方法
print(date1.year, date1.month, date1.day) # 2012 05 10
is_date = Date.is_date_valid('2012-09-18') # 使用静态方法判断日期格式
print(is_date) # 格式正确 返回True