zoukankan      html  css  js  c++  java
  • python『学习之路03』装饰器

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # @Time : 2017/11/19 10:45
    # @Author : mixiu26

    # def foo():
    # print("in the foo")
    # bar() ---->> bar() 方法未定义
    # foo()
    #
    # def bar():
    # print("in the bar()")
    # def foo():
    # print("in the foo()")
    # bar() ---- >> 正常运行
    # foo()

    # 改进: 内存加载时机是先定义在调用, 函数也是变量,所以呢,代码运行逻辑是从上到下,就是在调用foo()之前, 就先在内存中定义了
    # 变量foo,就是将方法体赋给foo, 这里其实什么也没有做, 然后定义bar()这个函数,然后在调用运行foo()这个函数,而在运行它之前
    # foo() 和 bar() 这个方法已经存在了, 变量的使用规则就是,先定义在使用, 这里也一样适用 --- >> 函数即'变量'
    # def foo():
    # print("in the foo()")
    # bar() # ---- >> 正常运行
    # def bar():
    # print("in the bar()")
    # foo()
    # 改进:
    # def foo():
    # print("in the foo()")
    # bar() # ---- >> 无法正常运行 --- >>因为foo()调用前 bar()还未定义
    # foo()
    # def bar():
    # print("in the bar()")

    # 高阶函数:=========================>>
    # 1.吧一个函数当做实参传递给另外一个函数:
    # 2.返回值中包含函数名

    # def bar():
    # print("in the bar()")

    # def test1(func):
    # print(func) # bar的内存地址: <function bar at 0x0000000002452E18>
    # func() # in the bar()
    #
    # test1(bar) # in the bar()
    # 解释, 首先函数即是变量不解释, 关于变量的使用 ---- >> x = 1; y = x; ----- >> y = 1
    # 我们调用test1() --- >>传入了bar ---- 这里的bar == func ---- >>其实就是把bar的内存地址给了func ,
    # 因为bar在内存中作为一个变量来存储方法体, test1中吧bar传给func, 那么这里的func == bar了 --- 他们指向的方法体是一样的只不过在加了个func的引用而已
    import time
    # 函数作为实参传递的高阶函数
    # def bar():
    # time.sleep(3)
    # print("in the bar()") # in the bar()
    #
    # def test1(func):
    # start_time = time.time()
    # func()
    # sttop_time = time.time()
    # print("the func run time is %s" %(sttop_time-start_time)) # the func run time is 3.010805130004883
    # test1(bar) # in the bar()

    # 返回值中携带函数的高阶函数:
    # def bar():
    # time.sleep(3)
    # print("in the bar")
    #
    # def test2(func):
    # print(func)
    # return func

    # 注意传值问题:
    # test2(bar()) ---- >> 这样传值就不符合高阶函数定义, 是要把函数作为实参传递, 如果你带了括号传递的就是bar() 这个方法的返回值
    # 而传递: test(bar) ---- >> 这里的bar 传递的就是地址值, 是这个方法体的引用, 所以传递的时候一定要注意
    # test2(bar)

    # 当我们吧bar传过来, test2就开始打印bar的内存地址, 打印完成后又返回bar的内存地址,
    # 我们将test2的运行结果进行接收: 注意了,我们知道, 函数即是变量. so --- >>ret() <==> bat() ---- > 因为return的是地址值, 所以
    # 对于ret 和test2(bar) 来说, 他们现在是一模一样的, 只不过是引用名不一样而已,不影响哦

    # ret = test2(bar) # 运行机制 : bar 一旦传入,马上打印bar的地址值, 打印完成后返回bar的内存地址值, 赋值给ret,so得到的结果应该是一样的
    # print(ret) # <function bar at 0x00000000024A2E18> <function bar at 0x00000000024A2E18> 一模一样
    #
    # ret()
    # <function bar at 0x00000000024A2E18>
    # in the bar
    # 注意看 ---- >> 其实 ret 就是bar的地址值, so ret() --- >> 其实就相当于运行run bar() 这个函数了, 而我们又知道变量名是可以随便使用的
    # 也就是说我们可以吧test2()的返回值, 重新赋值给bar自己 ---- >> bar = test2(bar) ---- >> 这样我们得到的还是bar自己,将来在调用
    # 的时候,还是使用bar() 自己原本的调用方式, so,由此我们就得到了不改变原函数的调用方式的基础上给调用函数添加功能

    # 覆盖之前的bar:
    # bar = test2(bar)
    # bar() # bar() 的运行结果和ret() 是一模一样 run bar()
    # <function bar at 0x00000000024A2E18>
    # in the bar

    # 嵌套函数: 函数中定义函数: 在一个函数体内,用def去声明另一个函数而不是调用哦
    # def foo():
    # print("in the foo: ")
    # def bar(): # 嵌套函数中的函数, 相当于局部变量,不能直接外部调用
    # print("in the bar: ")
    # bar() # 嵌套函数的调用方式呢: 内部就得在内部调用 ,但是要调用嵌套函数中的内部函数,
    # # 又不能把调用卸载内部函数之上,为啥你写上方就没定义啊,程序执行从上到下.
    # foo()

    # def grandpa():
    # x=1
    # def father():
    # x=2
    # def son():
    # x=3
    # print(x) # x=3 --- >>嵌套函数的运行方式: 从外到内一层一层的运行, 从gra --- >> fat --- >> son
    # son()
    # father()
    # grandpa()

    # 装饰器 ---- >> 统计函数的运行时间:
    def timer(func): # timmer(test1) ---- >> 其实就是把test1的内存地址值传给func
    def deco(): # 程序正常运行后声明了deco的函数变量, 然后直接返回deco的内存地址
    start_time = time.time()
    func()
    stop_time = time.time()
    print("the func time is %s" %(stop_time-start_time))
    return deco # 注意,return的是地址值, 实际上timer传入函数变量名的结果,实际上就是返回了deco的内存地址 <function timer.<locals>.deco at 0x00000000026DAAE8>
    @timer # test1 = timer(test1)
    def test1():
    time.sleep(3)
    print("in the test1")
    @timer # test2 = timer(test2)
    def test2():
    time.sleep(3)
    print("in the test2")

    # timmer(test1())
    # 打印deco内存地址:
    # print(timer(test1)) # <function timer.<locals>.deco at 0x00000000026DAAE8>
    # 有了deco的地址值, 我们只要将deco的地址值直接括号一下,就可以作为方法执行了,所以我们把他重新赋值给test1 和test2,
    # 这样就即保证了不改变方法的调用方式有添加了新功能
    # test1 = timer(test1)
    # ---- >>此时的test1执行的是deco这个函数,在deco中添加了时间统计,然后再通过func去执行test1(),这里就相当于run test1(),然后打印运行时间
    # test1()
    # in the test1 the func time is 3.010805368423462
    # 是不是即执行了test1 和test2中自带的方法,又在不改变源代码的情况下给test1和test2添加了新功能
    # test2 = timer(test2)
    # test2() # in the test2 the func time is 3.010805130004883

    # 但是这么做比较麻烦,可不可以采用直接调用的方式呢?
    # 在需要新增功能的函数前添加@符号,在配置上装饰器的名称即可,例: @timer # test1 = timer(test1)
    # 加入装饰器后在运行test1()和test2()
    test1() # in the test1 the func time is 3.010805130004883
    test2() # in the test2 the func time is 3.010805606842041

    # 将装饰器转换成通用型:
    def timm(func): # func = test2
    def deco(*args,**kwargs):
    start_time = time.time()
    # *ags,**kwargs c传入func() --- >> 如果test中没有数据,那参数就为空,如果test中有数据,就全部传进来
    func(*args,**kwargs) # run test2() 而我们在调用test2的时候要求携带参数,但是我们没有携带,所以运行时候就报错了
    stop_time = time.time()
    print("the func run time is %s" %(stop_time-start_time))
    return deco

    @timm # test1 = timm(test1)
    def test3():
    time.sleep(2)
    print('in the test3')

    @timm
    def test4(name,age,hoppy): # test2 = timm(test2) = deco === >test2() = deco()
    time.sleep(2)
    print('in the test4 --- >>',name,age,hoppy)

    # 注意你看,当我们的test中携带参数时,装饰器报错, TypeError: test4() missing 1 required positional argument: 'name'
    # 那该怎么办呢?

    test3()
    test4("mixiu26",25,["yaa"])
    # 装饰器最终版:
    user = "mixiu26"
    pwd = "abc123"
    def auth(auth_type):
    print("auth func: ", auth_type)
    def outer_wrapper(func):
    def wrapper(*args, **kwargs):
    print("auth args: ", *args, **kwargs)
    if auth_type == "local":
    username = input("username: ").strip()
    password = input("password: ").strip()
    if user == username and pwd == password:
    print("33[32;1mUser has passed authentication33[0m")
    res = func(*args, **kwargs)
    print("after func------>>")
    return res
    else:
    exit("33[31;1mInvalid username or password33[0m")
    elif auth_type == "ldap":
    print("毛线,不会。。。。")
    return wrapper
    return outer_wrapper
    def index():
    print("welcome to index page: ")

    @auth(auth_type="local")
    def home(): # 测试方法携带返回值,则func()执行时仍需返回
    print("welcome to home page: ")
    return "good girl"
    @auth(auth_type="ldap") # 我们需要传递一个type ---- >>type,传入type后返回的是wrapper(bbs)内存地址值,因为初始化的时候他会一层一层的执行下去,
    # 所以,当type传递过来, 就会得到wrapper内存地址值, 然后再去执行这一步 bbs = outer_wrapper(bbs)=== wrapper()内存地址值
    # 这里其实就相当于将path作为登录方式判断层,接收到具体的登录type时,开始进行下一步,就将func()这个函数传递给outer_wrapper
    # 就得到我们之前做的这一步:bbs = outer_wrapper(bbs) ==== wrapper返回的地址值,然后将它的地址值赋值给bbs也就是这部:
    # bbs = outer_wrapper(bbs) ----- >> bbs() 因为 outer_wrapper(bbs)方法返回的是wrapper的地址值, 而具体调用bbs方法的func()
    # 在 wrapper() 方法里,因此将outer_wrapper(bbs) 赋值给bbs,bbs() === > wrapper() ---- >执行这个方法就会调用bbs()对吧,
    # 所以就相当于在auth中添加了一层类型读取
    def bbs():
    print("welcome to bbs page: ")
    return "good girl"

    index()
    print(home())
    bbs()
     
  • 相关阅读:
    Ansible自动部署lnmp架构+上线电商
    elastalert 基于EFK环境的邮件报警
    冒泡法、选择法、插入法排序
    Java的基本数据类型
    cmd运行java程序找不到或无法加载主类解决办法
    JavaBean进行表单开发
    Web项目各目录详解
    【数论】整数分块及详细证明
    题解 P4874 【[USACO14DEC] Piggyback_Silver 背负式运输(银)】
    题解 P3792 【由乃与大母神原型和偶像崇拜】
  • 原文地址:https://www.cnblogs.com/mixiu26/p/7859911.html
Copyright © 2011-2022 走看看