装饰器:本质是函数,用于装饰其他函数,在不改变其他函数的调用和代码的前提下,增加新功能

原则:

1.不能修改被装饰函数的源代码

2.不能修改被装饰函数的调用方式

3.装饰函数对于被装饰函数透明

参考如下链接:

http://egon09.blog.51cto.com/9161406/1836763

实现装饰器的需求:

  1. 函数即“变量”,函数名指向内存中的函数体,加()表示调用
  2. 高价函数,将函数当做参数传递给其他函数
  3. 嵌套函数,在函数体内再定义函数

1.调用简单的函数:

import time
def test1():
time.sleep(1)
print('in the test1')
test1() output:
in the test1

2.在不改变原来函数的定义和调用方式,增加计时功能

import time
def timmer(func):
def warpper(*args,**kwargs):
start_time=time.time()
func()
stop_time=time.time()
print('the func run time is %s' %(stop_time-start_time))
return warpper @timmer
def test1():
time.sleep(1)
print('in the test1')
test1() output:
in the test1
the function run time is -1.0007600784301758:
# /usr/bin/env python
# -*- coding: utf-8 -*-
# Author:Jenvid.yang
# def foo():
# print('in the foo')
# bar()
# foo()
# bar() 未定义,肯定会报错 # 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()
# 定义函数的时候,只是用函数名指向内存地址,只要函数执行步骤在函数定义之后即可,各函数间前后关系不影响 # def foo():
# print('in the foo')
# bar()
# foo()
# def bar():
# print('in the bar')

函数名及调用关系

总结1:定义函数的时候,只是用函数名指向内存地址,只要函数执行步骤在函数定义步骤之后即可,各函数间定义先后不影响,与变量定义先后一个道理

高阶函数

1.把一个函数名当做实参传给另外一个函数--实现装饰器:不修改被装饰函数下,给源代码,也就是被装饰函数增加新功能

2.返回值中包含函数名--实现装饰器:不修改函数的调用方式

  • 函数返回实参函数内存地址
def bar():
print('in the bar')
def test1(func):
print(func)
test1(bar)
print('分割线'.center(,'-'))
def test2(func):
return func
fff=test2(bar) # 将内存地址,也就是函数体,也就是test2返回值,赋值给一个变量
fff() # 变量加上(),表示调用
output:
<function bar at 0x7f5fa0a7d730>
-------------分割线--------------
in the bar
  • 高阶函数传参方式实现源代码bar没有被更改,调用方式被更改了
import time
def bar():
time.sleep(1)
print('in the bar')
def test1(func):
start_time=time.time()
func()
stop_time=time.time()
print('the function run time is %s' % (stop_time-start_time))
test1(bar)
output:
in the bar
the function run time is 1.0011107921600342
#被调用函数原来调用方式是:bar(),函数体里面的内容也没有更改
#新的函数是test1,调用的是test1(),改变了调用函数名,如果有生产线上已经在调用bar(),这种情况下意味着所有的调用bar函数的其他函数,都需要将bar改成test1
  • 高阶函数返回值方式实现函数调用方式不更改
import time
def bar():
time.sleep()
print('in the bar')
def test1(func):
start_time=time.time()
return func
stop_time=time.time()
print('the function run time is %s' % (stop_time-start_time))
bar=test1(bar)
bar()
output:
in the bar
# 高阶函数返回内存地址实现调用方式不更改,但函数的新功能又没加上
  • 嵌套函数,在高阶函数下,给新功能函数增加多一层函数嵌套
import time
def bar():
time.sleep(1)
print('in the bar')
def timmer(func):
def test1():
start_time=time.time()
func()
stop_time=time.time()
print('the function run time is %s' % (stop_time-start_time))
return test1 #返回test1的内存地址
bar=timmer(bar) #将bar函数地址作为实参传递给timmer函数的形参func,func将在嵌套函数test1里面进行调用
bar() #此时的bar其实是timmer函数地址,并不是原来的bar
output:
in the bar
the function run time is 1.0010881423950195
  • 语法糖
