Python 函數與常用模組 - 迭代器

迭代器

我們已經知道可以直接作用於 for 循環的數據類型有以下幾種:

  • 一類是集合數據類型: list 、 tuple 、 dict 、 set 、 str 、 bytes 等。
  • 另一類是 generator ,包括生成器和帶 yield 的 generator function。

這些可以直接作用於 for 循環的對象,統稱為可迭代的對象( Iterable ):

可迭代的對象,可以把它想成就是 可以循環的對象可迭代 = 可循環

可以使用 isinstance() 判斷一個對象是否為 Iterable 對象

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

from collections import  Iterable

print("[] list 列表是否為可迭代對象:", isinstance([], Iterable))
print("() tuple 元組是否為可迭代對象:", isinstance((), Iterable))
print("{} dict 字典是否為可迭代對象:", isinstance({}, Iterable))
print("( x for i in range(10) ) generator 生成器是否為可迭代對象:", isinstance( ( x for i in range(10) ), Iterable))
print("123 數字是否為可迭代對象:", isinstance(123, Iterable))

---------------執行結果---------------

[] list 列表是否為可迭代對象: True
() tuple 元組是否為可迭代對象: True
{} dict 字典是否為可迭代對象: True
( x for i in range(10)  generator 生成器是否為可迭代對象: True
123 數字是否為可迭代對象: False

Process finished with exit code 0

而生成器不但可以作用於 for 循環,還可以被 __next__() 函數不斷地調用並返回下一個值,直到最後抛出 StopIteration 錯誤,表示無法繼續返回下一個值了。

注意,特重要 可以被 __next__() 函數調用,並不斷地返回下一個值的對象稱為迭代器( Iterator )

可以使用 isinstance() 判斷一個對象是否為 Iterator 對象

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

from collections import Iterator

print("[] list 列表是否為迭代器:", isinstance([], Iterator))
print("() tuple 元組是否為迭代器:", isinstance((), Iterator))
print("{} dict 字典是否為迭代器:", isinstance({}, Iterator))
print("( x for i in range(10) ) generator 生成器是否為迭代器:", isinstance( ( x for i in range(10) ), Iterator))
print("123 數字是否為迭代器:", isinstance(123, Iterator))

---------------執行結果---------------

[] list 列表是否為迭代器: False
() tuple 元組是否為迭代器: False
{} dict 字典是否為迭代器: False
( x for i in range(10) ) generator 生成器是否為迭代器: True
123 數字是否為迭代器: False

Process finished with exit code 0

生成器肯定是迭代器,因為有 __next__() 方法, 但迭代器一定是生成器嗎?答案是不一定

生成器都是 Iterator 對象,但 list、dict、str 雖然是 Iterable,卻不是 Iterator。

那如果想要把 list、dict、str等 Iterable 變成 Iterator的話,可以使用 iter() 函數:

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

from collections import Iterator

print("iter( [] ) list 列表是否為迭代器:", isinstance( iter([]), Iterator) )
print("iter( () ) tuple 元組是否為迭代器:", isinstance( iter(()), Iterator) )
print("iter( {} ) dict 字典是否為迭代器:", isinstance( iter({}), Iterator ) )
print("iter( ( x for i in range(10) ) ) generator 生成器是否為迭代器:", isinstance( iter(( x for i in range(10) ) ), Iterator ) )
print("iter( 123 ) 數字是否為迭代器:", isinstance(iter(123), Iterator))
---------------執行結果---------------

iter( [] ) list 列表是否為迭代器: True
iter( () ) tuple 元組是否為迭代器: True
iter( {} ) dict 字典是否為迭代器: True
iter( ( x for i in range(10) ) ) generator 生成器是否為迭代器: True
Traceback (most recent call last):
  File "/Python/project/interator_pratice.py", line 16, in <module>
    print("iter( 123 ) 數字是否為迭代器:", isinstance(iter(123), Iterator))
TypeError: ‘int‘ object is not iterable

Process finished with exit code 1

創建立一個普通的列表叫 a = [ 1, 2, 3, ], 然後使用 iter(a) 函數,這時候它就是一個迭代器了,然後在賦值給 b = iter(a),再來去用 __next__() 方法去調用它。

>>> from collections import Iterator
>>> a = [ 1, 2, 3 ]
>>> iter(a)
<list_iterator object at 0x10f91ee80>
>>> b = iter(a)
>>> b.__next__()
1
>>> b.__next__()
2
>>> b.__next__()
3
>>> b.__next__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>>

你可能會問,為什麼 list、dict、str 等數據類型不是 Iterator ?

這是因為 Python 的 Iterator 對象表示的是一個數據流, Iterator 對象可以被 __next__() 函數調用,並不斷返回下一個數據,直到沒有數據時,拋出 StopIteration的錯誤;可以把這個數據流看成是一個 有序序列,但我們卻不能提前知道序列的長度,只能不斷地透過 __next__() 函數實現按需計算下一個數據,所以Iterator的計算是 惰性的,只有在需要返回下一個數據時,它才會計算。

Iterator甚至可以表示一個無限大的數據流,例如:全體自然數。而使用 list 是永遠不可能存儲全體自然數的。

小結

  • 凡是可以作用於for循環的對象都是 Iterable 類型
  • 凡是可作用於 __next__() 函數的對象,都是 Iterator 類型,它們表示一個惰性計算的序列
  • 集合數據類型,如: list 、 dict 、 str 等是 Iterable ,但不是 Iterator ,不過可以通過 iter() 函數獲得一個 Iterator 對象。

Python 的 for 循環本質上就是通過不斷調用 next() 函數實現的,例如:

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

for i in [1, 2, 3, 4, 5]:
    print(i)

實際上完全等於下面的代碼

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

# 先獲得 Iterator 對象
it = iter([1, 2, 3, 4, 5])

# 循環
while True:
    try:
        # 獲取下一個值
        x = next(it)

        # 把取到的值給打印出來
        print(x)

    # 遇到 StopIteration 就退出循環
    except StopIteration:
        break

透過上面代碼的解說,可以明白清楚地了解到 Python 3.x 的 range() 函數,其實本身就是迭代器,只是被封裝了起來。

Python 3.6.0 (default, Dec 24 2016, 00:01:50)
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> range(0, 10)
range(0, 10)
>>>

但在 Python 2.7 的 range() 函數,就只是一個列表

Python 2.7.13 (default, Apr  4 2017, 08:46:44)
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> range(0, 10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>>

那在 Python 2.7 中,想要把 range() 函數變成一個迭代器,只要使用 xrange() 函數,就可以把它變成一個迭代器了

Python 2.7.13 (default, Apr  4 2017, 08:46:44)
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> xrange(0, 10)
xrange(10)
>>>

它真是一個迭代器嗎?來…讓我們來驗証

Python 2.7.13 (default, Apr  4 2017, 08:46:44)
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> dir(xrange(0, 10))
[‘__class__‘, ‘__delattr__‘, ‘__doc__‘, ‘__format__‘, ‘__getattribute__‘, ‘__getitem__‘, ‘__hash__‘, ‘__init__‘, ‘__iter__‘, ‘__len__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__reversed__‘, ‘__setattr__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘]
>>> i = xrange(0, 10)
>>> a = iter(i)
>>> dir(a)
[‘__class__‘, ‘__delattr__‘, ‘__doc__‘, ‘__format__‘, ‘__getattribute__‘, ‘__hash__‘, ‘__init__‘, ‘__iter__‘, ‘__length_hint__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__setattr__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘, ‘next‘]
>>> a.next()
0
...(略)
>>> a.next()
9
>>> a.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

好,現在大伙都知道什麼是 可迭代對象迭代器,那接下來考考大家一個問題

a = [ 1, 2, 3 ]
  • Q1: 上面代碼是 可迭代對象 嗎?

Ans: 是。詳解如下

>>> from collections import Iterable
>>> a = [ 1, 2, 3 ]
>>> print("a 是否為可迭代對象:", isinstance(a, Iterable) )
a 是否為可迭代對象: True
  • Q2: 上面代碼是 迭代器 嗎?

Ans: 不是。因為 a 沒有 __next__ 方法,所以不叫迭代器,我們透過 dir() 就可以查出 a 所有可以使用的方法有哪些,詳情如下。

>>> dir(a)
[‘__add__‘, ‘__class__‘, ‘__contains__‘, ‘__delattr__‘, ‘__delitem__‘, ‘__dir__‘, ‘__doc__‘, ‘__eq__‘, ‘__format__‘, ‘__ge__‘, ‘__getattribute__‘, ‘__getitem__‘, ‘__gt__‘, ‘__hash__‘, ‘__iadd__‘, ‘__imul__‘, ‘__init__‘, ‘__init_subclass__‘, ‘__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‘]
>>>
时间: 2024-10-09 08:55:50

Python 函數與常用模組 - 迭代器的相关文章

Python 函數與常用模組 - 生成器並行運算

目前我們已經大致上都了解生成器了,但要怎麼實際應用呢?!接下來就要舉個例子 yield 保存了這個函數的中斷狀態,返回當前這個狀態的值,並且把函數停在這,想什麼時候回來執行就什麼時候回來執行. 通過yield實現單綫程的情況下,實現並發運算的效果 #!/usr/bin/env python3 # -*- coding:utf-8 -*- def consumer(name): print("%s 準備吃包子啦!" %name) while True: baozi = yield # b

Python 函數 Function

函數最初被設計出來,是用來減輕重複 coding 一段相同的代碼,這之間只有代碼 (方法,Method) 的重用,但還沒有物件導向OO整個Object 的屬性與方法被封裝重用的概念. 函數的定義很簡單,使用 def 這保留字,而其宣告方式如下: def 函數名(參數名) : 例如: >>> def print_count(): # 函數宣告...    print('Good')...    print('Morning')...    print('Mr.')...>>&g

OpenERP函數字段的應用

在ERP開發過程中經常會使用到某字段的值是由其他字段計算得來,並且有些還需要將計算的結果存入資料庫. 以上功能上OpenERP中是用field.function實現的 其中有種模式 a). 只計算,不存儲 這種方式比較簡單,只需要設定用來計算值的函數即可,函數分類method和function,method是指當前對象的方法,function是指一般的python函數,有特定簽名的函數 [python] view plaincopy 'amount': fields.function(_amt,

DB2常用函數總結

最近用DB2,数据库之间的差异还是很大的,好多函数都不一样 1.去空格 *DB2中:Ltrim()是去左边空格,rtrim()是去右边空格.*informix中:trim()就是去空格.用法:例:string a="  abc  ";     *DB2中:Ltrim(a)="abc  ";rtrim(a)="  abc",rtrim(ltrim(a))="abc";     *informix中:trim(a)="a

[DE0-NANO] HC-05藍芽模組實現與測試(二)

篇是接續  [DE0-NANO] HC-05藍芽模組實現與測試(一) 本篇主要功能是將RFS板與電腦連接.硬體程式與上一篇相同. 在NIOS軟體部分,我新增了一個SLAVE模式,把RFS板上的HC05設為被動模式(SLAVE). 操作步驟 -------------------------------------------------------------------------- 1. 下載 DE0_Nano.sof 2.斷掉RFS板子電源,按下BT_KEY後在接上RFS板子電源. (此時

對比:莫比烏斯反演與歐拉函數

最近題讓我非常困惑,貌似我現在已經完全分不清楚哪些題用莫比烏斯反演,哪些用歐拉函數. 下面簡單總結一下,莫比烏斯反演處理的是 求segma(gcd(x,y)) 1<=x<=n,1<=y<=m (見<能量項鍊>) gcd(x,y) = k   1<=x<=n 1<=y<=m  求x,y對數 (見<bzoj 2301  problem b>) 莫比烏斯反演原來是解決以上問題2的,大體思路是 設F(a,b,k)表示1<=x<=a

非靜態初始化塊與夠着函數的 執行順序

題目: 子類A繼承父類B,A a=new A();則父類B夠着函數.父類B靜態代碼塊.父類B非靜態代碼塊執行的先後順序是? 正確的執行順序是:杜磊B靜態代碼塊->子類A靜態代碼塊->父類B非靜態代碼塊->父類B構造函數->子類A非靜態代碼塊->子類A構造函數 也就是説非靜態初始化塊的執行順序要在構造函數之前. 1 class SuperClass{ 2 private static String str="Spuer Class Static Variable&qu

學習日記:函數和對象

2016-2-21 1. Living without an aim is like sailing without a compass. 生活沒有目標,猶如航海沒有羅盤. 2. 無論是現實世界還是計算機世界,可讀性都是相當重要的,因為這涉及到人們的意識或者是認識效率,一般文字比數字的可理解性和可讀性要好,圖片的可讀性最強. a) 一般數學是比較抽象的,因為其中充滿著各種阿拉伯數字和已經不能再簡化的希臘字符. b) 數學家的得意之作就是覺得自己是在世界科學界的最巔峰. c) 我們能用數學工具處理

Python(四)装饰器、迭代器&生成器、re正则表达式、字符串格式化

本章内容: 装饰器 迭代器 & 生成器 re 正则表达式 字符串格式化 装饰器 装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志.性能测试.事务处理等.装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用.概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能. 先定义一个基本的装饰器: ########## 基本装饰器 ########## def orter(func):    #定义装饰器     de