python迭代器,生成器,裝飾器

裝飾器

  • 不修改被裝飾對象的源代碼
  • 不修改被裝飾對象的調用方式
  • 被裝飾函數的正上方,單獨一行

1、無參裝飾器

import time
def timer(func):
    def wrapper(*args,**kwargs):
        start_time=time.time()
        res=func(*args,**kwargs)
        stop_time=time.time()
        print('run time is %s' %(stop_time-start_time))
        return res
    return wrapper
 
@timer    #把@timer放到foo()函數的定義處,至關於執行了foo=timer(foo)
def foo():
    time.sleep(3)
    print('from foo')
foo()
"""執行結果
from foo
run time is 3.000171661376953
"""
無參裝飾器

2、有參裝飾器

def timer2(ms = 'file'):
    def timer(func):
        def wrapper(*args, **kwargs):
            if ms == 'file':
                name = 'file_name'
                res = func(*args, **kwargs)
                print('%s login successful'%name)
                return res
            elif ms == 'mysql':
                name = 'mysql_name'
                res = func(*args, **kwargs)
                print('%s login successful'%name)
                return res
            else:
                name = 'other_name'
                res = func(*args, **kwargs)
                print('%s login successful'%name)
                return res
        return wrapper
    return timer
 
@timer2(ms = 'file')  #timer2(ms = 'file')返回timer函數引用 就和無參的同樣了
def foo(name):
    print('from foo %s'%name)
foo('rose')
"""
from foo rose
file_name login successful
"""
有參裝飾器

3、多裝飾器

  • 加載順序(outter函數的調用順序):自下而上
  • 執行順序(wrapper函數的執行順序):自上而下
# 疊加多個裝飾器
# 1. 加載順序(outter函數的調用順序):自下而上
# 2. 執行順序(wrapper函數的執行順序):自上而下

def outter1(func1):  # func1=wrapper2的內存地址
    print('加載了outter1')

    def wrapper1(*args, **kwargs):
        print('執行了wrapper1')
        res1 = func1(*args, **kwargs)
        return res1

    return wrapper1


def outter2(func2):  # func2=wrapper3的內存地址
    print('加載了outter2')

    def wrapper2(*args, **kwargs):
        print('執行了wrapper2')
        res2 = func2(*args, **kwargs)
        return res2

    return wrapper2


def outter3(func3):  # func3=最原始的那個index的內存地址
    print('加載了outter3')

    def wrapper3(*args, **kwargs):
        print('執行了wrapper3')
        res3 = func3(*args, **kwargs)
        return res3

    return wrapper3

# 被裝飾函數的正上方,單獨一行
@outter1  # outter1(wrapper2的內存地址)======>index=wrapper1的內存地址
@outter2  # outter2(wrapper3的內存地址)======>wrapper2的內存地址
@outter3  # outter3(最原始的那個index的內存地址)===>wrapper3的內存地址
def index():           # index=outter1(outter2(outter3(index)))
    print('from index')


print('==========================')
index()
"""
加載了outter3
加載了outter2
加載了outter1
==========================
執行了wrapper1
執行了wrapper2
執行了wrapper3
from index
"""
多個裝飾器

迭代器

1、爲什麼要有迭代器?

對於序列類型:字符串、列表、元組,咱們可使用索引的方式迭代取出其包含的元素。但對於字典、集合、文件等類型是沒有索引的,若還想取出其內部包含的元素,則必須找出一種不依賴於索引的迭代方式,這就是迭代器,迭代器有節省內存的好處。mysql

2、什麼是可迭代對象iterable?

可迭代對象指的是內置有__iter__方法的對象,即obj.__iter__,以下sql

  • 'hello'.__iter__
  • (1,2,3).__iter__
  • [1,2,3].__iter__
  • {'a':1}.__iter__
  • {'a','b'}.__iter__
  • open('a.txt').__iter__