import time
def timmer(func):
def test1():
start_time=time.time()
func()
stop_time=time.time()
print('the function run time is %s' % (stop_time-start_time))
return test1
# bar=timmer(bar)
@timmer #只需要在被调用函数的前面用关键字符“@装饰器函数”,既可以实现装饰器效果
def bar():
time.sleep(1)
print('in the bar')
bar()
output:
in the bar
the function run time is 1.0011053085327148
import time
def timmer(func):
def warpper(): #一般将装饰器嵌套函数名命名为warpper 据说懂了这个可以解决90%的问题
start_time=time.time()
func()
stop_time=time.time()
print("the function run time is %s:" %(start_time-stop_time))
return warpper
@timmer #这里还是相当于更改被装饰函数的’变量名‘内存地址 bar=timmer(bar)
def bar():
time.sleep(1)
print('in the bar')
bar()
  • 装饰器参数传值-被装饰函数传1个值
import time
def timmer(func):
def warpper(name):
start_time=time.time()
func(name)
stop_time=time.time()
print("the function run time is %s:" %(start_time-stop_time))
return warpper
@timmer
def test1(name):
time.sleep()
print('in the test1 %s',name)
test1('alex')
output:
in the test1 %s alex
the function  run time is -1.0006732940673828:
  • 装饰器参数传值-被装饰函数传2个值
import time
def timmer(func):
def warpper(name,age):
start_time=time.time()
func(name,age)
stop_time=time.time()
print("the function run time is %s:" %(start_time-stop_time))
return warpper
@timmer
def test1(name,age):
time.sleep()
print('in the test1 name is %s,age is %d'%(name,age))
test1('alex',)
output:
in the test1 name is alex,age is 18
the function  run time is -1.000108003616333:
  • 装饰器参数传值-被装饰函数传n个值
import time
def timmer(func):
# print('func timmer')
def wrapper(*args,**kwargs):
print('打印实参:',args,kwargs)
start_time=time.time()
func(*args,**kwargs)
stop_time=time.time()
print("the function run time is %s:" % (stop_time-start_time))
return wrapper
@timmer
# test1=timmer(test1)
def test1(name,age):
time.sleep()
print('in the test1 func, name is %s,age is %d' % (name,age))
test1('alex',) @timmer
# test1=timmer(test1)
def test2(name,age,sex,salary):
time.sleep()
print('in the test1 func, name is %s,age is %d,sex is %s,salary is %d' % (name,age,sex,salary))
test2('alex',,'man',)
output:
打印实参: ('alex', 18) {}
in the test1 func, name is alex,age is 18
the function  run time is 1.0013768672943115:
打印实参: ('alex', 18, 'man', 3000) {}
in the test1 func, name is alex,age is 18,sex is man,salary is 3000
the function  run time is 1.0012915134429932:
  • 试错过程:
  • import time
    def timmer(func):
    def warpper():
    # print('打印实参:',args,kwargs)
    start_time=time.time()
    func()
    stop_time=time.time()
    print("the function run time is %s:" % (stop_time-start_time))
    return warpper
    @timmer #这里相当于test1=timmer(test1)=wrapper,test1加上参数相当于test1(name,age)=wrapper(name,age)
    def test1(name,age):
    time.sleep()
    print('in the test1 name is %s,age is %s'%(name,age))
    test1('alex',)
    输出如下:
    /usr/bin/python3. /root/PycharmProjects/S14/day04/bin/decoreate03.py
    Traceback (most recent call last):
    File "/root/PycharmProjects/S14/day04/bin/decoreate03.py", line , in <module>
    test1('alex',)
    TypeError: warpper() takes positional arguments but were given Process finished with exit code 1
    step1:定义装饰器函数体timmer
    step2:语法糖,调用装饰器函数,准备装饰下一行函数,返回内层嵌套函数变量warpper内存地址
    step3:定义函数体test1
    step4:调用函数test1,将test1作为实参,传递给装饰器timmer
    step5:warpper函数调用,需要讲test1的实参传递给wrapper,由于命名空间关系,test1的参数无法传递到warpper,func也无法获得实参
  • 给包装函数warrper增加形参
  • import time
    def timmer(func):
    def warpper(name,age):
    # print('打印实参:',args,kwargs)
    start_time=time.time()
    func()
    stop_time=time.time()
    print("the function run time is %s:" % (stop_time-start_time))
    return warpper
    @timmer #test1=timmer(test1)
    def test1(name,age):
    time.sleep(1)
    print('in the test1 name is %s,age is %s'%(name,age))
    test1('alex',18)
    输出如下
    /usr/bin/python3.5 /root/PycharmProjects/S14/day04/bin/decoreate03.py
    Traceback (most recent call last):
    File "/root/PycharmProjects/S14/day04/bin/decoreate03.py", line 17, in <module>
    test1('alex',18)
    File "/root/PycharmProjects/S14/day04/bin/decoreate03.py", line 9, in warpper
    func()
    TypeError: test1() missing 2 required positional arguments: 'name' and 'age'
    这个解释很明显,func就是test1,缺少了2个位置参数,把func()改成func(name,age)即正确
  • # /usr/bin/env python
    # -*- coding: utf-8 -*-
    # Author:Jenvid.yang
    import time
    def timmer(func): #func=test2
    def wrapper(*args,**kwargs):
    start_time=time.time()
    func(*args,**kwargs)
    stop_time=time.time()
    print('func run time is %s' %(stop_time-start_time))
    return wrapper @timmer
    def bar():
    time.sleep(1)
    print('in the bar')
    bar()
    @timmer #test2=timmer(test2)=warpper ->test2(name)=warpper(name)
    def test2(name):
    time.sleep(1)
    print('in the test2',name)
    test2('alex')

    课堂分析源码

  • 被装饰函数如果带有返回值的情况
