zoukankan      html  css  js  c++  java
  • Python 装饰&生成&迭代器

    Python 装饰器

    装饰器可以使函数执行前和执行后分别执行其他的附加功能,这种在代码运行期间动态增加功能的方式,称之为"装饰器"(Decorator),装饰器的功能非常强大,装饰器一般接受一个函数对象作为参数,以对其进行增强,相当于C++中的构造函数,与析构函数.

    装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象.它经常用于有迫切需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景.装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用.

    总而言之,装饰器具有如下几个特点:

    ● 装饰器本身也是一个函数,其作用是,用于装饰其他函数.
    ● 装饰器是一个闭包函数是嵌套函数,通过外层函数提供嵌套函数的环境
    ● 装饰器在权限控制,增加额外功能,如增加记录日志,缓存处理,发送邮件用的比较多

    ◆无参装饰器◆

    原函数中不带参数的装饰器,如下例子假设:我定义了一个函数lyshark(),现在想要在不改变原来函数定义的情况下,在函数运行前打印一段话,函数运行后打印另一段话,此时我们可以使用装饰器的装饰功能来简单的实现这个需求.

    >>> import os
    >>> import sys
    >>> 
    >>> def outer(function):
        def inner():
            print("主函数开始执行前,会先执行我!")
            result=function()
            print("主函数执行结束后,要在执行我!")
            return result
        return inner
    
    
    # (1) @ + 函数名,直接作用在需要装饰的函数上一行
    # (2) 自动执行outer函数并且将下面的函数名lyshark()当做参数传递到outer()
    # (3) 将outer函数的返回值inner,重新赋值给lyshark()函数
    >>> @outer
    def lyshark():
        print("lyshark 的主函数体,装饰器在装饰我(*^_^*)")
        return "check ok"
    
    #==调用并执行函数,结果如下==========================================
    >>> ret=lyshark()
    主函数开始执行前,会先执行我!
    lyshark 的主函数体,装饰器在装饰我(*^_^*)
    主函数执行结束后,要在执行我!
    
    >>> print("lyshark()函数的返回值: ",ret)
    lyshark()函数的返回值:  check
    

    如上代码的执行流程是这样的,步骤如下:

    1.当我们调用lyshark()函数时,会自动检查lyshark()函数上是否有装饰器
    2.如果有则将lyshark()函数的指针,传递给装饰器outer(function)
    3.outer(function)接到指针后,执行嵌套函数内的inner(),则先执行print打印一段话
    4.由于lyshark()函数指针,传递给了function变量,执行function()则相当于执行lyshark()
    5.紧接着,最后一步执行打印一段结束的话,并执行返回,返回inner

    ◆有参装饰器◆

    原函数带一个参数的装饰器: 我们在以上的案例中,给装饰器添加一个参数,并在内部使用这个参数.

    >>> import os
    >>> import sys
    >>> 
    >>> def outer(function):
        def inner(args):
            print("主函数开始执行前,会先执行我!")
            ret=function(args)
            print("主函数执行结束后,要在执行我!")
            return ret
        return inner
    
    >>> @outer
    def lyshark(args):
        print(args)
        return 0
    
    #==调用并执行函数,结果如下==========================================
    >>> ret=lyshark("hello world!")
    主函数开始执行前,会先执行我!
    hello world!
    主函数执行结束后,要在执行我!
    
    >>> print("lyshark 的返回值是:",ret)
    lyshark() 函数的返回值是: 0
    

    原函数带两个参数的装饰器: 接下来继续演示一下,带有两个参数的装饰器,3个4个,以此类推.

    >>> import os
    >>> import sys
    >>> 
    >>> 
    >>> def outer(function):
        def inner(x,y):
            print("主函数开始执行前,会先执行我!")
            ret=function(x,y)
            print("主函数执行结束后,要在执行我!")
            return ret
        return inner
    
    >>> @outer
    def lyshark(x,y):
        print(x,y)
        return 0
    
    #==调用并执行函数,结果如下==========================================
    >>> ret=lyshark("Hello","LyShark")
    主函数开始执行前,会先执行我!
    Hello LyShark
    主函数执行结束后,要在执行我!
    
    >>> print("lyshark() 函数的返回值是:",ret)
    lyshark() 函数的返回值是: 0
    

    传递一个万能参数: 装饰器也可传递一个万能参数,通过此参数传递列表字典等.

    >>> import os
    >>> import sys
    >>> 
    >>> def outer(function):
        def inner(*args,**kwargs):
            print("主函数开始执行前,会先执行我!")
            ret=function(*args,**kwargs)
            print("主函数执行结束后,要在执行我!")
            return ret
        return inner
    
    >>> @outer
    def lyshark(*args):
        print(args)
        return 0
    
    #==调用并执行函数,结果如下==========================================
    >>> num=[1,2,3,4,5]
    >>> ret=lyshark(num)
    主函数开始执行前,会先执行我!
    ([1, 2, 3, 4, 5],)
    主函数执行结束后,要在执行我!
    >>> 
    >>> print("lyshark() 函数的返回值是:",ret)
    lyshark() 函数的返回值是: 0
    
    #==调用并执行函数,结果如下==========================================
    @outer
    def lyshark_kw(*args,**kwargs):
            print(args,kwargs)
            return 0
    
    num=[1,2,3,4,5]
    kw={"1001":"admin","1002":"guest"}
    ret=lyshark_kw(num,kw)
    

    一次使用两个装饰器装饰函数: 如果一个装饰器不够用的话,我们可以使用两个装饰器,首先将函数与内层装饰器结合然后在与外层装饰器相结合,要理解使用@语法的时候到底执行了什么,是理解装饰器的关键.

    >>> import os
    >>> import sys
    >>> 
    >>> def outer2(function2):
        def inner2(*args,**kwargs):
            print("装饰器2--->【开始】")
            ret=function2(*args,**kwargs)
            print("装饰器2--->【结束】")
            return ret
        return inner2
    
    >>> def outer1(function1):
        def inner1(*args,**kwargs):
            print("装饰器1--->【开始】")
            ret=function1(*args,**kwargs)
            print("装饰器1--->【结束】")
            return ret
        return inner1
    
    
    @outer2
    @outer1
    def lyshark():
        print("lyshark 函数被执行了")
    
    #==调用并执行函数,结果如下==========================================
    >>> lyshark()
    装饰器2--->【开始】
    装饰器1--->【开始】
    lyshark 函数执行了
    装饰器1--->【结束】
    装饰器2--->【结束】
    
    #==调用并执行函数,结果如下==========================================
    @outer1
    @outer2
    def lyshark_and():
        print("lyshark_and 函数被执行了")
    
    >>> lyshark_and()
    装饰器1--->【开始】
    装饰器2--->【开始】
    lyshark_and 函数执行了
    装饰器2--->【结束】
    装饰器1--->【结束】
    

    ◆带参装饰器◆

    前面的装饰器本身没有带参数,如果要写一个带参数的装饰器怎么办,那么我们就需要写一个三层的装饰器,而且前面写的装饰器都不太规范,下面来写一个比较规范带参数的装饰器,下面来看一下代码,大家可以将下面的代码自我运行一下.

    给装饰器本身添加参数: 接下来我们将给装饰器本身添加一些参数,使其能够实现参数传递.

    >>> import functools
    >>> import sys
    >>> 
    >>> def lyshark(temp=""):                                   #指定装饰器默认参数
        def decorator(function):                                #定义装饰器
            @functools.wraps(function)                          #使被装饰的装饰器的函数名不改变
            def wrapper(*args,**kwargs):
                print("主函数开始执行前,会先执行我!")
                print("{}:{}".format(temp,function.__name__))   #这里调用了装饰器temp变量
                ret=function(*args,**kwargs)
                print("主函数执行结束后,要在执行我!")
                return ret
            return wrapper
        return decorator
    
    #==调用并执行函数,结果如下==========================================
    >>> #如果不给装饰器加参数,那么这个装饰器将使用默认参数 temp="",来填充
    >>> @lyshark()
    def test(x):
        print(x+100)
    
    >>> test(100)
    主函数开始执行前,会先执行我!
    :test                         #这里由于没有传递参数则第一项为空,第二项是函数名称`function.__name__`取出的
    主函数执行结束后,要在执行我!
    
    #==调用并执行函数,结果如下==========================================
    >>> #下面是给装饰器一个参数,将不是用默认参数 temp="",将变成 temp="LyShark"
    >>> @lyshark("LyShark")
    def test(x):
        print(x+100)
    
    >>> test(100)
    主函数开始执行前,会先执行我!
    LyShark:test
    主函数执行结束后,要在执行我!
    

    给装饰器本身添加参数: 接下来我们将给装饰器本身添加两个参数,使其能够传递多个参数.

    >>> import sys
    >>> import os
    >>> 
    >>> def lyshark(x="Hello",y="LyShark"):
        def decorator(function):
            def wrapper():
                print("主函数执行前,应先执行我!")
                print(x,y)
                ret=function()
                print("主函数执行后,要执行我!")
                return ret
            return wrapper
        return decorator
    
    #==调用并执行函数,结果如下==========================================
    >>> #使用默认参数的装饰器:此时 x="Hello" y="LyShark"
    >>> @lyshark()
    def test():
        print("我是test(),主函数,装饰器在装饰我")
    
    >>> test()
    主函数执行前,应先执行我!
    Hello LyShark
    我是test(),主函数,装饰器在装饰我
    主函数执行后,要执行我!
    
    #==调用并执行函数,结果如下==========================================
    >>> #给装饰器指定参数:此时 x="My Name Is :" y="LyShark"
    >>> @lyshark("My Name Is :","LyShark")
    def test():
        print("我是test(),主函数,装饰器在装饰我")
    
    >>> test()
    主函数执行前,应先执行我!
    My Name Is : LyShark
    我是test(),主函数,装饰器在装饰我
    主函数执行后,要执行我!
    

    ◆装饰器实战(练习)◆

    测试程序执行时间: 通过使用装饰器,给一个已存在的函数,测试函数的执行时间.

    import os
    import sys
    import time
    
    def showtime(func):
            def main():
                    start_time=time.time()
                    func()
                    end_time=time.time()
                    print("程序运行了: %s"%(end_time-start_time))
            return main
    
    #==调用并执行函数,结果如下==========================================
    @showtime
    def timer():
            time.sleep(3)
            print("执行程序结束....")
    
    timer()
    

    记录登陆状态(1): 使用装饰器记录登陆状态,下次自动登录.

    import os
    import sys
    
    user_info={}
    user_name="admin"
    pass_word="1233"
    
    def check_login(func):
            def main():
                    if((user_info.get("is_login",None) ==True) and (user_info.get("is_type",None)==0)):
                            print("你已经登陆过了,不能重复登陆!")
                            func()
                    else:
                            print("你没有登陆,请先登录,在进行此操作")
            return main
    
    
    def login():
            username=input("输入用户名:").strip()
            password=input("输入密码:").strip()
    
            if username==user_name and password==pass_word:
                    user_info["is_login"]=True
                    user_info["is_type"]=0
                    print("你好: "+username+"登陆成功,登录状态已被记录")
            elif username!="" and password!="":
                    print("您输入的用户名,或者密码不正确,请重新输入")
            else:
                    print("您没有输入任何内容,错误")
    
    @check_login
    def home():
            print("用户登录成功,显示这个函数中的内容.")
    
    
    def main():
            while True:
                    temp=input("1.登陆  2.查询状态
    ")
                    if(temp=="1"):
                            login()
                    elif(temp=="2"):
                            home()
                    else:
                            continue
    
    main()
    

    记录登陆状态(2): 使用装饰器记录登陆状态,下次自动登录.

    USER_INFO = {}
    
    def check_login(func):
        def inner(*args,**kwargs):
            if USER_INFO.get('is_login',None):
                ret = func()
                return ret
            else:
                print("请登录")
        return inner
    
    def check_admin(func):
        def inner(*args,**kwargs):
            if USER_INFO.get('user_type',None) == 2:
                ret = func()
                return ret
            else:
                print("无权限查看")
        return inner
    
    
    @check_login        #先检查你是不是登陆了
    @check_admin        #在检查你是不是管理员
    def index():
        """
        管理员用户
        """
        print('index')
    
    @check_login
    def home():
        """
        普通用户
        
        """
        print('home')
    
    def login():
        user = input("请输入用户名:")
        if user == 'admin':
            USER_INFO['is_login'] = True
            USER_INFO['user_type'] = 2
        else:
            USER_INFO['is_login'] = True
            USER_INFO['user_type'] = 1
    
    
    def main():
        while True:
            inp = input('1、登录;2、查看信息;3、超级管理员管理
    >:')
            if inp == '1':
                login()
            elif inp == '2':
                home()
            elif inp == '3':
                index()
    
    main()
    

    ## Python 生成器

    通过列表生成式,我们可以直接创建一个列表.但是,受到内存限制,列表容量肯定是有限的,而且创建一个包含100万个元素的列表,不仅占用很大的存储空间,还会造成大量空间的白白浪费,如果列表元素可以按照某种算法推算出来,这样我们就不必创建完整的list,从而节省大量的空间.在Python中,这种一边循环一边计算的机制,称为生成器(Generator).

    生成器是一个特殊的程序,可以被用作控制循环的迭代行为,Python中生成器是迭代器的一种,使用yield返回值函数,每次调用yield会暂停,而可以使用next()函数和send()函数恢复生成器.

    生成器类似于返回值为数组的一个函数,这个函数可以接受参数,可以被调用,但不同于一般的函数会一次性返回包括了所有数值的数组,生成器一次只能产生一个值,这样消耗的内存数量将大大减小,而且允许调用函数可以很快的处理前几个返回值,因此生成器看起来像是一个函数,但是表现得却像是迭代器.

    生成器的注意事项,和相关的特性:

    ● 当我们调用一个生成器函数时,其实返回的是一个迭代器对象
    ● 在Python语言中,只要表达式中使用了,yield函数,通常将此类函数称为生成器(generator)
    ● 运行生成器时,每次遇到yield函数,则会自动保存并暂停执行,直到使用next()方法时,才会继续迭代
    ● 跟普通函数不同,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器

    在学习生成器之前,我们先来看一下以下两种情况的对比,第一种方法很简单,只有把一个列表生成式的[]中括号改为()小括号,就创建了一个生成器.

    >>> lis = [x*x for x in range(10)]
    >>> print(lis)
    [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
    
    >>> generator = (x*x for x in range(10))
    >>> print(generator)
    <generator object <genexpr> at 0x0000022E5C788A98>
    

    如上的例子,第一个lis通过列表生成式,创建了一个列表,而第二个generator则打印出一个内存地址,如果我们想获取到第二个变量中的数据,则需要迭代操作,如下所示:

    >>> generator = (x*x for x in range(10))
    
    >>> print(next(generator))
    0
    >>> print(next(generator))
    1
    >>> print(next(generator))
    4
    >>> print(next(generator))
    9
    

    以上可以看到,generator保存的是算法,每次调用next(generaotr),就计算出他的下一个元素的值,直到计算出最后一个元素,使用for循环可以简便的遍历出迭代器中的数据,因为generator也是可迭代对象.

    >>> generator = (x*x for x in range(10))
    >>> 
    >>> for i in generator:
    	print(i,end="")
    
    0149162536496481
    

    ◆生成器的实例◆

    实例(1): 通过函数,和yield关键字,生成几个生成器.

    >>> import sys
    >>> 
    >>> def func():
    	yield 1
    	yield 2
    	yield 3
    	yield 4
    	yield 5
    
    >>> temp=func()
    >>> temp.__next__()
    1
    >>> temp.__next__()
    2
    >>> temp.__next__()
    3
    

    实例(2): 使用while循环构建一个生成器,并通过for遍历打印出结果.

    >>> import sys
    >>> 
    >>> def yieldNum(x):
    	y=0
    	while (y <= x):
    		yield y
    		y += 1
    	
    >>> yie=yieldNum(5)
    >>> for i in yie:
    	print(i)
    
    0
    1
    2
    3
    4
    5
    

    实例(3): 使用生成器求1-10的平方.

    >>> def yieldNum():
        x=1
        while (x <=10 ):
            yield x ** 2
            x += 1
    
    >>> yie=yieldNum()
    >>> 
    >>> for i in yie:
        print(i)
    4
    16
    36
    64
    100
    

    实例(4): 使用生成器,自定义实现range函数.

    >>> def xrange(num):
        temp=-1
        while True:
            temp=temp+1
            if (temp >= num):
                return
            else:
                yield temp
    
    >>> xrange(10)
    <generator object xrange at 0x038E3030>
    

    实例(5): 通过使用生成器求斐波那契数列.

    >>> def fib(max):
        n,a,b=0,0,1
        while n < max:
            yield b
            a,b=b,a+b
            n+=1
        return "done"
    
    >>> f=fib(5)
    >>> f
    <generator object fib at 0x038F4A20>
    

    实例(6): 使用生成器,在单个进程情况下实现并发效果.

    import time
    def consumer(name):
        print("%s 准备吃包子啦!" %name)
        while True:
           baozi = yield
     
           print("包子[%s]来了,被[%s]吃了!" %(baozi,name))
     
    def producer(name):
        c = consumer('A')
        c2 = consumer('B')
        c.__next__()
        c2.__next__()
        print("老子开始准备做包子啦!")
        for i in range(10):
            time.sleep(1)
            print("做了2个包子!")
            c.send(i)
            c2.send(i)
     
    producer("alex")
    

    ◆生成列表解析◆

    列表解析是Python迭代机制的一种应用,它常用于实现创建新的列表,因此要放置于[]中,列表解析非常灵活,可以用户快速创建一组相应规则的列表元素,且支持迭代操作.

    求阶乘: 通过列表解析式,来实现列表的迭代求阶乘.

    >>> temp1 = [1,2,3,4,5]
    >>> temp2 = [ x ** 2 for x in temp1 ]
    
    >>> temp1
    [1, 2, 3, 4, 5]
    >>> temp2
    [1, 4, 9, 16, 25]
    

    求阶乘: 通过列表解析式,实现迭代求阶乘,并且只打印大于2(if x>=2)的数据.

    >>> temp1 = [1,2,3,4,5]
    >>> temp2 = [ x**2 for x in temp if x>=2 ]
    
    >>> temp1
    [1, 2, 3, 4, 5]
    >>> temp2
    [4, 9, 16, 25]
    

    求阶乘: 通过列表解析式,实现迭代求阶乘,并通过range函数生成相关数据.

    >>> temp = [ (x**2)/2 for x in range(1,10)]
    >>> temp
    [0.5, 2.0, 4.5, 8.0, 12.5, 18.0, 24.5, 32.0, 40.5]
    

    数据合并: 通过列表解析式,实现迭代将两个列表按照规律合并.

    >>> temp1=["x","y","z"]
    >>> temp2=[1,2,3]
    >>> temp3=[ (i,j) for i in temp1 for j in temp2 ]
    >>> temp3
    [('x', 1), ('x', 2), ('x', 3), ('y', 1), ('y', 2), ('y', 3), ('z', 1), ('z', 2), ('z', 3)]
    

    文件过滤: 通过使用列表解析,实现文本的过滤操作.

    >>> import os
    
    >>> file_list=os.listdir("/var/log")
    >>> file_log=[ i for i in file_list if i.endswith(".log") ]
    >>> print(file_log)
    ['boot.log', 'yum.log', 'ecs_network_optimization.log', 'ntp.log']
    
    >>> file_log=[ i for i in os.listdir("/var/log") if i.endswith(".log") ]
    >>> print(file_log)
    ['boot.log', 'yum.log', 'ecs_network_optimization.log', 'ntp.log']
    

    ◆生成器表达式◆

    生成器表达式并不真正创建数字列表,而是返回一个生成器对象,此对象在每次计算出一个条目后,把这个条目"产生"(yield)出来,生成器表达式使用了"惰性计算"或称作"延迟求值"的机制序列过长,并且每次只需要获取一个元素时,应当考虑使用生成器表达式而不是列表解析.

    >>> import sys
    >>> 
    >>> yie=( i**2 for i in range(1,10) )
    >>> next(yie)
    1
    >>> next(yie)
    4
    >>> next(yie)
    9
    
    >>> for j in ( i**2 for i in range(1,10)):print(j/2)
    ... 
    0.5
    2.0
    4.5
    8.0
    12.5
    18.0
    24.5
    32.0
    40.5
    

    ## Python 迭代器

    迭代器是访问集合元素的一种方式,迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束.迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退.另外,迭代器的一大优点是不要求事先准备好整个迭代过程中所有的元素.迭代器仅仅在迭代到某个元素时才计算该元素,而在这之前或之后,元素可以不存在或者被销毁.这个特点使得它特别适合用于遍历一些巨大的或是无限的集合,比如几个G文件的遍历.

    关于迭代器的几个特性:

    ● 迭代器是一个可以记住遍历的位置的对象
    ● 字符串,列表或元组对象都可用于创建迭代器
    ● 迭代器有两个基本的方法:iter()和next()
    ● 迭代器便于循环比较大的数据集合,节省了内存开支
    ● 迭代是Python最强大的功能之一,是访问集合元素的一种方式
    ● next()就相当于调用__next__(),for也是iterable(可迭代)对象
    ● 访问者不需要关心迭代器内部的结构,仅需通过next()方法不断去取下一个内容
    ● 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束.迭代器只能往前不会后退

    实例(1): 迭代器的基本用法1.

    >>> import sys
    >>> 
    >>> temp=iter([1,2,3,4,5,6,7,8,9])
    >>> temp
    <list_iterator object at 0x03BD1F90>
    
    >>> temp.__next__()
    1
    >>> temp.__next__()
    2
    >>> temp.__next__()
    3
    

    实例(2): 迭代器的基本用法2.

    >>> import sys
    >>> 
    >>> temp=[1,2,3,4,5]
    >>> ite=temp.__iter__()
    >>> print(type(temp),type(ite))
    <class 'list'> <class 'list_iterator'>
    
    >>> next(ite)
    1
    >>> 
    >>> next(ite)
    2
    >>> next(ite)
    3
    >>> next(ite)
    4
    

    实例(3): 通过while语句遍历迭代对象.

    >>> ite=iter([1,2,3,4,5])
    >>> 
    >>> while True:
        try:
            temp=next(ite)
            print(temp)
        except StopIteration:
            break
    
    1
    2
    3
    4
    5
    
  • 相关阅读:
    MVC概念性的内容
    类 class
    php获取真实IP地址
    面向对象static静态的属性和方法的调用
    smarty 入门2(个人总结)
    smarty入门
    读取文件内容fopen,fgets,fclose
    mysql常用命令
    mybatis查询的三种方式
    MyBatis 映射文件
  • 原文地址:https://www.cnblogs.com/LyShark/p/11295877.html
Copyright © 2011-2022 走看看