3、什麼是迭代器對象iterator?

  • 內置有__iter__方法的對象就是可迭代對象。
  • 內置有__iter__方法和__next__方法的對象就是迭代器對象。
  • 可迭代對象執行obj.__iter__()獲得的結果就是迭代器對象。
  • 迭代器對象指的是即內置有__iter__又內置有__next__方法的對象。
  • 迭代器對象.__iter__()後仍然是迭代器對象自己,文件類型是迭代器對象open('a.txt').__iter__()open('a.txt').__next__()
  • 迭代器遵循迭代器協議 :必須擁有__iter__方法和__next__方法。
  • for循環就是基於迭代器協議提供了一個統一的能夠遍歷全部對象的方法,即在遍歷以前,先調用對象的__iter__方法將其轉換成一個迭代器,而後使用迭代器協議去實現循環訪問,這樣全部的對象就均可以經過for循環來遍歷了

"""
#優勢:
  - 提供一種統一的、不依賴於索引的迭代方式
  - 惰性計算,節省內存
#缺點:
  - 沒法獲取長度(只有在next完畢才知道到底有幾個值)
  - 一次性的,只能日後走,不能往前退,迭代完,就不能再次迭代
"""
dic={'a':1,'b':2,'c':3}
iter_dic=dic.__iter__() #獲得迭代器對象,迭代器對象即有__iter__又有__next__  迭代器對象.__iter__()後仍然是迭代器對象自己
print(iter_dic) #<dict_keyiterator object at 0x0000000000BA99F8>
print(iter_dic.__iter__()) #<dict_keyiterator object at 0x0000000000BD99F8>
print(iter_dic.__iter__() is iter_dic) #True  迭代器對象.__iter__()後仍然是迭代器對象自己
 
print(iter_dic.__next__()) #等同於next(iter_dic)
print(iter_dic.__next__()) #等同於next(iter_dic)
print(iter_dic.__next__()) #等同於next(iter_dic)
# print(iter_dic.__next__()) #拋出異常StopIteration,或者說結束標誌
 
#有了迭代器,咱們就能夠不依賴索引迭代取值了
iter_dic=dic.__iter__()
while 1:
    try:
        k=next(iter_dic)
        print(k,dic[k])
    except StopIteration:   # 須要咱們本身捕捉異常
        break
 
#基於for循環,咱們能夠徹底再也不依賴索引去取值了
dic={'a':1,'b':2,'c':3}
for k in dic:
    print(k, dic[k])
 
print('123123213')
for k in dic:
    print(k, dic[k])
 
#for循環的工做原理
#1:執行in後對象的dic.__iter__()方法,獲得一個迭代器對象iter_dic
#2: 執行next(iter_dic),將獲得的值賦值給k,而後執行循環體代碼
#3: 重複過程2,直到捕捉到異常StopIteration,結束循環
迭代器
dir([1,2].__iter__())#是列表迭代器中實現的全部方法,
dir([1,2])           #是列表中實現的全部方法,都是以列表的形式返回,爲了看的更清楚,分別把他們轉換成集合,而後取差集。

# print(dir([1,2].__iter__()))   # 返回一個列表
# print(dir([1,2]))
print(set(dir([1,2].__iter__()))-set(dir([1,2])))  #{'__length_hint__', '__next__', '__setstate__'}

#列表迭代器中多了三個方法
iter_l = [1,2,3,4,5,6].__iter__()
#獲取迭代器中元素的長度
print(iter_l.__length_hint__())   #6
#根據索引值指定從哪裏開始迭代
print('*',iter_l.__setstate__(4))  #* None
#一個一個的取值
print('**',iter_l.__next__())      #** 5
print('***',iter_l.__next__())     #*** 6


print('__next__' in dir(range(12)))  #False
print('__iter__' in dir(range(12)))  #True

