大聊Python----装饰器

Posted 追风的小蚂蚁

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了大聊Python----装饰器相关的知识,希望对你有一定的参考价值。

什么是装饰器

  装饰器其实和函数没啥区别,都是用def去定义的,其本质就是函数,而功能就是装饰其他的函数,说白了就是为其他函数提供附加功能

装饰器有什么作用

  比如你是一个公司的员工,你所写的程序里有100个函数,但是你所写的程序都已经上线运行了,突然有一天你的产品经理来找你,让你在咱们的APP上新增一段功能!那你说该怎么做这件事情?问题是你的程序都已经在运行了 ,不能修改你程序的源代码,否则会出现意想不到的效果!所以你想新增一项功能,但是不能修改你的源代码!那该怎么办呢?

  装饰器对待被修饰的函数是完全透明的状态!也就是函数感觉不到装饰器的存在,装饰器没有动函数的源代码,也不影响函数的运行。

先看一下代码

import time

def timmer(func): # 装饰器
    def warpper(*args,**kwargs):
        start_time = time.time() # 开始的时间
        func()
        stop_time = time.time() # 结束的时间
        print(\'the fun run time is %s\'%(stop_time - start_time))
    return warpper

@timmer # 被装饰的函数
def test1():
    time.sleep(3) # 延时3秒
    print("in the test1")

test1()

结果展示:

实现装饰器知识储备

1、函数即“变量

机制

函数调用顺序:其他高级语言类似,Python 不允许在函数未声明之前,对其进行引用或者调用

错误示范:

def foo():
    print \'in the foo\'
    bar()
     
foo()
 
报错:
in the foo
 
Traceback (most recent call last):
  File "<pyshell#13>", line 1, in <module>
    foo()
  File "<pyshell#12>", line 3, in foo
    bar()
NameError: global name \'bar\' is not defined
def foo():
    print \'foo\'
    bar()
foo()
def bar():
    print \'bar\'
     
报错:NameError: global name \'bar\' is not defined

正确示范:(注意,python为解释执行,函数foo在调用前已经声明了bar和foo,所以bar和foo无顺序之分)

def bar():
    print \'in the bar\'
def foo():
    print \'in the foo\'
    bar()
     
foo()
 
def foo():
    print \'in the foo\'
    bar()
def bar():
    print \'in the bar\'
foo()

2、高阶函数

  a、就是把函数名当做实参传给另外一个函数(在不修改被装饰函数源代码的情况下为其增添功能)

  示例:

import time

def bar():
    time.sleep(3)
    print("in the bar")
def test1(func):
    start_time = time.time()
    func()
    stop_time = time.time()
    print("the func run rime is %s"%(stop_time - start_time))

test1(bar)

  结果:

  b、返回值中包含函数名(不修改函数的调用方式)

  示例:

import time

def bar():
    time.sleep(3)
    print("in the bar")
def test2(func):
    print(func)
    return func

bar = test2(bar)
print(bar) # run bar

  效果:

<function bar at 0x00000000007C48C8>
<function bar at 0x00000000007C48C8>

3、嵌套函数

局部作用域和全局作用域的访问顺序

x=0
def grandpa():
    # x=1
    def dad():
        x=2
        def son():
            x=3
            print(x)
        son()
    dad()
grandpa()

显示效果为:

3


高阶函数 + 嵌套函数 ==》 装饰器

先看个例子

import time
def timer(func):
    def deco():
        start_time = time.time()
        func()
        stop_time = time.time()
        print("the func\'s run time is %s "%(stop_time - start_time))
    return deco

@timer
def test1():
    time.sleep(3)
    print("the test1 is running!")

@timer
def test2():
    time.sleep(3)
    print("the test2 is running!")

test1()
test2()

结果显示:

the test1 is running!
the func\'s run time is 3.000171422958374
the test2 is running!
the func\'s run time is 3.000171661376953

若想给test2传递参数,如下例,该怎么做呢?

import time
def timer(func):
    def deco():
        start_time = time.time()
        func()
        stop_time = time.time()
        print("the func\'s run time is %s "%(stop_time - start_time))
    return deco

@timer
def test1():
    time.sleep(3)
    print("the test1 is running!")

@timer
def test2(name):
    time.sleep(3)
    print("the test2 is running!",name)

test1()
test2("alex")

通过执行,会出现下面的错误

意思是说,deco()缺少了一个元素!

那该怎么解决这个问题呢?

咱们先来捋顺下思路!

通过@timer可知test2() =timer(test2) = deco ,test2(name) = deco(name)

所以可以看出要给deco传递实参,所以做了下面

def timer(func):
    def deco(name):
        start_time = time.time()
        func(name)
        stop_time = time.time()
        print("the func\'s run time is %s "%(stop_time - start_time))
    return deco

通过执行,会看到下面的结果!

这回好了,test2不出错了,反而test1报错了,那个该怎么办呢?

看下test1出错的原因是test1的deco缺少了一个实参,那么问题来了,test1该怎么处理呢?

其实很简单,使用*args,和**kwargs尽可以完美的解决这个问题!

现在来看看经过改正的程序

import time
def timer(func):
    def deco(*args,**kwargs): 
        start_time = time.time()
        func(*args,**kwargs)
        stop_time = time.time()
        print("the func\'s run time is %s "%(stop_time - start_time))
    return deco

@timer
def test1():
    time.sleep(3)
    print("the test1 is running!")

@timer
def test2(name):
    time.sleep(3)
    print("the test2 is running!",name)

test1()
test2("alex")

结果显示:

the test1 is running!
the func\'s run time is 3.000171661376953
the test2 is running! alex
the func\'s run time is 3.000171422958374

装饰器之高潮

进入高潮之前,我们先来点前戏

先看下面的代码

import time
user , passwd = "sutaoyu" , "sutaoyu01"

def auth(func):
    def wrapper(*args,**kwargs):
        username = input("Username").strip()
        password = input("Password").strip()
        if user == username and passwd == password:
            print("\\033[32;1mUser has passed authentication\\033[0m")
            func(*args,**kwargs)
        else:
            exit("\\033[31;1mInvalid username and password\\033[0m")
    return wrapper

@auth
def index():
    print("welcome to index Page!")

@auth
def home():
    print("welcome to index Home!")

@auth
def bbs():
    print("welcome to index BBS!")


index()
home()
bbs()

其输出的结果为:

输入正确时:

输入错误时:

 

现在当我们把前面的代码稍微改一下,装饰器代码不动,只改变下面两个地方

def index():
    print("welcome to index Page!")
    return "Page"

print(index())

此时看输出的结果:

会发现无结果并为None,那是因为什么呢?

因为装饰器里的wrapper没有返回值所以,我们给他提供返回值即可!

def auth(func):
    def wrapper(*args,**kwargs):
        username = input("Username:").strip()
        password = input("Password:").strip()
        if user == username and passwd == password:
            print("\\033[32;1mUser has passed authentication\\033[0m")
            return func(*args,**kwargs)
        else:
            exit("\\033[31;1mInvalid username and password\\033[0m")
    return wrapper

运行下程序,看看结果

可以看出,问题已经解决!

现在高潮部分即将来临:

我能不能让我的home在认证的时候用本地的认证,但是bbs认证的时候用远程的ldap?

答案是肯定的!

先看下代码!

import time
user , passwd = "123" , "123"

def auth(auth_type):
    print("auth_typr:",auth_type)
    def outer_wrapper(func):
        def wrapper(*args,**kwargs):
            print("wrapper func args:",*args,**kwargs)
            if auth_type == "local":
                username = input("Username:").strip()
                password = input("Password:").strip()
                if user == username and passwd == password:
                    print("\\033[32;1mUser has passed authentication\\033[0m")
                    func(*args,**kwargs)
                else:
                    exit("\\033[31;1mInvalid username and password\\033[0m")
            elif auth_type == "ldap":
                print("搞毛啊!!!!!")
        return wrapper
    return outer_wrapper

# @auth
def index():
    print("welcome to Index page!")
    return "Page"

@auth(auth_type =  "local")
def home():
    print("welcome to Home page!")

@auth(auth_type = "ldap")
def bbs():
    print("welcome to BBS page!")


index()
home()
bbs()

运行的结果为:

可以看出,我们的认真已经成功!

 

以上是关于大聊Python----装饰器的主要内容,如果未能解决你的问题,请参考以下文章

大聊Python----迭代器

Python面向对象学习之八,装饰器

大聊Python----协程

大聊Python----进程和线程

[TimLinux] Python 装饰器

python装饰器