1. 程式人生 > 其它 >迭代器、生成器、遞迴、三元表達、生成式

迭代器、生成器、遞迴、三元表達、生成式

一、迭代器

概念

1、什麼是迭代器

迭代器就是用來迭代取值的工具,那麼什麼是迭代呢?迭代就是一個重複的過程,但每次重複都是基於上一次結果進行的

# 單純的重複不叫迭代
while True:
  1 + 1
  
# 下面才屬於迭代過程
count = 1
while count < 5:
  print(count)
  count += 1

2、為何要用迭代器

優點:(1)找到一種通用的迭代取值方案==>for 迴圈

(2)惰性計算,節省記憶體

缺點:(1)不能直接取指定的值,只能依次next

(2)迭代器物件是一次性的:一個迭代器物件取值取乾淨了,就不能再繼續取了

3、可迭代物件

內建有__iter__方法的物件都是可迭代物件

'abc'.__iter__()
"abc".__iter__()
[1, 2, 3].__iter__()
(1, 2, 3).__iter__()
{"x": 1, "y": 2}.__iter__()
{1, 2, 3, 4}.__iter__()
open('a.txt',mode='r').__iter__()

呼叫可迭代物件的iter會得到一個返回值,該返回值是迭代器物件

s = 'abc'
iter_s = s.__iter__()
# iter_s 就是s這個可迭代物件的迭代器物件

迭代器物件的特點:

(1)內建有 .next

()方法

(2)內建有.iter()方法

s = "abc"
iter_s = s.__iter__()  # iter_s = iter(s)
print(iter_s.__next__())  # print(next(ites_s)) 輸出a
print(iter_s.__next__())  # 輸出b
print(iter_s.__next__())  # 輸出c
print(iter_s.__next__())  # 再取就會報異常

dic = {'x':1,'y':2}
iter_dic = dic.__iter__()
print(iter_dic.__next__())
print(iter_dic.__next__())  # 取出的是字典的key值

雖然迭代器物件是有.__iter__方法的,但是我們發現,針對迭代器物件呼叫.__iter__得到的依然是之前那個可迭代物件的迭代器

items = [111, 222, 333]

print(len(items))  # items.__len__()

iter_s = iter(items)

print(iter_s.__iter__().__iter__().__iter__() is iter_s)  # 結果沒有什麼變化
 # names = ['egon', 'tom', 'jack', 'lili', 'ttt']
names = {"x":1,"y":2,'z':3}
# 權當我們此時並不知道for迴圈 我們需要將上述列表或字典 遍歷
 iter_names = iter(names)
 while True:
     try:
         print(next(iter_names))
    except StopIteration:
        break
 # 用while迴圈寫的迭代器迴圈

由上面我們大概可以看出for迴圈的工作原理:

(1)調出in後面那個物件的iter方法,得到一個迭代器物件iter_names

(2)x = next(iter_names),然後執行迴圈體程式碼

(3)重複步驟2,直到取完值,丟擲異常,for迴圈會幫我們捕捉異常結束迴圈

names = ['egon', 'tom', 'jack', 'lili', 'ttt']


iter_s1 = iter(names)
print(next(iter_s1))
print(next(iter_s1))
print(next(iter_s1))
print(next(iter_s1))
print(next(iter_s1))

iter_s2=iter(names)  # 當我們想針對同一迭代器物件再次取值就需要再賦值一個迭代器物件

print(next(iter_s2))
print(next(iter_s2))
print(next(iter_s2))
print(next(iter_s2))
print(next(iter_s2))

# 但是我們使用for迴圈針對一個迭代器物件是可以多次取值的,因為在for迴圈內部,每次已經幫我們建好了迭代器

二、生成器

生成器就是迭代器,如何得到一個自定義的迭代器:在函式內一旦存在yield關鍵字,呼叫函式並不會執行函式體程式碼,會返回一個生成器物件,生成器即自定義的迭代器

def func():
    print('one')
    yield 1
    print('two')
    yield 2
    print('three')
    yield 3
    print('第四次')

func() # 此時python內檢測到有yield該函式就算呼叫了也不會執行
g = func()  # 賦值給g
print(g)  # 輸出結果為<generator object func at 0x1025ada50> 
# 會觸發函式體程式碼的執行,然後遇到yield停下來,將yield後的值
# 當做本次呼叫的結果返回
res1=g.__next__()
print(res1)

res2=g.__next__()
print(res2)

res3=g.__next__()
print(res3)

res4=g.__next__()
def my_range(start,stop,step=1):
    # print('start...')
    while start < stop:
        yield start
        start+=step
    # print('end....')


