zoukankan      html  css  js  c++  java
  • Python基础第九天——迭代对象、 迭代器对象、生成器、三元表达式列表解析、生成器表达式

    鸡汤:

      要时刻不断地给自己灌输一种思想:都TMD是个人,凭什么他会而我就不会?王候将相宁有种乎?我承认人有天赋的差别,但是勤奋能弥补缺陷!所以,根据以上观点得出以下结论,只要出生时不是个傻子,那么就没有蠢的人,只有懒的猪!只要勤奋上进,小白也会变大神。加油

                                                            ——奔跑吧小白

    一、迭代对象、迭代器对象

    1、迭代

    定义:带有__iter__方法的就是可迭代对象

    python常用的数据类型中,除了数字外,都是迭代对象。

    例:用isinstance判断python常用数据类型是否为迭代对象,经验证,python常用数据类型中除数字类型外,都是可迭代对象。

    from collections import  Iterable
    
    int1 = 1
    str1 = 'xiaobai'
    list1 = [1,2,3,4,5]
    tuple1 = (1,2,3,4,5)
    dic1 = {'a':1,'b':2,'c':3}
    set1 = {1,2,3,4}
    f = open('a.txt','w')
    
    # 用isinstance判断是否为迭代对象
    print(isinstance(int1,Iterable))   # 数字类型结果:False
    print(isinstance(str1,Iterable))   # 字符串结果:True
    print(isinstance(list1,Iterable))  # 列表结果:True
    print(isinstance(tuple1,Iterable)) # 元组结果:True
    print(isinstance(dic1,Iterable))   # 字典结果:True
    print(isinstance(set1,Iterable))   # 集合结果:True
    print(isinstance(f,Iterable))      # 文件结果:True
    

    输出结果:

    False
    True
    True
    True
    True
    True
    True
    View Code

    2、迭代器

    定义:有__iter__和__next__的方法的就是迭代器。

    取值:通过__next__方法,加上括号拿到迭代器里的值。(迭代器也有一个__iter__方法。)

    特点:

      优点:不依赖于索引,惰性计算节省内存

      缺点:取值不方便,一次性取值,只能住后取,不能回退。

    例1:迭代器用__next__()的方式取值

    l = [1,2,3,4,5]
    x = l.__iter__()  # 迭代器
    print(x.__next__())
    print(x.__next__())
    print(x.__next__())
    print(x.__next__())
    print(x.__next__())
    print(x.__next__())  # 已知列表l有5个值,
                        # 当取到第6个值的时候会异常 StopIteration
    

    输出结果:

    1
    Traceback (most recent call last):
    2
      File "C:/Users/William/PycharmProjects/Python_Item2/study/day9/迭代器.py", line 19, in <module>
    3
    4
        print(x.__next__())  # 已知列表l有5个值,
    5
    StopIteration
    View Code

    例2:用isinstance判断python常用数据类型是否为迭代器对象

    from collections import  Iterator
    
    int1 = 1
    str1 = 'xiaobai'
    list1 = [1,2,3,4,5]
    tuple1 = (1,2,3,4,5)
    dic1 = {'a':1,'b':2,'c':3}
    set1 = {1,2,3,4}
    f = open('a.txt','w')
    
    # 用isinstance判断是否为迭代器对象
    print(isinstance(int1,Iterator))   # 数字类型结果:False
    print(isinstance(str1,Iterator))   # 字符串结果:True
    print(isinstance(list1,Iterator))  # 列表结果:True
    print(isinstance(tuple1,Iterator)) # 元组结果:True
    print(isinstance(dic1,Iterator))   # 字典结果:True
    print(isinstance(set1,Iterator))   # 集合结果:True
    print(isinstance(f,Iterator))      # 文件结果:True
    

    输出结果:

    False
    False
    False
    False
    False
    False
    True
    View Code

    例3:迭代器执行__iter__()方法后得到的还是自己。

    # 文件是迭代器
    with open('a.txt','r',encoding='utf-8') as f:
        print(f)                        # 得到迭代器
        obj = f.__iter__()             # 执行__iter__后仍然是迭代器
        print(obj is f)               # True
    

    输出结果:

    <_io.TextIOWrapper name='a.txt' mode='r' encoding='utf-8'>
    True
    View Code

    3、for循环原理

    步骤:

        1、先执行迭代对象的__iter__()方法,得到迭代器

      2、for循环会自动调用迭代器内的__next__()方法,将得到的返回值再赋予变量

      3、直到捕捉到StopIteration这个异常后,再结束循环。

    for循环只能判断要遍历的对象是否为迭代对象,而不能只判断是否为迭代器。

    例:

    l = [1,2,3,4]
    for i in l:  #  1、先执行迭代对象的__iter__()方法,得到迭代器
                 # 2、for循环会自动调用迭代器下面的__next__()方法,得到返回值后再赋予变量“i”
                 # 3、直到报出StopIteration异常,for循环再捕捉到这个异常,最后结束循环。
        print(i)
    

    补充:

    要循环一个文件里的内容,不需要用f.readlines()把文件所有的内容都取出来,这样做是把文件的内容全都读到内存中了。我们只需通过迭代器的形式——“for i in 文件名”的形式取出文件里的内容,这样不会占用太大的内存。

    4、用while循环实现不依赖于索引的方式取值。

    (1)try...except

    这里要用到一个新的知识点:

    try...except

    用于捕捉程序出现的异常。

    语法:

      try

        逻辑

      except 要捕捉的异常:

        条件

    (2)用while循环实现不依赖于索引的方式取值

    d = {'a':1,'b':2,'c':3}
    
    obj = d.__iter__()      # 1、先将迭代对象转换成迭代器
    
    while True:             # 2、再用__next__()方法取值
        try:                # 3、用try...except捕捉StopIteration的异常。
            i = obj.__next__()
            print(i)
        except StopIteration:
            break
    

     

     

     

    二、生成器

    1、定义:

          生成器也叫生成器函数,函数体内包含yield关键字,那么该函数执行的结果是生成器函数。

           生成器也是迭代器。因为它下面也有__iter__方法和__next__方法。

    例1:证明生成器就是迭代器

    def foo():
        print('start')
        yield
        print('end')
    
    g = foo() # 调用时并不会执行函数内的代码,它会得到一个生成器。把它赋值给变量g
    print(g)  # 得到生成器
    
    # 通过以下方法证明生成器就是迭代器
    from collections import Iterator
    print(isinstance(g,Iterator))    # 结果为True
    

    2、生成器的取值

    生成器的取值是通过“__next__()”的方式取值的。

    例:

    def foo():
        print('start')
        yield
        print('end')
    
    g = foo()    # 得到生成器,生成器就是迭代器
    g.__next__() # 结果为start
                 # 重点:__next__()一次,则触发函数的一次执行,因为g = foo()
    

    输出结果:

    start
    View Code

    3、yied的特性

    函数一定要有return才能结束,即使不给函数加return,函数默认也有return None,

    所以说函数要执行一次,就会从头到尾依次执行函数体,直到碰到return函数才会停止。

    yield功能

      1、与return特性一样,充当函数的返回值。与return不同之处在于yield可以返回多次值,而return只能返回一次函数就结束了

      2、把函数封装好了__iter__和__next__方法,将函数的执行结果做成了一个迭代器。

      3、遵循迭代器的取值方式。例:obj.__next__(),触发函数的执行,函数暂停与再继续的状态都是由yield保存起来的

      

    例:

    def foo():
        print('start')
        yield    # yield功能与return相似,
                 # 函数运行时碰到yield时函数结束,并用来返回值。yield后面不跟值默认是返回None
                 # 所以第一次g.__next__()返回值就是None,用print打印查看
        print('end')
    
    g = foo()    # 得到生成器,生成器就是迭代器
    # g.__next__() # 结果为start,重点:__next__()一次,则触发foo函数的一次执行。
    
    res = g.__next__()
    print(res) # 结果为None,因为yield后面没有跟任何参数
    

    输出结果:

    start
    None
    View Code

    4、yield与return的区别

    (1)return在一个函数中可以有多个,但是只能执行一次值

    def foo():
        print('---------first---------')
        return 1
        print('---------second--------')
        return 2
        print('---------third---------')
        return 3
    
    foo()   # 一个函数中有多个return时,只会执行一个return,只能执行一次值。
    

     输出结果:

    ---------first---------
    View Code

    (2)yield在一个函数中可以有多个,并且能执行多次值

    当一个函数中有多个yield时,用“__next__()”方法执行一次函数后,函数就暂停住了,再次执行函数时,函数则会接上一次暂停的位置接着往后走。(这就把函数与迭代器的概念结合一块了。)

    例:

    def foo():
        print('---------first---------')
        yield 1
        print('---------second--------')
        yield 2
        print('---------third---------')
        yield 3
        print('---------fourth---------')
    g = foo()
    print(g.__next__()) # 执行第一次函数,得到返回值:1
    
    print(g.__next__())# 接上次暂停的的位置继续执行,得到返回值:2
    
    print(g.__next__())# 接上次暂停的的位置继续执行,得到返回值:3
    
    print(g.__next__())# 接上次暂停的的位置继续执行,但是没有找到yield,
        # 此时__next__()一直在找返回值,但是返回值是由yield控制的。
        # yield与return不同,不写yield它不会默认返回None,它则会报错StopIteration的错误
    

    输出结果:

    ---------first---------
    Traceback (most recent call last):
    1
      File "C:/Users/William/PycharmProjects/Python_Item2/study/day9/生成器可以被for循环遍历.py", line 22, in <module>
    ---------second--------
        print(g.__next__())
    2
    StopIteration
    ---------third---------
    3
    ---------fourth---------
    View Code

    (3)生成器可以被for循环遍历。

    因为通过yield把函数做成了迭代器。迭代器调__iter__()的结果还是其本身。

    例1:

    def foo():
        print('---------first---------')
        yield 1
        print('---------second--------')
        yield 2
        print('---------third---------')
        yield 3
        print('---------fourth---------')
    
    g = foo()
    
    for i in g:  # 虽然循环体内写的pass,但是使用for循环相当于在调__iter__()方法,(for循环的特性)
        pass     # 生成器就是迭代器
                 # 迭代器在调用__iter__()时得到的还是它自己
                 # 当走到函数最后一条不带yield时,for循环自带了捕捉异常的功能。所以不会报错
    

    输出结果:

    ---------first---------
    ---------second--------
    ---------third---------
    ---------fourth---------
    View Code

    5、计数功能

    def countdown(n):
        print('start countdown')
    
        while n > 0:
            yield n
            n -= 1
        print('stop countdown')
    
    g = countdown(5)
    # print(g.__next__())  # 5
    # print(g.__next__())  # 4
    # print(g.__next__())  # 3
    # print(g.__next__())  # 2
    # print(g.__next__())  # 1
    # print(g.__next__())  # 报StopIteration错误。
    
    # 生成器又是迭代器,所以可以用for循环来取值。
    for i in g:  # 用for循环自动捕捉异常
        print(i)
    

    输出结果:

    start countdown
    5
    4
    3
    2
    1
    stop countdown
    View Code

    6、用代码模拟linux命令:tail -f 功能

    tail  -f 功能:动态查看文件新增的内容。

    思路:

      首先以读的方式打开一个文件,将光标移至文件内容的最后位置。

       然后写一个循环读的过程,有两种情况:一种是读到有值,另一种是没有读到值

       最后读到值时则返回,没有读到值则进入下一次循环

    代码如下:

    # tail -f 的功能是动态查看文件新增一行的内容。
    import time
    def tail(file_path,encoding='utf-8'):   # 1、传一个文件路径进来,并指定默认形参(字符编码默认是utf-8)
        with open(file_path,'r',encoding=encoding) as f:
            f.seek(0,2)       # 2、让光标从文件内容的末尾开始,
            while True:      # 3、循环的读
                line = f.readline()  # 4、再开始读就是读新增的内容了。
                if line:     # 5、判断读的内容是否存在,如果为真,表示文件新增了内容,然后打印内容
                    print(line,end='')
                else:       # 5、如果读不到内容,表示未新增内容,则让程序睡0.5秒,此时程序是死循环,加上时间是为了不让程序执行过快而占用内存。
                    time.sleep(0.5)
    
    tail('a.txt')
    

    先运行以上代码,然后打开一个a.txt文件,在a.txt文件的最后一行输入内容,查看效果。

    运行效果:

    往a.txt文件里添加以下两行内容,然后保存

    再查看代码,此时已经实现了tail -f 命令的功能

    7、用代码模拟linux命令:tail  -f | grep  '要过滤的内容'

    (1)先用代码实现grep命令的功能

    能过以上第6小节已了解tail  -f 是动态查看文件新增内容的功能,而grep则是过虑指定内容的功能。

    思路:

      在linux命令中,用“tail -f | grep '要过滤的内容'”一条命令就可以实现功能,但是我们在模拟这一条命令的时候,不能把这一条命令的所有功能都写在一个函数内,必须分为两个函数,一个函数写动态查看命令功能,另一个函数写过滤功能。所以,以下代码先完成过滤的功能

      传两个值,例如lines和patter,lines表示文件中的多行内容,patter表示要过滤的内容,然后循环每一行内容,判断patter是否在lines内,如果是则打印该行内容。

      在python中不管一个功能的作用有多大或多小,都必须要单独写一个函数进行封装。

    代码如下:

    # 过滤功能
    def grep(lines,pattern):   # 接收文件中一行行的内容 ,lines表示文件中的多行内容,pattern表示要过滤的内容
        for line in lines:
            if patter in line:# 判断要过滤的内容是否在文件中,如果是则打印包含要过滤的内容的这行来。
                print(line)
    
    grep(lines,'要过滤的内容') # 传入两个值,一个是文件内容,另一个则是要过滤的内容。
    

    (2)用代码实现linux命令完整功能——tail  -f | grep  '要过滤的内容'的功能'

    但是以上过滤功能的代码在调用时需要传的lines,而lines则必须要先执行tail -f这个功能后,才能得到源源不断的、新增的文件内容。所以此时就要用到生成器功能来实现这条完整的linux命令。

    代码如下:

    import time
    # 定义阶段
    
    # 模拟动态查看文件功能:tail -f
    def tail(file_path,encoding='utf-8'):
        with open(file_path,'r',encoding=encoding) as f:
            f.seek(0,2)
            while True:
                line = f.readline()
                if line:
                    yield line # 表示有内容时,用yield暂停住
                else:
                    time.sleep(0.5)
    
    
    # 模拟过滤功能:grep '要过滤的内容'
    def grep(lines,pattern):
        for line in lines:    # 遍历生成器给值的每一行进行判断
            if pattern in line:
                print(line)
    
    
    # 调用阶段
    g = tail('a.txt')   # 将调用函数的结果赋予变量g,此时变量g为生成器,
                        # 要取值则用g.__next__(),也可以用for循环取值,因为for会自动触发__next__()
    
    grep(g,'error')    # 将生成器和要过滤的内容传给grep函数
    

    运行效果:

    先打开a.txt文件,内容如下:

    运行以上代码后,再往.a.txt文件中添加以下内容并保存:

    此时,得到过滤字符‘error’后的行,内容如下:

    8、用代码模拟linux命令:tail  -f | grep  '要过滤的内容' | grep '想要过滤的内容' | grep '想要过滤的内容'.....

    说明:本小节意思为linux在使用过滤命令时后面可能要接多个过滤的内容。所以在第7小节的例子中不能将第一次过滤的内容打印,而是将它变成生成器。

    例:

    要求:用代码模拟linux命令:tail -f | grep 'error' | grep '404' | grep 'xiaobai',表示过滤出动态文件中包含有‘error’、'404'、'xiaobai'的一行。

    代码如下:

    # 要求:用代码模拟linux命令:tail -f | grep 'error' | grep '404'|grep 'xiaobai'
    import time
    # 定义阶段
    
    # 模拟动态查看文件功能:tail -f
    def tail(file_path,encoding='utf-8'):
        with open(file_path,'r',encoding=encoding) as f:
            f.seek(0,2)
            while True:
                line = f.readline()
                if line:
                    yield line # 表示有内容时,用yield暂停住
                else:
                    time.sleep(0.5)
    
    
    # 模拟过滤功能:grep '要过滤的内容'
    def grep(lines,pattern):
        for line in lines:
            if pattern in line:
                yield line  # 将grep函数变成生成器,以便于向外传多个值。
    
    
    # 调用阶段
    tail_g = tail('a.txt') # tail函数的生成器
    grep_g1 = grep(tail_g,'error') #
    grep_g2 = grep(grep_g1,'404') #
    grep_g3 = grep(grep_g2,'xiaobai')
    
    for line in grep_g3:
        print(line)
    

    运行效果:

    先打开a.txt文件,内容如下:

    运行以上代码后,再往.a.txt文件中添加以下内容并保存:

    最终得到过滤出"error"、"404"、"xiaobai"的行:

     

    三、三元表达式,列表解析,生成器表达式

    1、三元表达式

    定义:一共有三个元素:一个条件,条件成立的情况下产生的值,条件不成立的情况下产生的值

    格式:

    例:比较x和y中最大的值。

    res =x  if  x  >  y  else  y      # 根据条件情况,将值往左右两边放

    print(res)

    之前我们用if判断定义一个三元表达式的代码如下:

    例1:输入两个值通过以下代码进行比较并出最大的值

    while True:
        user_input = input('Welcome to you!Do you want play? yes/no:').strip()
        if user_input == 'yes':
            x = input('X:').strip()
            y = input('Y:').strip()
            if x.isdigit() and y.isdigit():
                x = int(x)
                y = int(y)
                if x > y:
                    print('The bigger number is:%s,so 33[42mX33[0m is the biggest' % x)
                else:
                    print('The bigger number is:%s,so 33[42mY33[0m is the biggest' % y)
            else:
                print('Bitch,what the fuck are you inputting?')
                break
        elif user_input == 'no':
            break
        else:
            print('Please try again')
            continue
    

    哈哈,自己练习一下装饰器,又变着方式玩一下:

    def foo(func):
        def wrapper(*args,**kwargs):
            while True:
                user_input = input('Welcome to you!Do you want play? yes/no:').strip()
                if user_input == 'yes':
                    func(*args,**kwargs)
                    break
                elif user_input == 'no':
                    break
                else:
                    print('Please try again')
                    continue
        return  wrapper
    @foo
    def count():
        x = input('X:').strip()
        y = input('Y:').strip()
        if x.isdigit() and y.isdigit():
            x = int(x)
            y = int(y)
            if x > y:
                print('The bigger number is:%s,so 33[42mX33[0m is the biggest' % x)
            else:
                print('The bigger number is:%s,so 33[42mY33[0m is the biggest' % y)
        else:
            print('Bitch,what the fuck are you inputting?')
    count()
    

    例2:现将以上代码改成三元表达式:

    代码如下:

    while True:
        user_input = input('Welcome to you!Do you want play? yes/no:').strip()
        if user_input == 'yes':
            x = input('X:').strip()
            y = input('Y:').strip()
            if x.isdigit() and y.isdigit():
                x = int(x)
                y = int(y)
                res = x if x  > y else y
                print('The bigger number is:%s' % res)
            else:
                print('Bitch,what the fuck are you inputting?')
                break
        elif user_input == 'no':
            break
        else:
            print('Please try again')
            continue
    

    例3:三元表达式的返回值

     因为res是三元表达式的返回值,所以,三元表达式也可以不返回x和y的值,用来返回其它值:

    代码如下:

    while True:
        user_input = input('Welcome to you!Do you want play? yes/no:').strip()
        if user_input == 'yes':
            x = input('X:').strip()
            y = input('Y:').strip()
            if x.isdigit() and y.isdigit():
                x = int(x)
                y = int(y)
                res = 'X is the biggest' if x  > y else 'Y is the biggest'
                print(res)
            else:
                print('Bitch,what the fuck are you inputting?')
                break
        elif user_input == 'no':
            break
        else:
            print('Please try again')
            continue
    

    例4:用三元表达式写一个比较4个值中的最大值的程序。

    # 比较4个数中最大的数
    num1 = int(input('num1:'))
    num2 = int(input('num2:'))
    num3 = int(input('num3:'))
    num4 = int(input('num4:'))
    
    # 比较num1和num2,得到结果
    def max1_2(num1,num2):
        return num1 if num1 > num2 else num2
    res1 = max1_2(num1,num2)
    
    # 比较num3和num4,得到结果
    def max3_4(num3,num4):
        return num3 if num3 > num4 else num4
    res2 = max3_4(num3,num4)
    
    # 用内置函数max去比较以上两个函数的结果,得到最终结果。
    print(max(res1,res2))
    

     2、列表解析:

    三元表达式的另一种形式)

    列表的优点是取值方便,缺点就是太占用内存。

    格式:

    例:将字串中的每个字符以改成大写的形式放至一个列表中

       s = 'xiaobai'
       res = [i.upper() for i in s]     # 条件成立的情况下将值往左边放,得到列表,循环后面还可以跟条件
       print(res)

    输出结果:

    ['X', 'I', 'A', 'O', 'B', 'A', 'I']                  # 最终得到一个列表的形式。

    例:从用户输入的值中找出大于100的数。

    之前是定义一个新的空列表,再用for循环加上判断条件得到想要的结果:代码如下:

    num1 = int(input('Please input a num1:'))
    num2 = int(input('Please input a num2:'))
    num3 = int(input('Please input a num3:'))
    num4 = int(input('Please input a num4:'))
    num5 = int(input('Please input a num5:'))
    num6 = int(input('Please input a num6:'))
    num7 = int(input('Please input a num7:'))
    num8 = int(input('Please input a num8:'))
    num9 = int(input('Please input a num9:'))
    
    l = [num1,num2,num3,num4,num5,num6,num7,num8,num9,]
    l_new = []
    
    for item in l:
        if item > 100:
            l_new.append(item)
    print(l_new)
    

     现在只要用列表解析的方式就能用简短的代码得到结果。代码如下:

    num1 = int(input('Please input a num1:'))
    num2 = int(input('Please input a num2:'))
    num3 = int(input('Please input a num3:'))
    num4 = int(input('Please input a num4:'))
    num5 = int(input('Please input a num5:'))
    num6 = int(input('Please input a num6:'))
    num7 = int(input('Please input a num7:'))
    num8 = int(input('Please input a num8:'))
    num9 = int(input('Please input a num9:'))
    
    l = [num1,num2,num3,num4,num5,num6,num7,num8,num9,]
    res = [i for i in l if i > 100]
    print(res)
    

    3、生成器表达式

    相比列表解析更省内存

    格式:

      与列表解析相似,只是把中括号换成小括号。列表解析得到的一定是一个列表,而生成器表达式得到的是个生成器。

      例:

        g = (i  for  i  in  range(10000000000000000000000000000))     # 如果用列表解析去产生一个列表,计算机一定会崩溃。

                                     # 循环后面还可以跟条件     

        print(g)    # 打印得到一个生成器

        print(next(g)) # 取值

        print(next(g)) # 取值

        print(next(g)) # 取值

        print(next(g)) # 取值

        ......

  • 相关阅读:
    转 oracle catalog 库常用脚本
    转 【ORACLE】ORA-12537 问题整理
    转 Trace a specific ORA- error
    15%
    MySQL 存储过程
    MySQL 命令行客户机的分隔符
    MySQL 连接join
    MySQL 正则表达式
    MySQL 日期时间函数
    Arthas 快速入门
  • 原文地址:https://www.cnblogs.com/xiaoxiaobai/p/7773649.html
Copyright © 2011-2022 走看看