在学习python的时候,三大“名器”对没有其他语言编程经验的人来说,应该算是一个小难点,本次博客就博主自己对装饰器、迭代器和生成器理解进行解释。

为什么要使用装饰器

       什么是装饰器?“装饰”从字面意思来谁就是对特定的建筑物内按照一定的思路和风格进行美化的一种行为,所谓“器”就是工具,对于python来说装饰器就是能够在不修改原始的代码情况下给其添加新的功能,比如一款软件上线之后,我们需要在不修改源代码和不修改被调用的方式的情况下还能为期添加新的功能,在python种就可以用装饰器来实现,同样在写代码的时候也要考虑到后面的可扩展性,下面我们来看一步一步的看一下python的装饰器。

 

一个简单例子引入无参装饰器

先来看简单的几行代码,代码的运行结果是先睡2秒,再打印"hello boy!":

import time
def  foo():
    """打印"""
    time.sleep(2)
    print("Hello boy!")
foo()

我们现在我们需要为其添加一个程序计时功能,但是不能修改原始的代码:

import time
def timmer(func):
    def wrapper():
        """计时功能"""
        time_start=time.time()
        func()
        time_end=time.time()
        print("Run time is %f "%(time_end-time_start))
    return wrapper
def  foo():
    """打印"""
    time.sleep(2)
    print("Hello boy!")
foo=timmer(foo)
foo()
#运行结果
Hello boy!
Run time is 2.000446 

看!我们没有修改原来的代码就实现了这个功能,因为函数也是对象,所以能够将函数foo当做参数传递给了函数timmer。

在python中,有个更简洁的方式来取代foo=timmer(foo),使用@timmer这种方式,这个在python中被称为语法糖

import time
def timmer(func):
    def wrapper():
        """计时功能"""
        time_start=time.time()
        func()
        time_end=time.time()
        print("Run time is %f "%(time_end-time_start))
    return wrapper
@timmer      #等于  foo=timmer(foo)
def  foo():
    """打印"""
    time.sleep(2)
    print("Hello boy!")
foo()

下面我们来一步一步的分析函数的执行过程:

1.导入time模块

import time

2.定义函数timmer,定义函数并不会执行函数内的代码

def timmer(func):

3.调用装饰器,相当于foo=timer(foo),就是把函数foo作为参数穿给了函数timmer

@timmer

4.运行函数timmer,接受了参数   func=foo

def timmer(func):

5.在函数timmer内,定义了函数wrapper,wrapper函数内部代码也不执行,然后将函数wrapper作为返回值返回

return wrapper

6.将返回值赋值给了foo,在第3步中,foo=timmer(foo),还记吧

@timmer      #等于  foo=timmer(foo)

7.运行函数foo(),但是这里的函数已经不是原来的那个函数了,可以打印foo,对的,因为之前我们将wrapper作为返回值传给了foo,所以在这里执行foo就是在执行wrapper了,为了再确定这一点你也可打印wrapper,它们的内存地址相同,所以都是指向同一个地址空间:

<function timmer.<locals>.wrapper at 0x00000180E0A8A950>   #打印foo的结果
<function timmer.<locals>.wrapper at 0x000001F10AD8A950>   #打印wrapper的结果
foo()

8.运行函数wrapper,记录开始时间,执行函数func,在第4步的时候,func被foo赋值,运行func就是在运行原函数foo,睡2秒,打印字符串;

time_start=time.time()
    time.sleep(2)
    print("Hello boy!")

9.记录结束时间,打印运行时间,程序结束。

Hello boy!
Run time is 2.000161 

 

有参装饰器

 在前面的例子中,原函数没有参数,下面的来看一个当原函数有参数,该怎么修改装饰器函数呢?

import time
def timmer(func):
    def wrapper(*args,**kwargs):
        """计时功能"""
        start_time=time.time()
        res=func(*args,**kwargs)
        end_time=time.time()
        print("Run time is %f"%(end_time-start_time))
        return res
    return wrapper