g=my_range(1,5,2) # 1 3
print(next(g))
print(next(g))
print(next(g))



def func():
    res = 0
    while True:
        yield res
        res += 1

g=func()
# print(next(g))
# print(next(g))
# print(next(g))
for i in g:
    print(i)

有了yield關鍵字,我們就有了一種自定義迭代器的實現方式。yield可以用於返回值,但不同於return,函式一旦遇到return就結束了,而yield可以儲存函式的執行狀態掛起函式,用來返回多次值

三、函式遞迴呼叫

1、定義:是函式巢狀呼叫的一種特殊形式

具體是指:在呼叫一個函式的過程中又直接或者間接的呼叫到本身

# 直接呼叫本身
def f1():
  print('is me')
  f1()
f1()


# 間接呼叫本身
def f1()
	print('from f1')
  f2()
  
def f2():
  print('fromf2')
  f1()
  
f1()

# 這種函式造成的迴圈是有上限的 一般上線為1000次

遞迴應該分為兩個階段

1、回溯:一層一層往下呼叫

2、遞推:一層一層向上推出結果

一段程式碼迴圈執行的方式,可以用while for迴圈,另一種方式就是遞迴,遞迴的本質就是迴圈

 while True:
     print(1111)
     print(2222)
     print(3333)
#  方式二:遞迴的本質就是迴圈:
def f1():
	print(1111)
  print(2222)
	print(3333)
     f1()
f1()

2、需要強調的一點是:

遞迴呼叫不應該無限的呼叫下去,必須在滿足某種條件下結束遞迴呼叫

n = 0
while n < 0:
  print(n)
  n+=1
  
def f1(n):
  if n == 0
  	return  # 相當於滿足條件結束程式碼執行
  print(n)
  n+1=1
  f1(n)
  
f1(0)

3、遞迴的兩個階段

回溯:一層一層呼叫下去

遞推:滿足某種結束條件,結束遞迴呼叫,然後一層一層返回

age(5) = age(4) + 10
age(4) = age(3) + 10
age(3) = age(2) + 10
age(2) = age(1) + 10
age(1) = 18

def age(n):
    if n == 1:
        return 18
    return age(n-1) + 10


res=age(5)
print(res)

遞迴應用

# 將下列列表每個值取出來
l = [11, [22, [33, [44, [55, [66, [77, [88, [99]]]]]]]]]
# 如果沒有遞迴函式,我們就需要for遍歷然後判斷它是不是列表,不是列表打印出來,是列表就繼續遍歷

def func(l):
  for item in l:
    if type(item) is list:
      func(item)
    else:
      print(item)
# 當我們需要不停的使用一個邏輯迴圈時,我們就應該反應過來使用遞迴函式

遞迴實現二分法

nums = [3, 7, 9, 13, 21, 33, 57, 63, 78, 99]



def search(nums,find_num):
    print(nums)
    if len(nums) == 0:
        print('not exists')
        return
    mid = len(nums) // 2
    if find_num > nums[mid]:
        # 找右半部分
        search(nums[mid+1:],find_num)

    elif find_num < nums[mid]:
        # 找左半部分
        search(nums[:mid],find_num)
    else:
        print('find it')


search(nums,63)

三元表示式

三元表示式是一種符合邏輯且更簡潔的語法格式

# 針對以下需求
def func(x,y)
	if x > y:
    return x
  else:
    return y
  
res = func(1,2)
print(res)

三元表示式

語法格式:條件成立時要返回的值 if 條件 else 條件不成立時要返回的值

x = 111
y = 222
res = x if x > y else y
print(res)

當然只有兩個條件情況時可以用三元表示式,多於兩種情況就不合適了

生成式

1、列表生成式

# 沒有列表生成式之前我們需要造一個列表
l = []
for i in range:
  if i > 3:
    l.append(i)
    
# 我們可以這麼做
l = [i for i in range(10) if i > 3]
print(l)

# 將下面列表中名字帶_wx的名字組成一個新列表
names = ['jesse','shiki_wx','yuki_wx','kafka_wx']
wx_names = [name for name in names if name.endswith('_wx')]
print(wx_names)

2、字典生成式

dir = 'k%s' %i:i for i in range(3)
print(dir)

3、集合生成式

print({i for i in range(3)})

4、生成器表示式

res = (i for i in range(5))
print(res)  # 此時res是一個生成器
print(next(res))
print(next(res))
with open('a.txt', mode='rt', encoding='utf-8') as f:
    # res = f.read()
    # print(len(res))  # 23個字元

    # res = 0
    # for line in f:
    #     res += len(line)

    # res = sum((len(line) for line in f))
    res = sum(len(line) for line in f)
    print(res)