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 \033[42mX\033[0m is the biggest' % x)
            else:
                print('The bigger number is:%s,so \033[42mY\033[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 \033[42mX\033[0m is the biggest' % x)
        else:
            print('The bigger number is:%s,so \033[42mY\033[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)) # 取值

    ......

 

posted @ 2017-11-02 18:20  奔跑吧小白  阅读(553)  评论(0编辑  收藏  举报
levels of contents