迭代对象与迭代器,for循环本质引申生成器知识(含面试题),常用内置方法(***)以及面向过程编程

今日内容


 

迭代器, 可迭代对象, 迭代器对象, for循环内部原理, 生成器, 面试题, 生成器表达式, 内置函数补充, 面向过程编程

迭代器


 

什么是迭代器?

  • 迭代:更新换代(重复)的过程,每次迭代都必须基于上一次的结果
  • 迭代器:迭代取值的工具

为什么要用迭代器?

  • 迭代器给你提供了一种不依赖于索引取值的方式

如何用迭代器?

# 重复且每次迭代都是基于上一次结果而来的

l = [1,2,3,4]

n = 0

while n < len(l):

    print(l[n])

    n += 1

需要迭代取值的数据类型 : 字符串/列表/元祖/字典/集合

 

可迭代对象


 

定义:只要内置有__iter__方法,或者__next__方法的都叫做可迭代对象

补充:针对双下划线开头/双下划线结尾的方法,我们推荐用 双下 + 方法名来读

基本数据类型中,可迭代对象有:

  • str,list,tuple,set,dict
  • 文件对象(执行内置的iter后还是它本身,没有出现任何变化)

注意 :

  • 文件对象本身就是可迭代对象
  • 文件对象为f = open('XXX.txt', 'w', encoding='utf-8')
  • 可迭代对象执行内置的__iter__方法后得到的就是该对象的迭代器对象
l = [1,2,3,4]

# 生成一个迭代器对象

iter_1 = l.__iter__()

# 迭代器取值,调用__next__

print(iter_l.__next__())

print(iter_l.__next__())

print(iter_l.__next__())

print(iter_l.__next__())

# 取完了之后就报错------StopIteration

 

迭代器对象


 

 

定义:

  • 1.内置有__iter__方法
  • 2.内置有__next__方法

ps:迭代器一定是可迭代对象,而可迭代对象不一定是迭代器对象

注意点:可迭代对象仅仅内置有__iter__方法

迭代取值:

  • 优点:
    • 1.不依赖于索引取值
    • 2.内存中永远只占一份空间,不会导致内存溢出
  • 缺点:
    • 1.不能够获取指定的元素
    • 2.取完之后会报错----StopIteration

例子:

d = {'name':'jason','password':123,'hobby':'XXX'}

# 将可迭代对象d转换为迭代器对象

iter_d = d.__iter__()

# 迭代器对象的取值,必须用__next__

print(iter_l.__next__())
f = open('xxx.txt', 'w', encoding='utf-8')

# 调用f内置的__iter__方法

iter_f = f1.__iter__()

print(iter_f1 is f)

迭代器对象执行__iter__方法之后得到的还是迭代器对象本身(重要)

思考:__iter__方法就是用来帮我们生成迭代器对象,而文件对象本身就是迭代器对象,为什么还内置iter方法?


 

 小技巧:

d = {'name':'jason','password':123,'hobby':'XXX'}

iter_d = d.__iter__()

print(iter_d.__next__())

# 异常处理

while True:

  try:

  print(iter_d.__next__())  # 这段代码是可能会报的错

except StopIteration

  print('取值已到达上限')

  break

文件对象迭代取值:

f = open('XXX.txt', 'r', encoding='utf-8')

iter_f = f.__iter__()

print(iter_f.__next__())

迭代器取值的特点:

  • 只能往后依次取,不能往回取值

常规错误点:避免出现print(d.iter().next()),因为它每次都重新开始一段新的迭代

 

for循环的本质


 

常规:

d = {'name':'jason','password':123,'hobby':'XXX'}

for i in d:

    print(i)

试试能否将l迭代:

for i in l:

    pass

iter(l)

注意点:for 循环后面的in, 跟的必须是一个可迭代对象,否则会报错------object is not iterable

for 循环内部的本质:

  • 1.将in后面的对象调用__iter__转换成迭代器对象
  • 2.调用__next__迭代取值
  • 3.内部有异常捕获StopIteration,当用__next__取值时报这个错,则自动结束循环
l1 = [1,2,3,4]

l2 = ['a','b','c','d']

print(zip(l1,l2))  # 这其实就是一个迭代器------返回结果为<zip object at 内存地址>

 

生成器:


 

定义:用户自定义的迭代器,本质就是迭代器

def fun():

    print('first')

    yield  # 函数内如果有yield关键字,那么加括号执行函数的时候并不会触发函数体代码的运行

func()  # 运行结果为空,但是能够初始化生成器,将函数变成迭代器

如果加上g = func():

def fun():

    print('first')

    yield

g = func()

g()  # 这时候就能够执行函数体代码了

这时候用__next__取值:

def fun():

    print('first')

    yield 1  # yield后面跟的值就是调用迭代器next后,你能够得到的值

g = func()

print(g)  # <generator object func at 内存地址>

print(g.__next__())  # 返回结果是 1

yield返回多个值的例子:

