Python学习 day13

一、可迭代对象和迭代器

1、回顾可以被for循环的对象

list、dic、str、set、tuple、文件句柄f、range()、enumerate()

只有可迭代对象才能被for循环,当我们遇到一个新的变量,不确定能不能for循环时就判断它是否可迭代,那如何判断对象可迭代?

2、双下方法

dir() -- 打印一个对象的所有方法

打印一个列表的所有方法:

print(dir([]))

结果:

['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']

可以看到,前面的方法前后都带有两条下划线,这就是我们说的双下方法

双下方法使用C语言写好的,可以有不同调用方式调用的方法,在实际使用中,我们一般不会直接调用双下方法,而是采用其他的调用方式。

如上面列表中的__add__方法:

print([1, 3] + [1, 2])  # 列表可以直接相加
print([1, 3].__add__([1, 2]))  # 调用__add__方法

结果:

从这个例子可以理解,在实际使用时我们会使用“+”来对两个列表相加,但实际“+”机器是不认识的,在调用时实际上还是调用了__add__方法,根据方法中的步骤执行操作,包括数字的加减也是一样。

3、可迭代对象的秘密

利用dir我们可以得到所有可迭代对象的所有方法,为了找到可迭代的秘密,我们对可迭代对象的方法求交集:

print(set(dir([])) & set(dir(())) & set(dir({})) & set(dir(set([]))))  # dir 获取的结果是集合,转化为set可求交集

结果:

{'__format__', '__doc__', '__ne__', '__setattr__', '__getattribute__', '__contains__', '__ge__', '__str__', '__init__', '__dir__', '__sizeof__', '__delattr__', '__reduce__', '__gt__', '__iter__', '__lt__', '__subclasshook__', '__repr__', '__len__', '__hash__', '__reduce_ex__', '__new__', '__le__', '__eq__', '__class__'}

“可迭代的”英文即“iterable”,在上面的方法中可以找到与这个词相近的方法__iter__方法,这个方法就是可迭代对象的秘密了

检验:

print('__iter__' in dir(range(10)))
print('__iter__' in dir(enumerate([])))
print('__iter__' in dir(int))
print('__iter__' in dir(bool))

结果:

结论:只要是能被for循环的,即可迭代的,就一定拥有__iter__方法

4、迭代器

上面我们已经知道了__iter__方法,那执行__iter__方法后有什么结果?

print([].__iter__())

结果:

iterator即“迭代器”的意思,事实上,在调用__iter__方法后,该方法会返回一个迭代器

再次使用set来计算迭代器与可迭代对象所拥有的方法差异:

print(set(dir([].__iter__())) - set(dir([])))  # 利用set求差集

结果:

{'__setstate__', '__next__', '__length_hint__'}

其中“__next__”就是我们要找的方法了,调用__next__将获取到迭代器的下一个元素

使用__length_hint__查看迭代器的元素个数

print(['momo', 2, 5, [1, 2, 3]].__iter__().__length_hint__())

结果:

4 

判断迭代器的方法中是否有__iter__方法:

print('__iter__' in dir([].__iter__()))

结果:

True

所以迭代器也是可迭代对象,可以被for循环

 

5、可迭代协议和迭代器协议

可迭代协议 -- 只要含有__iter__方法的都是可迭代的(即可被for循环的)

迭代器协议 -- 内部含有__next__和__iter__方法的就是迭代器

5、迭代器取值

  • 使用__next__方法取值
iterator = [1, 4, 'python', [1, 2, 3], {'iter': 'iterator'}].__iter__()
print(iterator.__next__())
print(iterator.__next__())
print(iterator.__next__())
print(iterator.__next__())
print(iterator.__next__())

结果:

迭代器的元素个数是固定的,最后一个值取完后如果再调用__next__就会报错

Traceback (most recent call last):

  File "F:/Users/alice/PycharmProjects/exercise/test/test18.py", line 30, in <module>
  print(iterator.__next__())
StopIteration

  •  使用for循环取值
iterator = [1, 4, 'python', [1, 2, 3], {'iter': 'iterator'}].__iter__()
for i in iterator:
    print(i)

结果:

for循环使用的是for关键字而不是调用某一方法,这里就可以想到,其实for循环实际是调用了某个双下方法,因为双下方法都是通过其他方式调用的,平常for循环是自动结束的,结束的标志即调用__next__报错

  • __next__和for

猜你喜欢

转载自www.cnblogs.com/misutang/p/9831573.html