from collections import Iterator
print(isinstance(range(100000000),Iterator))  #  False  驗證range執行以後獲得的結果不是一個迭代器
迭代器方法
from collections import Iterable,Iterator
class Foo:
    def __init__(self,start):
        self.start=start

    def __iter__(self):
        return self

    def __next__(self):
        return 'aSB'
f=Foo(0)
print(isinstance(f,Iterable)) #True 可迭代
print(isinstance(f,Iterator)) #True 迭代器
Iterable,Iterator

生成器Generator

  • 迭代器有兩種:一種是調用方法直接返回的,一種是可迭代對象經過執行 iter方法獲得的
  • 迭代器有的好處是能夠節省內存。
  • 若是在某些狀況下,咱們也須要節省內存,就只能本身寫。本身寫的這個能實現迭代器功能的東西就叫生成器。
  • 生成器本質:迭代器 ( 因此自帶了 __iter__方法和 __next__方法,不須要咱們去實現)
  • 特色:惰性運算,開發者自定義
  • 生成器本質上就是個迭代器,咱們根據本身的想法創造的迭代器,支持for循環

1、生成器表達式

  • 把列表解析的[]換成()獲得的就是生成器表達式
  • 列表解析與生成器表達式都是一種便利的編程方式,只不過生成器表達式更節省內存
  • Python不但使用迭代器協議,讓for循環變得更加通用。大部份內置函數,也是使用迭代器協議訪問對象的。
"""
生成器表達式:
優勢:省內存,一次只產生一個值在內存中
把列表推導式的[]換成()就是生成器表達式
"""
gen_exp = (i for i in range(10))  #生成器表達式
print(gen_exp) #<generator object <genexpr> at 0x0000000000A1DEB8>
# for i in gen_exp:  #取出生成器表達式的值,for循環
#     print(i)
print(gen_exp.__next__()) #next方法
print(gen_exp.__next__())
print(gen_exp.__next__())
生成器表達式
egg_list=['雞蛋%s' %i for i in range(10)] #列表解析
print(egg_list)  #['雞蛋0', '雞蛋1', '雞蛋2', '雞蛋3', '雞蛋4', '雞蛋5', '雞蛋6', '雞蛋7', '雞蛋8', '雞蛋9']

laomuji=('雞蛋%s' %i for i in range(10))#生成器表達式
print(laomuji)  #<generator object <genexpr> at 0x0000000000BA3938>
print(next(laomuji)) #雞蛋0    next本質就是調用__next__
print(laomuji.__next__())   #雞蛋1
print(next(laomuji))   #雞蛋2

#sum函數是Python的內置函數,該函數使用迭代器協議訪問對象,而生成器實現了迭代器協議,因此,能夠直接這樣計算一系列值的和:
print(sum([x ** 2 for x in range(4)]))  #14
#而不用畫蛇添足的先構造一個列表:
print(sum(x ** 2 for x in range(4)))    #14


###################################
def demo():
    for i in range(4):
        yield i

g=demo()

g1=(i for i in g)
g2=(i for i in g1)

print(list(g1),type(g1))  #[0, 1, 2, 3] <class 'generator'>
print(list(g2))  #[]

###################################
def demo():
    for i in range(4):
        yield i

g=demo()

g1=[i for i in g]
g2=(i for i in g1)

print(list(g1),type(g1))  #[0, 1, 2, 3] <class 'list'>
print(list(g2))  #[0, 1, 2, 3]
print(list(g2))  #[]
View Code

2、生成器函數

  • 一個包含yield關鍵字的函數就是一個生成器函數。
  • yield和return同樣能夠從函數中返回值,可是yield又不一樣於return,return的執行意味着程序的結束,只能返回一次,yield能夠返回屢次。
  • 調用生成器函數不會獲得返回的具體的值,而是獲得一個生成器對象。
  • 每一次從這個可迭代對象獲取值,就能推進函數的執行,獲取新的返回值。直到函數執行結束(yield像是擁有可以讓函數暫停的魔力)。
  • 生成器有什麼好處呢?就是不會一會兒在內存中生成太多數據
"""
生成器函數:
只要函數內部包含有yield關鍵字,那麼函數名()的到的結果就是生成器,而且不會執行函數內部代碼
生成器就是迭代器(執行函數獲得生成器)
"""
 
 
def product():
    for i in range(1, 3):
        print("開始生產包子")
        yield "第 %d 屜包子" % (i)
        print("賣包子,買完再生產")
 
 
pro = product()  # 生成一個作包子的生成器,至關於作包子的
print(pro)
p = print(pro.__next__())  # 賣包子的
print('===')
print(pro.__next__())
print('============')
for i in pro:
    print(i)
 
"""
<generator object product at 0x00000000006DDEB8>
開始生產包子
第 1 屜包子
===
賣包子,買完再生產
開始生產包子
第 2 屜包子
============
賣包子,買完再生產
"""
 
def product():
    for i in range(1,3):
        print("開始生產包子")
        yield  "第 %d 屜包子" %(i)
        print("賣包子,買完再生產")
pro = product()  #生成一個作包子的生成器,至關於作包子的
 
while 1:
    try:
        k=next(pro)
        print(k)
    except StopIteration:   # 須要咱們本身捕捉異常
        break
生成器函數
#生成器函數
def my_range():
    print('我是一個生成器函數')
    n = 0
    while 1:
        yield n
        n += 1

#實現開始和結束
def my_range2(start, stop):
    n = start
    while n < stop:
        yield n
        n += 1

#再進一步,實現步長:
def my_range3(start, stop, step):
    n = start
    while n < stop:
        yield n
        n += step

#生成器本質上就是個迭代器,咱們根據本身的想法創造的迭代器,它固然也支持for循環:
for i in my_range3(1, 10, 2):
    print(i)
my_range
import time


def tail(filename):
    f = open(filename)
    f.seek(0, 2) #從文件末尾算起
    while True:
        line = f.readline()  # 讀取文件中新的文本行
        if not line:
            time.sleep(0.1)
            continue
        yield line

tail_g = tail('tmp')
for line in tail_g:
    print(line)
生成器監聽文件輸入的例子
def gen1():
    for c in 'AB':
        yield c
    for i in range(3):
        yield i

print(gen1())   #['A', 'B', 0, 1, 2]

def gen2():
    yield from 'AB'
    yield from range(3)

print(list(gen2()))  #['A', 'B', 0, 1, 2]
在一個生成器中引用另一個生成器。
import pickle
class Course:
    def __init__(self,name,price,period,teacher):
        self.name = name
        self.price = price
        self.period = period
        self.teacher = teacher

def get_course():
    with open('course_info', 'rb') as f:
        while True:
            try:
                course_obj = pickle.load(f)
                yield course_obj
            except EOFError:
                break

cour=get_course()
print(cour)
print(cour.__iter__())
print(cour.__next__())
print(cour.__next__())

for obj in cour:
    print(obj.name)


############################

def get_line():
    with open('a.txt', encoding='utf-8', mode='r') as f:
        while True:
            try:
                line = f.readline()
                yield line
            except StopIteration:
                break
    g=get_line()
    print(g)
    print(g.__next__())
    print(g.__next__())
    print(g.__next__())
生成器-文件

3、協程函數

  • yield能夠返回值,也能夠接收值。
  • 經過生成器的send方法能夠給yield傳值。
  • send兩個做用:1.給yield傳值 2.繼續執行函數
"""
協程函數:
yield關鍵字的另一種使用形式:表達式形式的yield
對於表達式形式的yield,在使用時,第一次必須傳None,g.send(None)等同於next(g)
 
yield總結
一、把函數作成迭代器
二、對比return,能夠返回屢次值,能夠掛起/保存函數的運行狀態
"""
def eater(name):
    print('%s 準備開始吃飯啦' % name)
    food_list = []
    for i in range(4):
        food = yield food_list
        print('%s 吃了 %s' % (name, food))
        food_list.append(food)
        print('while', food_list)
    # print('eater',food_list)   ####這的代碼不會執行,尚未走到這程序就退出了
 
 
g = eater('tom')
print(g)
g.send(None)  # 對於表達式形式的yield,在使用時,第一次必須傳None,g.send(None)等同於next(g)
print('====')
 
g.send('包子')
g.send('米飯')
g.send('麪條')
 
try:
    g.send('稀飯')
except StopIteration:
    g.close() #依舊執行後面的print(g)
    # quit() #後面的print(g)就不執行了
try:
    g.send('肉夾饃')
except StopIteration:
    g.close()
 
print(g)
 
"""
<generator object eater at 0x0000000000BA1BF8>
tom 準備開始吃飯啦
====
tom 吃了 包子
while ['包子']
tom 吃了 米飯
while ['包子', '米飯']
tom 吃了 麪條
while ['包子', '米飯', '麪條']
tom 吃了 稀飯
while ['包子', '米飯', '麪條', '稀飯']
<generator object eater at 0x0000000000BA1BF8>
"""
 
# 單線程一邊發送,一邊執行
import time
 
 
def consumer(name):
    print("%s 準備吃包子啦!" % name)
    while True:
        food = yield
        print("包子[%s]來了,被[%s]吃了!" % (food, name))
 
 
def producer(name):
    c = consumer('顧客A')
    c2 = consumer('顧客B')
    c.__next__()
    c2.__next__()
    print("老闆%s==開始準備作包子啦!" % name)
    for i in range(3):
        time.sleep(1)
        print("作了2個包子!")
        c.send(i)  # 發送的值,就是yield的返回值
        c2.send(i)
 
 
producer("tom")
print('===========')
producer("jack")
 
"""
顧客A 準備吃包子啦!
顧客B 準備吃包子啦!
老闆tom==開始準備作包子啦!
作了2個包子!
包子[0]來了,被[顧客A]吃了!
包子[0]來了,被[顧客B]吃了!
作了2個包子!
包子[1]來了,被[顧客A]吃了!
包子[1]來了,被[顧客B]吃了!
作了2個包子!
包子[2]來了,被[顧客A]吃了!
包子[2]來了,被[顧客B]吃了!
===========
顧客A 準備吃包子啦!
顧客B 準備吃包子啦!
老闆jack==開始準備作包子啦!
作了2個包子!
包子[0]來了,被[顧客A]吃了!
包子[0]來了,被[顧客B]吃了!
作了2個包子!
包子[1]來了,被[顧客A]吃了!
包子[1]來了,被[顧客B]吃了!
作了2個包子!
包子[2]來了,被[顧客A]吃了!
包子[2]來了,被[顧客B]吃了!
"""
協程函數
# def eat(name):
#     print('%s要開始吃了!' % name)
#     while 1:
#         food = yield
#         print('{}在吃{}'.format(name, food))
#
#
# a = eat('alex')
# a.__next__()  # 初始化,讓函數暫停在yield處
# a.send('包子')  # send兩個做用:1.給yield傳值 2.繼續執行函數
# a.send('餃子')

def init(func):  #在調用被裝飾生成器函數的時候首先用next激活生成器
    def inner(*args,**kwargs):
        g = func(*args,**kwargs)
        next(g)
        return g
    return inner

@init
def averager():
    total = 0.0
    count = 0
    average = None
    while True:
        term = yield average
        total += term
        count += 1
        average = total/count


g_avg = averager()
# next(g_avg)   在裝飾器中執行了next方法
print(g_avg.send(10))  #10.0
print(g_avg.send(30))  #20.0
print(g_avg.send(5))   #15.0
  
協程的裝飾器
相關文章
相關標籤/搜索