def fun():

    print('first')

    yield 666,777,888  # yield既可以返回一个值也可以返回多个值,并且多个值也是按照元祖的形式返回

g = func()

print(g.__next__())  # 返回结果是(666,777,888)

yield有暂停执行的作用:

def fun():

    print('first')

    yield 1

    print('second')

    yield 2

    print('third')

    yield 3

    yield

    yield

g = func()

print(g.next())  # 返回结果是 first 1 

print(g.next())  # 返回结果是 first 1 second 2 

print(g.next())  # 返回结果是 first 1 second 2 third 3 

print(g.next())  # 返回结果是 first 1 second 2 third 3 None

print(g.next())  # 返回结果是 first 1 second 2 third 3 None None

为 for i in range(1,10,1):  

  print(i) 创建一个自定义生成器

def my_range(start,end,step=2):

    while start < end:

        yield start

        start += step

for j in my_range(1,10,2):

    print(j)

yield表达式形式:

注意点:yield支持外界为其传参

def dog(name)

    print('%s 准备开吃'%name)

    while True:

        food = yield

        print('%s 吃了 %s'%(name,food))

# 当函数内有yield关键字时,调用该函数不会执行函数体代码,而是将函数变成生成器

g = dog('egon')

g.next()  # egon 准备开吃

res = g.next()  # 返回值为yield,且必须先将代码运行至yield,才能为其传值

g.send('狗不理包子')  # 给yield左边的变量传参,触发了next方法

g.next()  # egon 吃了狗不理包子

yield:

  • 1.帮你提供了一种自定义生成器方式
  • 2.帮你将函数的运行状态暂停住
  • 3.可以返回值

return之间异同点:

  • 相同点:
    • 都可以返回值,并且都可以返回多个值
  • 不同点:
    • yield可以返回多次值,而return只能返回一次函数立即结束
    • yield还可以接收外部传入的值

 

生成器表达式:


 

res = [i for i in range(1,10) if i != 4]

print(res)  # [1,2,3,5,6,7,8,9]
res = (i for i in range(1,10) if i != 4)  # 生成器表达式

print(res)  # <generator ogject <genexpr> at 内存地址>,它本质上是一个生成器 
print(res.__next__())  # 返回结果为1,再往后就依次返回2,3,5...

下面两种方式,前者太占用内存,而后者则是改进版本:

f = open('XXX.txt', 'r', encoding='utf-8')

data = f.read()

print(len(data))

f.close()
with open('XXX.txt', 'r', encoding='utf-8') as f:

    n = 0

    for line in f:

        n += len(line)

print(n)

在上述改进版本中,用迭代器的知识点来提高效率:

with open('XXX.txt', 'r', encoding='utf-8') as f:

    g = (len(line) for line in f)  # 这行代码本质上是一个迭代器

    print(sum(g))  # 将print(g.next())替换掉了

注意点:

  • 生成器不会主动执行任何一行代码
  • 必须通过__next__触发代码的运行

 

面试题目1(重要):


 

def add(n,i):

    return n + i

def test():

    for i in range(4):

        yield i

g = test()

for n in [1,10]:

    g = (add(n,i) for i in g)  # 此段代码循环结束后,n = 10

# 第一次for循环,g = (add(n,i) for i in test())

# 第二次for循环,g = (add(n,i) for i in (add(n,i) for i in test()))

res = list(g)  # 返回值结果为[20,21,22,23]

# 其中list(g),括号内部是基于for循环的

# 等价于for i in (add(n,i) for i in test()):

# 得到 n = 10 后,再代入add(n,i)计算结果

# 另外补充一点, in后面跟生成器,就会触发 生成器
      

 

面试题目2(重要):


 

def multipliers():

    return [lambda x: i*x for i in range(4)]

print([m(2) for m in multipliers()])  # [6,6,6,6]

至于为何返回值不是[0,2,4,6],

原因:闭包函数的延迟绑定----在内层函数执行时才会绑定变量i

# 将上段代码用常用闭包函数的形式表达出来

def multipliers():

    list1 = []

    for i in range(4):

        def func(x):

            return x*i

        list1.append(func)

    return list1

print([m(2) for m in multipliers()])  # [6,6,6,6]

 

def multipliers():

    list1 = []

    for i in range(4):

        def func(x,i=i):  # 第一个i是func函数的默认参数,第二个i是上面函数的i

            return x*i

        list1.append(func)

    return list1

print([m(2) for m in multipliers()])  # [0,2,4,6]

# 这时就不存在闭包函数的延迟绑定了

 

面试题目3:


 

需求:读取一个文件并返回每行数据的长度

1.常规解决方法:

with open('test1.txt', 'w', encoding='utf-8') as f:

    for line in range(1000):

        f.write(f'www{line}aaa' * (line + 1) + '\n')

2.列表推导式(可处理数据量小的数据,若处理数据量大的文件会导致内存溢出)

res1 = [len(line) for line in open('test1.txt', 'r', encoding='utf-8')]