@timmer    
def  my_max(x,y):
    """返回两个值的最大值"""
    res=x if x > y else y
    time.sleep(2)
    return res
res=my_max(1,2)
print(res)
#运行结果
Run time is 2.000175
2

 当原函数有需要传入参数的时候,在这个例子my_max有两个位置形成需要传入参数,只需要在wrapper上添加两个形参,本例子中使用了可变参数(*args,**kwargs)也是可以的,这是@timmer就等于my_max(1,2)=timmer(my_max)

 下面我们来看一个有参数的装饰器:

def auth(filetype):
    def auth2(func):
        def wrapper(*args,**kwargs):
            if filetype == "file":
                username=input("Please input your username:")
                passwd=input("Please input your password:")
                if passwd == '123456' and username == 'Frank':
                    print("Login successful")
                    func()
                else:
                    print("login error!")
            if filetype == 'SQL':
                print("No SQL")
        return wrapper
    return auth2
@auth(filetype='file')  #先先返回一个auth2 ==》@auth2  ==》 index=auth2(index) ==》 index=wrapper
def index():
    print("Welcome to China")
index()

如果装饰器本身有参数,就需要多一层内嵌函数,下面我们一步一步分析执行流程:

1.定义函数auth

def auth(filetype):

 2.调用解释器,首先要运行函数auth(filetype='file')

@auth(filetype='file')

 3.运行函数auth,定义了一个函数auth2,并作为返回值返回,那么这个@auth(filetype='file')就等同于@auth2,等同于index=auth2(index)

def auth(filetype):
    def auth2(func):
        def wrapper(*args,**kwargs):
        return wrapper
    return auth2

 4.auth2(index)执行,func=index,定义函数wrapper,并返回之,这时候index其实就是等于wrapper了

def wrapper(*args,**kwargs):
return wrapper

 5.当运行index,即运行wrapper,运行函数内部代码,filetype=="file",提示用户输出用户名和密码,判断输入是否正确,如果正确,则执行函数func(),等于执行原来的index,打印

if filetype == "file":
                username=input("Please input your username:")
                passwd=input("Please input your password:")
                if passwd == '123456' and username == 'Frank':
                    print("Login successful")
                    func()

 6.运行结果测试

Please input your username:Frank
Please input your password:123456
Login successful
Welcome to China

 装饰器也是可以被叠加的:

import time
#
def timmer(func):
    def wrapper():
        """计时功能"""
        time_start=time.time()
        func()
        time_end=time.time()
        print("Run time is %f "%(time_end-time_start))
        # print("---",wrapper)
    return wrapper
def auth(filetype):
    def auth2(func):
        def wrapper(*args,**kwargs):
            if filetype == "file":
                username=input("Please input your username:")
                passwd=input("Please input your password:")
                if passwd == '123456' and username == 'Frank':
                    print("Login successful")
                    func()
                else:
                    print("login error!")
            if filetype == 'SQL':
                print("No SQL")
        return wrapper
    return auth2
@timmer
@auth(filetype='file')  #先先返回一个auth2 ==》@auth2  ==》 index=auth2() ==》 index=wrapper
def index():
    print("Welcome to China")
index()

#测试结果
Please input your username:Frank
Please input your password:123456
Login successful
Welcome to China
Run time is 7.966267 

 注释优化

import time
def timmer(func):
    def wrapper():
        """计算程序运行时间"""
        start_time=time.time()
        func()
        end_time=time.time()
        print("Run time is %s:"%(end_time-start_time))
    return wrapper
@timmer
def my_index():
    """打印欢迎"""
    time.sleep(1)
    print("Welcome to China!")
my_index()
print(my_index.__doc__)

#运行结果
Welcome to China!
Run time is 1.0005640983581543:
计算程序运行时间
View Code

相关文章: