写Python不用装饰器的话怎么装逼呢??
一、为什么要用装饰器(decorator)
以一个需求来说明:比如要判断一个数字是否是质数,可以给出如下方法:
def is_prime(num):
if num<2:
return False
elif num==2:
return True
else:
for i in range(2,num):
if num%i==0:
return False
return True
追加需求:如果是质数的话,请将质数打印,范围是1-10000,可以追加代码如下:
def is_prime(num):
if num<2:
return False
elif num==2:
return True
else:
for i in range(2,num):
if num%i==0:
return False
return True
def prime_nums():
for i in range(2,10001):
if is_prime(i)
print(i)
追加需求:要求打印程序运行时间,修改代码如下:
# -*- coding:utf-8 -*-
import time
def is_prime(num):
if num<2:
return False
elif num==2:
return True
else:
for i in range(2,num):
if num%i==0:
return False
return True
def prime_nums():
time1 = time.time()
for i in range(2,10001):
if is_prime(i):
print(i)
print(time.time()-time1)
prime_nums()
其实仔细思考一下程序的写法,类似计时这样的需求可能会被重复用到,其代码在整个文件中会被多处用到,而且其代码会与其他的逻辑代码在书写布局上产生交叉,并且像质数判断的函数在其他地方可能变成了别的函数,将其单独分离成一个个方法进行调用也可以,但未免麻烦,这种情况下使用装饰器是一个更加优秀的解决方案。
二、如何使用呢
继续使用上面的例子,先给装饰器如下。装饰器说白了也是一个函数,其参数是另外一个将会调用的函数名。下面这段代码的含义就是将计算运行时间的功能运用到 func这个函数上面。
def display_time(func):
def wrapper(): # wrapper函数表示函数的功能
time1 = time.time()
func() # 被调用的函数,比如可以换成prime_nums
print(time.time()-time1)
return wrapper
那么如果 prime_time() 函数要用到这个装饰器来获取运行时间的话,只要在 prime_time() 函数上方书写“@dispaly_time”即可。装饰器也改变了函数的执行流程,当调用了装饰器的函数开始运行时,并不会直接运行此函数,而是跳转到装饰器运行wrapper()中的内容。修改代码如下:
# -*- coding:utf-8 -*-
import time
def display_time(func):
def wrapper(): # wrapper函数表示函数的功能
time1 = time.time()
func() # 被调用的函数,比如可以换成prime_nums
print(time.time()-time1)
return wrapper
def is_prime(num):
if num<2:
return False
elif num==2:
return True
else:
for i in range(2,num):
if num%i==0:
return False
return True
@display_time
def prime_nums():
for i in range(2,10001):
if is_prime(i):
print(i)
prime_nums()
需求又追加:打印质数的个数。这就需要在 prime_nums() 中返回一个 count。注意为什么不直接打印呢,这是一个编程习惯,一般一个函数实现了某个功能后会将实现结果返回。代码修改如下:
@display_time
def count_prime_nums():
count = 0
for i in range(2,10001):
if is_prime(i):
count = count + 1
return count
count = count_prime_nums()
print(count)
可以看到运行时间打印了,可质数的个数并没有打印,很简单,因为 count_prime_nums() 返回的 count 并没有传到装饰器中,因此可以在装饰器中加一个接收 count_prime_nums() 执行结果的变量,然后 return 该变量即可,注意是return而不是print,这点很重要。代码修改如下,改了下时间输出格式。
# -*- coding:utf-8 -*-
import time
def display_time(func):
def wrapper(): # wrapper函数表示函数的功能
time1 = time.time()
result = func() # 被调用的函数,比如可以换成prime_nums
print("Total time:{:.4} s".format(time.time()-time1)) # 4位小数输出时间
return result
return wrapper
def is_prime(num):
if num<2:
return False
elif num==2:
return True
else:
for i in range(2,num):
if num%i==0:
return False
return True
@display_time
def count_prime_nums():
count = 0
for i in range(2,10001):
if is_prime(i):
count = count + 1
return count
count = count_prime_nums()
print(count)
再次追加需求:计算从 1到 maxnum 的质数个数,而不是简单的10000。其实只要给 count_prime_nums() 加一个参数即可,比如这样:
@display_time
def count_prime_nums(maxnum):
count = 0
for i in range(2,maxnum+1):
if is_prime(i):
count = count + 1
return count
count = count_prime_nums(10000)
print(count)
但如何修改装饰器呢,不修改装饰器程序会报错,因此需要在装饰器的 wrapper() 中追加参数,然后在 func() 中也写上即可。但是但是,这里 count_prime_nums() 只用了一个参数,你可以在 wrapper 中指定,那换了一个别的函数可能会用到不同数量的参数,总不能一个一个地修改吧,这时就体现出了装饰器的另外一个强大之处,可以把 wrapper() 的参数写成 wrapper(*args),表示n个参数,然后 func(*args) 即可。最终代码如下:
# -*- coding:utf-8 -*-
import time
def display_time(func):
def wrapper(*args): # wrapper函数表示函数的功能
time1 = time.time()
result = func(*args) # 被调用的函数,比如可以换成prime_nums
print("Total time:{:.4} s".format(time.time()-time1)) # 4位小数输出时间
return result
return wrapper
def is_prime(num):
if num<2:
return False
elif num==2:
return True
else:
for i in range(2,num):
if num%i==0:
return False
return True
@display_time
def count_prime_nums(maxnum):
count = 0
for i in range(2,maxnum+1):
if is_prime(i):
count = count + 1
return count
count = count_prime_nums(10000)
print(count)
总结:
python 装饰器很强大,可以用来解决实际问题,要经常用。