print(res1)

3.生成器表达式:处理数据量大的文件推荐使用

res2 = (len(line) for line in open('test1.txt', 'r', encoding='utf-8'))

print(res2)  # <generator object <genexpr> at 内存地址>

print(next(res2))  # 开始返回值8 15 22 ...(依次加7)

 

面试题目4:


 

def demo():

    for i in range(4):

        yield i

g = demo()  # <gen...>1 (0,1,2,3)

g1 = (i for i in g)  # <gen...>2 (0,1,2,3)

g2 = (i for i in g1)  # <gen...>3 

print(list(g1))  # [0,1,2,3],这时候list()已经将所有值都获取出来了

print(list(g2))  # []

 

常用内置方法补充:


 

abs

print(abs(-11,11))  # 求绝对值

all,any

l = [0,1,2]

print(all(l))  # False,----只要有一个为Flase就返回Flase

print(any(l))  # True,----只要有一个为True就返回True

bin,oct,hex

print(bin(10))  # 0b1010------将10进制转化为二进制

print(oct(10))  # 0o12------将十进制转化为八进制

print(hex(10))  # 0xa------将十进制转化为十六进制

bool

print(bool(1))  # True

print(bool(0))  # False

encode,bytes

s = 'hello'

print(s.encode('utf-8'))  # b'hello' 

print(bytes(s,encoding='utf-8'))  # b'hello' 

callable:可调用的(可以加括号执行相应功能的)

1 = [1,2,3]

def index():

    pass

print(callable(l))  # False

print(callable(index))  # True

chr:将数字转换成ASCLL码表对应的字符

# 将数字转换成ASCLL码表对应的字符

print(chr(65))  # A

print(chr(97))  # a

ord:

# 将字符按照ASCLL转换为对应的数字

print(ord('a'))  # 97

面向对象需要学习的方法:

classmethod  delattr  getattr  hasattr  issubclass  property  repr  setattr  super  staticmethod

dir: 获取当前对象名称空间里所有它支持的名字

l = [1,2,3]

print(dir(l))

divmod: 分页器

print(divmod(100,11))  # (9,1) 9页余1

# 例子:

total_num,more = divmod(101,10)

    if more:

        total_num += 1

print('总页数:',total_num)  # 总页数:82页

enumerate :枚举

l = ['a','b']

for i,j in enumerate(1,l):

    print(i,j)

# 1 a

# 2 b

eval :只能识别简单的python代码,不支持逻辑代码

exec :能够帮你识别字符串中的代码,然后执行(包括逻辑类代码)

s = " " "

print('hello baby~')

print(1 + 2)

" " "

eval(s)  #  'hello baby~' 3 

# 或者

name = 'jason'

s = " " "

name

" " "

print(eval(s))  # 'jason'

format三种玩法

  • {}占位
  • {index}索引占位
  • {name}指名道姓

 globals,locals

print(globals())  # 无论在哪都是将当前所有全局空间变量名打印出来

print(locals())  # 将当前所在的局部空间变量名打印出来,当前语句在哪个位置,就会返回哪个位置所存储的所有的名字

help

def login():

" " "

XXX

:resturn:

" " ''

print(help(login))  # 显示注释

isinstance

n = 1

print(type(n))

print(isinstance(n,list))  # 推荐的统一用法 : 判断对象是否属于某个数据类型

print(type(n)) is int)  # 以前的常规判断方法

pow

print(pow(10,3))  # 10的3次方

round:四舍五入

print(round(3.49))  # 3

print(round(3.51))  # 4

 

面向过程的编程


 

面向过程编程 : 类似于设计一条流水线

  • 优点:
    • 将复杂的问题流程化,从而简单化
  • 缺点:
    • 可扩展性比较差,一旦需要修改,整体都会收到影响

 

例子 : 注册功能的实现

1.获取用户输入:

def get_info():

    while True:

        username = input('>>>:').strip()

        if username.isalpha(): '''判断字符串必须是字母'''

            print('不能包含数字')

            continue

        password = input('>>>:').strip()

        confirm_password = input('confirm>>>:').strip()

        if password == confirm_password:

            d = {

                   '1':'user',

                    '2':'admin'

            }

            while True:

                print("""

                    1 普通用户

                    2 管理员

                """)

                choice == input('please choice user type to register>>>:').strip()

                if choice not in d:continue

                user_type = d.get(choice)

                operate_data(username,password,user_type)

                break

            else:

                print('两次密码不一致')

2.处理用户信息:

def operate_data(username,password,user_type):

    res = '%s|%s|%s\n'%(username,password,user_type)

    save_data(res,'userinfo.txt')

3.存储到文件中:

def save_data(res,file_name):

    with open(file_name, 'a', encoding='utf-8') as f:

        f.write(res)

4.合成功能:

def register():

    get _info()

register()

 

 

posted @ 2019-07-15 21:58  泡泡茶壶i  阅读(261)  评论(0)    收藏  举报