import time
def timmer(func):
# print('func timmer')
def wrapper(*args,**kwargs):
print('打印实参:',args,kwargs)
start_time=time.time()
func(*args,**kwargs)
stop_time=time.time()
print("the function run time is %s:" % (stop_time-start_time))
return wrapper
@timmer
# test1=timmer(test1)
def test1(name,age,address):
time.sleep(1)
print('in the test1 func, name is %s,age is %d,address is %s' % (name,age,address))
test1('alex',18,'nuber001')
# @timmer
# test1=timmer(test1)
def test3(name,age,sex,salary):
time.sleep(1)
# print('in the test1 func, name is %s,age is %d,sex is %s,salary is %d' % (name,age,sex,salary))
return (name,age,sex,salary)
print(test3('alex',18,'man',3000))
输出如下:
/usr/bin/python3.5 /root/PycharmProjects/S14/day04/decoreate02.py
打印实参: ('alex', 18, 'nuber001') {}
in the test1 func, name is alex,age is 18,address is nuber001
the function  run time is 1.0009291172027588:
('alex', 18, 'man', 3000) Process finished with exit code 0
函数test1,没有返回值,装饰后正常输出
函数test2,带有返回值,没有装饰,打印了实参元组
如果test2装饰后呢?
import time
def timmer(func):
# print('func timmer')
def wrapper(*args,**kwargs):
print('打印实参:',args,kwargs)
start_time=time.time()
func(*args,**kwargs)
stop_time=time.time()
print("the function run time is %s:" % (stop_time-start_time))
return wrapper
@timmer
# test1=timmer(test1)
def test1(name,age,address):
time.sleep(1)
print('in the test1 func, name is %s,age is %d,address is %s' % (name,age,address))
test1('alex',18,'nuber001')
@timmer
# test1=timmer(test1)
def test3(name,age,sex,salary):
time.sleep(1)
# print('in the test1 func, name is %s,age is %d,sex is %s,salary is %d' % (name,age,sex,salary))
return (name,age,sex,salary)
print(test3('alex',18,'man',3000))
输出如下:
打印实参: ('alex', 18, 'nuber001') {}
in the test1 func, name is alex,age is 18,address is nuber001
the function  run time is 1.0010955333709717:
打印实参: ('alex', 18, 'man', 3000) {}
the function  run time is 1.0010552406311035:
None
带有返回值的test2被装饰后,变成了None,改变了原函数的输出,这是不允许的 给包装函数中的调用原函数增加赋值和返回值:
import time
def timmer(func):
# print('func timmer')
def wrapper(*args,**kwargs):
print('打印实参:',args,kwargs)
start_time=time.time()
res=func(*args,**kwargs) #用一个变量保存原函数输出值
stop_time=time.time()
print("the function run time is %s:" % (stop_time-start_time))
return res #返回原函数结果给wrapper
return wrapper
@timmer
# test1=timmer(test1)
def test1(name,age,address):
time.sleep(1)
print('in the test1 func, name is %s,age is %d,address is %s' % (name,age,address))
test1('alex',18,'nuber001')
@timmer
# test1=timmer(test1)
def test3(name,age,sex,salary):
time.sleep(1)
# print('in the test1 func, name is %s,age is %d,sex is %s,salary is %d' % (name,age,sex,salary))
return (name,age,sex,salary)
print(test3('alex',18,'man',3000))
输出如下:
打印实参: ('alex', 18, 'nuber001') {}
in the test1 func, name is alex,age is 18,address is nuber001
the function  run time is 1.0010664463043213:
打印实参: ('alex', 18, 'man', 3000) {}
the function  run time is 1.0010428428649902:
('alex', 18, 'man', 3000)
  • 语法糖带参数情况,index使用本地验证,home使用ldap验证,同一个装饰器,如何实现不同的验证方式?
  • def deco(func):
    def wrapper(username,password):
    print('I am 包装1')
    func(username,password)
    print('I am 包装2')
    return wrapper
    @deco(auth_type='local')
    def index(username,password):
    print('welcome to index page',username)
    index('yzw','secret')
    @deco(auth_type='ldap')
    def home(username,password):
    print('welcome to home page',username)
    home('alex','secret')
  • 再嵌套一层函数
  • def auth(auth_type):
    def out_wrapper(func):
    def wrapper(username,password):
    if auth_type == 'local':
    print('via local certification')
    print('I am 包装前')
    func(username,password)
    print('I am 包装后')
    elif auth_type == 'ldap':
    print('嘻嘻嘻嘻')
    func(username, password)
    print('via ldap certfication')
    return wrapper
    return out_wrapper
    @auth(auth_type='local')
    #1.全部不带参数,相当于index=deco(index)
    #2.被装饰函数带参数,相当于index=deco(index)=wrapper ->index(args)=wrapper(args)
    #3.语法糖带参数,就需要多嵌套一层外层函数,将语法糖的参数传递给这一外层函数
    def index(username,password):
    print('welcome to index page <local>',username)
    index('yzw','secret')
    @auth(auth_type='ldap')
    def home(username,password):
    print('welcome to home page <ldap>',username)
    home('alex','secret')
    输出如下:
    /usr/bin/python3.5 /root/PycharmProjects/S14/day04/bin/decorate04.py
    via local certification
    I am 包装前
    welcome to index page <local> yzw
    I am 包装后
    嘻嘻嘻嘻
    welcome to home page <ldap> alex
    via ldap certfication Process finished with exit code 0
  • #/usr/bin/env python
    #-*- coding: utf-8 -*-
    #Author:jenvid.yang
    def auth(auth_type):
    def out_wrapper(func):
    def wrapper(*args,**kwargs):
    if auth_type == 'local':
    print('via local certification')
    func(*args,**kwargs)
    elif auth_type == 'ldap':
    print('via ldap certfication')
    func(*args, **kwargs)
    return wrapper
    return out_wrapper
    @auth(auth_type='local')
    #1.全部不带参数,相当于index=deco(index)
    #2.被装饰函数带参数,相当于index=deco(index)=wrapper ->index(args)=wrapper(args)
    #3.语法糖带参数,就需要多嵌套一层外层函数,将语法糖的参数传递给这一外层函数
    def index(username,password):
    print('welcome to index page <local>',username)
    index('yzw','secret')
    @auth(auth_type='ldap')
    def home(username,password):
    print('welcome to home page <ldap>',username)
    home('alex','secret')

    改成万能参数

  • # /usr/bin/env python
    # -*- coding: utf-8 -*-
    # Author:Jenvid.yang
    import time
    user,passwd='alex','abc123'
    def auth(auth_type):
    print('auth func:',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;1m user has passed authentication \033[0m')
    res = func(*args,**kwargs)
    print('xxxxx')
    return res
    else:
    exit('\033[31;1m invalid username or password \033[0m')
    elif auth_type == 'ldap':
    print('xxxx')
    return wrapper
    return outer_wrapper
    # @auth
    def index():
    print('welcome to index page')
    @auth(auth_type='local') #home=wrapper()
    def home():
    print('welcom to home page')
    return ('from home')
    # 函数结果被改变
    @auth(auth_type='ldap')
    def bbs():
    print('welcome to bbs page') index()
    print(home()) # wrapper()
    bbs()

    课堂分析源码

python3.x 基础三:装饰器的更多相关文章

  1. python3【基础】-装饰器

    要理解充分理解python的装饰器,有充分理解下述三个知识点为前提: python作用域规则 函数即对象 闭包 一.python作用域规则: 首先介绍python中的作用域规则.python的作用域规 ...

  2. 函数基础(三) 装饰器\闭包 day 12

    目录 昨日回顾 可变长参数 * ** 函数对象 函数嵌套 名称空间和作用域 今日内容 闭包函数 装饰器 语法糖 装饰器模板 迭代器 昨日回顾 可变长参数 * *形参:接受多余的位置实参 *实参(可迭代 ...

  3. python基础—函数装饰器

    python基础-函数装饰器 1.什么是装饰器 装饰器本质上是一个python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能. 装饰器的返回值是也是一个函数对象. 装饰器经常用于有切 ...

  4. 十. Python基础(10)--装饰器

    十. Python基础(10)--装饰器 1 ● 装饰器 A decorator is a function that take a function as an argument and retur ...

  5. Python3基础笔记--装饰器

    装饰器是十二分重要的高级函数. 参考博客:装饰器 所需前提知识: 1.作用域: LEGB 2.高阶函数 高阶函数是至少满足下列一个条件的函数: 1)接受一个或多个函数作为输入 2)输出一个函数 注意理 ...

  6. python基础之装饰器(实例)

    1.必备 #### 第一波 #### def foo(): print 'foo' foo #表示是函数 foo() #表示执行foo函数 #### 第二波 #### def foo(): print ...

  7. 【Python基础】装饰器的解释和用法

    装饰器的用法比较简单,但是理解装饰器的原理还是比较复杂的,考虑到接下来的爬虫框架中很多用到装饰器的地方,我们先来讲解一下. 函数 我们定义了一个函数,没有什么具体操作,只是返回一个固定值 请注意一下缩 ...

  8. 学习PYTHON之路, DAY 5 - PYTHON 基础 5 (装饰器,字符格式化,递归,迭代器,生成器)

    ---恢复内容开始--- 一 装饰器 1 单层装饰器 def outer(func): def inner(): print('long') func() print('after') return ...

  9. [python基础]关于装饰器

    在面试的时候,被问到装饰器,在用的最多的时候就@classmethod ,@staticmethod,开口胡乱回答想这和C#的static public 关键字是不是一样的,等面试回来一看,哇,原来是 ...

随机推荐

  1. Linux open() 一个函数,两个函数原型

    open在手册中有两个函数原型, 如下所示: int open(const char *pathname, int flags); int open(const char *pathname, int ...

  2. Inno setup: check for new updates

    Since you've decided to use a common version string pattern, you'll need a function which will parse ...

  3. [Windows API] Listing the Files in a Directory,可用来数文件夹下有多少个子文件(夹)

    转载 #include <windows.h> #include <tchar.h> #include <stdio.h> #include <strsafe ...

  4. 图论--2-SAT--HDU/HDOJ 4115 Eliminate the Conflict

    Problem Description Conflicts are everywhere in the world, from the young to the elderly, from famil ...

  5. 白话typescript中的【extends】和【infer】(含vue3的UnwrapRef)

    大家好,我是小雨小雨,致力于分享有趣的.实用的技术文章. 内容分为翻译和原创,如果有问题,欢迎随时评论或私信,希望和大家一起进步. 分享不易,希望能够得到大家的支持和关注. extends types ...

  6. Python(Redis 中 String/List/Hash 类型数据操作)

    1.下载 redis 模块 pip install redis 2.redis 数据库两种连接方式 简单连接 decode_responses=True,写入和读取的键值对中的 value 为 str ...

  7. java读源码 之 list源码分析(LinkedList)

    文章目录 LinkedList: 继承关系分析: 字段分析: 构造函数分析: 方法分析: LinkedList: 继承关系分析: public class LinkedList<E> ex ...

  8. Spring Cloud学习 之 Spring Cloud Ribbon(负载均衡策略)

    文章目录 AbstractLoadBalancerRule: RandomRule: RoundRobinRule: RetryRule: WeightedResponseTimeRule: 定时任务 ...

  9. 深入理解Mybatis插件

    Mybatis插件实现原理 本文如有任何纰漏.错误,请不吝指出,谢谢! 首先,我并没有使用过 Mybatis的插件,但是这个和我写这篇文章并不冲突,估计能真正使用到插件的人也比较少,写这篇文章的目的主 ...

  10. python学习之 %s %d 以及%变量名的含义

    %age是对前面age变量的引用,%d是将这个变量名为age的值加到其中,但是如果变量值为字符串类型,则这里应该写成%s 也就是说当变量值为数值类型,而且必须是整型类型 应该使用%d 当变量值为字符串 ...