python基礎篇(三)

PYTHON基礎篇(三)


  • 裝飾器
    • A:初識裝飾器
    • B:裝飾器的原則
    • C:裝飾器語法糖
    • D:裝飾帶參數函數的裝飾器
    • E:裝飾器的固定模式
  • 裝飾器的進階
    • A:裝飾器的wraps方法
    • B:帶參數的裝飾器
    • C:多個裝飾器裝飾一個函數
  • 迭代器
    • A:初識迭代器
    • B:迭代器的優缺點
  • 生成器
    • A:初識生成器
    • B:文件監聽實例 
    • C:生成器函數的進階
    • D:生成器實例(平均值計算器)
    • E:yield from用法
  • 生成器表達式和各類推導式
    • A:生成器表達式
    • B:列表推導式
    • C:字典推導式
    • D:集合推導式

♣一:裝飾器

A:初識裝飾器 

import time
print(time.time())
#執行結果
1533473443.1654115  #這個結果是從1970年到如今的每一秒
def fun():
    start = time.time() #定義一個初始時間
    time.sleep(1)       #由於就打印一句話,這個時間差是計算不出來的,讓函數停1秒,模擬函數執行時間
    print('hello python!!!') #假如我這裏是要打開某個文件,讀取裏面的內容
    end = time.time()   #定義一個結束時間
    print(end - start)  #用結束減去開始的時間,就是程序執行的時間
fun()
#執行結果
# hello python!!!
# 1.000720739364624  #能夠看到花了這麼長時間
這是一個函數,若是是代碼裏面有100個函數,我每個函數都須要加這個,並且這個只是我想看執行時間而已,看完以後確定是會刪除掉的,這個時候就須要一個能在外部直接計算函數時間的函數

import time
def time1(t):   #咱們在外面定義一個函數,吧以前計算函數執行時間差的放外面來
    start = time.time()
    t()     #經過t這個位置參數才傳參
    end = time.time()
    print(end - start)
def fun():
    time.sleep(1)
    print('hello python!!!')
time1(fun)    #經過調用函數來計算fun函數計算的時間
# 執行結果
# hello python!!!
# 1.0004518032073975
這個方式仍是存在問題,100個函數,我每次都要單獨列出函數名來計算,意味着我要time1100遍

import time
def fun():
    time.sleep(1)
    print('hello python!!!')
def fun1():
    time.sleep(1)
    print('hello java!!!')
def time1(t):
    def time2():   #經過閉包的用法,而此處的time1就是一個裝飾器函數
        start = time.time()
        t()          #被裝飾的函數
        end = time.time()
        print(end - start)
    return time2  #這個地方必定不能加括號,若是是time2這樣,就等於調用函數了。
fun2 = time1(fun)
fun2()
fun2 = time1(fun1)   #這樣fun2能夠接收任意time1的參數(函數執行時間)並打印
fun2()
#執行結果
hello python!!!
1.0199263095855713
hello java!!!
1.0145087242126465
代碼運行時間裝飾器

在不斷的學習過程當中,咱們的代碼會愈來愈多,這個時候咱們想計算出代碼執行的時長,就可使用到裝飾器。html

在實際的生產中,每位程序員都須要相互協做,這就頗有可能你開發的函數會被別人調用,這樣一來你的函數就等於封板的函數,若是須要調整確定是須要申請的,爲了避免改變原有函數的調用方式,還想在原來的函數先後添加功能 ,就可使用到裝飾器,這樣經過閉包的裝飾器來調用先後的函數完成不改變原有函數功能的狀況下實現其它功能。java

B:裝飾器的原則

開放封閉原則python

開放:對擴展是開放的程序員

封閉:對修改是封閉的web

C:裝飾器語法糖

import time
def time1(t):
    def time2():   #經過閉包的用法,而此處的time1就是一個裝飾器函數
        start = time.time()
        t()          #被裝飾的函數
        end = time.time()
        print(end - start)
    return time2
@time1   #在此處加上@time1就等於下面fun = time1(fun)這個賦值操做,全部@time1更加方便好用
#語法糖的必定是@裝飾名,語法糖下面是你被裝飾的函數,這樣就造成了一個語法糖
def fun():
    time.sleep(1)
    print('hello python!!!')
#fun = time1(fun)
fun()
執行結果
hello python!!!
1.012437105178833
語法糖
import time
def time1(t):
    def time2():
        start = time.time()
        t()
        end = time.time()
        print(end - start)
    return time2
@time1
def fun():
    time.sleep(1)
    print('hello python!!!')  #在被裝飾的函數裏面是沒有參數和返回值的
#fun = time1(fun)
fun()

咱們給被裝飾的函數加上返回值
import time
def time1(t):
    def time2():
        start = time.time()
        t()
        end = time.time()
        print(end - start)
    return time2
@time1
def fun():
    time.sleep(1)
    print('hello python!!!')  #在被裝飾的函數裏面是沒有參數和返回值的
    return 'java'   #咱們在被裝飾的函數裏面return一個返回值
#fun = time1(fun)
ret = fun()  #咱們將函數賦予一個變量
print(ret) #打印變量的指
# 執行結果
hello python!!!
1.0013034343719482
None
#  能夠看到並無打印咱們返回內容,返回了一個空
這個是由於被裝飾的fun函數已經被@time1給調用了,而且改變了他的值,剛好上面的time2在調用fun的時候是沒有拿fun的return的值,並且time2本身自己也沒有返回值,致使time2調用結束就沒有返回任何值

調整time2調用邏輯
import time
def time1(t):
    def time2():
        start = time.time()
        ret = t()   #並把ret的內容給到t()便可
        end = time.time()
        print(end - start)
        return ret   #爲了下面的ret = fun()能拿到time2裏面的內容,首先要return,讓return拿到上面t()執行的結果
    return time2
@time1
def fun():
    time.sleep(1)
    print('hello python!!!')
    return 'java'
#fun = time1(fun)
ret = fun()   #咱們要保證這個fun()是執行的time2裏面的內容便可
print(ret)
執行結果
hello python!!!
1.0036890506744385
java
爲何是語法糖

D:裝飾帶參數函數的裝飾器

import time
def time1(t):
    def time2(a):  #裝飾器函數裏面也要加上
        start = time.time()
        ret = t(a)  #被裝飾的函數也要加上
        end = time.time()
        print(end - start)
        return ret
    return time2
@time1
def fun(a):   #必定要在函數加上位置參數
    time.sleep(1)
    print('hello python!!!',a) #若是是想在函數裏面加參數
    return 'java'
#fun = time1(fun)
ret = fun(2)
print(ret)
執行結果
hello python!!! 2
1.0001533031463623
java

可是上面的記過只能解決一個參數的狀況,若是是不固定的參數或者按照關鍵字傳參,裝飾器也要跟着改很麻煩
import time
def time1(t):
    def time2(*args,**kwargs):  #裝飾器函數裏面也要加上
        start = time.time()
        ret = t(*args,**kwargs)
        end = time.time()
        print(end - start)
        return ret
    return time2
@time1
def fun(a,b):   #必定要在函數加上位置參數
    time.sleep(1)
    print('hello python!!!',a,b) #若是是想在函數裏面加不少參數
    return 'java'
#fun = time1(fun)
ret = fun(2,b = 1)
print(ret)
執行結果
hello python!!! 2 1  #經過*args和**kwargs就能處理全部的參數
1.0002832412719727
java
裝飾器傳參

E:裝飾器的固定模式

import time
def login(t):  #1:裝飾的名字更改
               #2:若是定義一個裝飾器可使用wrapper_函數名
               #3:裝飾器()這個括號裏面永遠是被裝飾的函數
    def time2(*args,**kwargs):
               #4:若是裝飾器的內部函數,下面的必定要返回;
               #5:內部函數穿進來的動態參數
               #7:此處是被裝飾函數以前完成的事情
        ret = t(*args,**kwargs)
               #5:這個地方必定要原封不動的返回
               #7:被裝飾函數以後完成的事情
        return ret
               #6:且被裝飾的函數必定要原封不動的返回
    return time2 #4:且返回的函數名是不能帶括號的
@login  #1:語法糖也要更改
def fun(a,b): #8:此處有參數
    time.sleep(1)
    print('hello python!!!',a,b)
    return 'java'
fun = login(fun) #8:此處也要給參數
裝飾器的固定模式
用裝飾器把函數執行的結果記錄到一個文件
def recorder(func):
    def inner(*args,**kwargs):
        with open('diary.txt','a',encoding='utf8')as f:
            f.write(func.__name__+'\n') #經過__name__將函數名記錄下來
        ret = func(*args,**kwargs)
        return ret
    return inner
@recorder
def diary():
    print('週一天氣晴')
@recorder
def diary1():
    print('週二天氣陰')
diary()
diary1()
執行結果:
週一天氣晴
週二天氣陰
diary.txt文件內容
diary
diary1
裝飾器記錄儀
#網頁緩存裝飾器,(有緩存就在本地打開,沒有在網頁加載)
編寫思路:
1:首先須要導入os文件操做模塊和網頁處理相關的模塊
2:寫函數體和按照固定模式把裝飾器寫好
3:開始進行符合內容的需求編寫,本地有文件,那咱們須要建立一個文件用來存放網頁內容
4:本地有網頁內容了,咱們須要去判斷網頁內容在本地是否存在,可是文件是咱們手動建立的,不是程序進行建立的,判斷文件名不符合邏輯,須要判斷文件是否爲空
    (判斷文件的大小)便可。
5:怎麼區分是網頁讀取的仍是本地的,須要給本地的加上一個標記,便於區分
import os
from urllib.request import urlopen
def cache(func):
    def inner(*args,**kwargs):
        if os.path.getsize('web_cache'):  #os.path.getsize判斷文件大小
            with open('web_cache','rb')as f:
                return f.read()
        ret = func(*args,**kwargs)
        with open('web_cache','wb')as f:
            f.write(b'***'+ret)  #(b'***'+ret) 給本地讀取的問價加上標記
        return ret
    return inner
@cache
def get(url):
    code = urlopen(url).read()
    return code
ret = get('https://www.autohome.com.cn/beijing/')
print(ret)
ret = get('https://www.autohome.com.cn/beijing/')
print(ret)
執行結果
-0-0-0-0-1.html#pvareaid=3311459" class="more">\xb8\xfc\xb6\xe0 &gt;</a>\r\n 
b'***\r\n\r\n<!DOCTYPE html>\r\n\r\n<html>\r\n<head>\r\n   #本地文件
網頁緩存器

 ♣二:裝飾器的進階緩存

A:裝飾器的wraps方法

def Thunder_shower():
    '''
    若是是我想打印函數的名字Thunder_shower
    :return:
    '''
    print('今天局部雷陣雨')
print(Thunder_shower.__name__)  #使用__name__方法就能夠打印函數的名字,而且是字符串的形式
print(Thunder_shower.__doc__)  #doc方法是答應函數裏面的註釋內容
執行結果:
Thunder_shower

    若是是我想打印函數的名字Thunder_shower
    :return:
打印函數名方法
def wrapper(func):
    def inner(*args,**kwargs):
        ret = func(*args,**kwargs)
        return ret
    return inner
@wrapper
def holiday(day):
    print('今天節假日'%day)
    return '週六'
print(holiday.__name__)   #若是是裝飾器,我在外面使用打印函數名的方法打印的是裝飾器裏面函數的名字
執行結果
inner

那若是我只須要看到我函數名,不須要看到裝飾器名字,可使用下面的方法
from functools import wraps  #可使用from functools import wraps  
def wrapper(func):
    @wraps(func)   #在裝飾器裏面在裝飾下函數,以前咱們使用__name__打印的是inner,咱們在inner上層裝飾func便可正常打印被裝飾的函數名
    def inner(*args,**kwargs):
        ret = func(*args,**kwargs)
        return ret
    return inner
@wrapper
def holiday(day):
    '''這是一個放假通知'''
    print('放假%s天'%day)
    return '週六'
print(holiday.__name__)
print(holiday.__doc__)
ret = holiday(6)
print(ret)
執行結果
holiday   #能夠看到函數名被打印
這是一個放假通知   #函數裏面的註釋經過__doc__方式也打印出來了
放假6天
週六
裝飾器裏面打印函數名

B:帶參數的裝飾器

計算函數執行時間的裝飾器,若是是幻術有100個,前面已經加好了函數執行的時間,而且去使用的語法糖 ,若是是後面我要刪除這個裝飾器,會顯得極爲麻煩,由於你須要註釋或刪除100個語法糖,若是咱們使用帶參數的裝飾器就能夠在外面給裝飾器傳遞一個參數,至關於一個開關,當開關開啓我就執行計算運行時間裝飾器,關閉就不計算,先後不影響函數的使用過程,這個就是咱們要達到目的。閉包

import time
FLAGE = True
def timer_out(flage):#1:下面的@timer_out(會傳一個FLAGE的參數回來)
    def timmer(func): #3:裝飾器按照原來的方式執行了
        def inner(*args,**kwargs):
            if flage:  #4:到這裏開始判斷,等於True我就執行我下面的代碼
                start = time.time()
                ret = func(*args,**kwargs)
                end = time.time()
                print(end-start)
                return ret
            else:    #5:不然,就是等於False,就執行else後面的代碼
                ret = func(*args,**kwargs)
                return ret
        return inner
    return timmer  #2:執行到這裏的時候我return的是timmer,就等於我又執行了@timmer
@timer_out(FLAGE)   #此裝飾器的精髓就在此處:@timer_out==@timmer @timmer == timmer == eggs1(kkkk)
                    #以前函數執行的方式是調用語法糖@timmer這樣去執行,在上面能夠看到我在原裝飾器全部代碼不變的狀況下
                    #加了幾行,這個裏面包含開始的FLAGE = True,中間的if和else行,這個是怎麼執行的了,以前的裝飾器肯
                    #是要按照它以前的執行方式來的,及@timmer == timmer == eggs1(kkkk)這個函數,那麼加了timer_out就
                    #在等號前面又加了一個等號,及@timer_out==@timmer @timmer == timmer == eggs1(kkkk)這樣就又執行
                    #到原來裝飾器的語法糖了。
def eggs1():
    time.sleep(0.02)
    print('kkkkk')

@timer_out(FLAGE)
def eggs2():
    time.sleep(0.02)
    print('hahahha')
eggs1()
eggs2()
等於True的執行結果:
kkkkk
0.02007269859313965
hahahha
0.020559310913085938

等於Flase的狀況:
import time
FLAGE = False
def timer_out(flage):#1:下面的@timer_out(會傳一個FLAGE的參數回來)
    def timmer(func): #3:裝飾器按照原來的方式執行了
        def inner(*args,**kwargs):
            if flage:  #4:到這裏開始判斷,等於True我就執行我下面的代碼
                start = time.time()
                ret = func(*args,**kwargs)
                end = time.time()
                print(end-start)
                return ret
            else:    #5:不然,就是等於False,就執行else後面的代碼
                ret = func(*args,**kwargs)
                return ret
        return inner
    return timmer  #2:執行到這裏的時候我return的是timmer,就等於我又執行了@timmer
@timer_out(FLAGE)   #此裝飾器的精髓就在此處:@timer_out==@timmer @timmer == timmer == eggs1(kkkk)
                    #以前函數執行的方式是調用語法糖@timmer這樣去執行,在上面能夠看到我在原裝飾器全部代碼不變的狀況下
                    #加了幾行,這個裏面包含開始的FLAGE = True,中間的if和else行,這個是怎麼執行的了,以前的裝飾器肯
                    #是要按照它以前的執行方式來的,及@timmer == timmer == eggs1(kkkk)這個函數,那麼加了timer_out就
                    #在等號前面又加了一個等號,及@timer_out==@timmer @timmer == timmer == eggs1(kkkk)這樣就又執行
                    #到原來裝飾器的語法糖了。
def eggs1():
    time.sleep(0.02)
    print('kkkkk')

@timer_out(FLAGE)
def eggs2():
    time.sleep(0.02)
    print('hahahha')
eggs1()
eggs2()
執行結果:
kkkkk
hahahha
總結:爲何會執行,這個是利用了裝飾器是一個閉包函數的原理,以前提過閉包函數能夠調用外部的變量,帶參數的裝飾器也是利用了這個原理
運行時間裝飾器加開關

C:多個裝飾器裝飾一個函數

def eggs1(func):   #2:先執行eggs1裝飾器(放內存)
    def inner1():
        print('eggs,123456')
        func()
        print('eggs1,654321')
    return inner1
def eggs2(func):   #:4:執行eggs2裝飾器(放內存)
    def inner2():
        print('eggs2,123456')
        func()
        print('eggs2,654321')
    return inner2
@eggs2   #2:f=eggs2(f)此時的eggs2執行的時候結果是eggs1執行以後的結果,也就是eggs2(inner1)=inner2
@eggs1   #1:先執行,f=eggs1(f)==inner1
    #執行eggs1函數,由於裝飾器語法糖只能找到離他最近的函數去執行,這個就是此函數的轉折點。
def f():
    print('in f')
f()
#執行結果:
eggs2,123456
eggs,123456
in f
eggs1,654321
eggs2,654321
裝飾器的嵌套執行

 這個裝飾器的用法能夠用於,連續登陸次數計算,超過鎖定的功能,例如客戶登錄,確定是先登錄在計算次數,可是我在外面加上一個次數限制,限制5次就鎖定,那麼用戶在登陸以前就須要判斷次數了,而不是客戶登錄的過程當中再去判斷次數,這樣實際需求更加合理。app


 ♣三:迭代器

A:初識迭代器

先說明一個小知識點,咱們日常在使用xxx.這樣的時候,發現只要是帶點就能夠出來好多方法,其中你能夠看到好多__xxx__雙下劃線的方法,這些帶雙下劃線的方法咱們叫「雙下方法」,其實咱們寫代碼的時候直接寫1+2這樣的代碼,執行,python就能執行並計算出結果,咱們就覺得計算機能識別+,-,*,/這樣的符號,其實計算機是不能識別這種符號的,可是爲何能執行是由於其實咱們執行1+2的時候,python解釋器會進行識別,你是要執行加法運算,那麼我直接去調用__xxx__這樣的方法去執行,而後返回一個結果給你。ide

print(dir([]))
print([1]+[2]) == print([1].__add__([2]))
#執行結果
['__add__', '__class__', '__contains__', '__delattr__', 。。。。。
[1, 2]
[1, 2]
能夠看到上面第一行代碼執行的結果,會出來好多方法
上面第二行出來來的結果是一致的
第二行的執行過程就是當python解釋器讀到+號的時候就去調用內部的__and__
方法,而後把後面的[2]傳進來,獲得結果返回給你,執行方式其實就函數,只
不過這個函數是別人用c語言給你寫到集成到python裏面去了。
雙下方法

 在前面的基礎部分,列表,字典,元祖等均可以在python裏面調出它的方法,使用方法就是print便可。函數

print(set(dir([]))&set(dir({}))&set(dir(''))&set(dir(range(10)))) #求這些類型的共同方法
#執行結果:
#{'__init_subclass__', '__ge__', '__reduce_ex__', '__class__', '__iter__', '__str__', '__init__',。。。。
#其中有一個雙下方法__iter__這個方法,它就是iterable(迭代)英文的簡寫
print('__iter__' in dir(int))
print('__iter__' in dir(bool))
print('__iter__' in dir(list))
print('__iter__' in dir(dict))
print('__iter__' in dir(set))
print('__iter__' in dir(range(1)))
執行結果
False
False
True
True
True
True
結論:就是隻要能被for循環的都會有__iter__方法
for i in 123:
    pass
# 執行結果:
Traceback (most recent call last):
  File "C:/pycharm_fill/mg_迭代器.py", line 31, in <module>
    for i in 123:
TypeError: 'int' object is not iterable
# 能夠發現是報錯的,報錯內容裏面有一個單詞就是iterable,說明這個123是不能循環的
__iter__方法

 經過上面的咱們得出只要是打印方法出來帶__iter__方法都是可迭代的且均可以被for循環,另外只要有__iter__方法就必須遵照可迭代協議。

#__iter__的__next__方法:
k = ['a',1,'wedw']
iter1 = k.__iter__()
print(iter1.__next__())
print(iter1.__next__())
print(iter1.__next__())
執行結果:
a
1
wedw
__next__方法

 既然上面的得出結論[].__iter__()就是一個迭代器,那麼__next__方法就能夠在迭代器裏面一個一個取值。

上面咱們整理了這麼多最後就指向了一個方法就是__iter__和,那麼只要是帶這個方法的就是迭代器,也定義了咱們若是是本身寫的代碼包含__iter__和__next__方法就是一個迭代器

並且這兩個方法少一個都不行。

print('__iter__' in dir([].__iter__()))
print('__next__' in dir([].__iter__()))
執行結果:
True
True
說明列表的方法裏面iter和next方法

from collections import Iterable  #Iterable【迭代器】
from collections import Iterator  #Iterator【可迭代】
print(isinstance([],Iterable))
print(isinstance([],Iterator))
執行結果
True   #是一個迭代器
False  #不是一個可迭代對象

from collections import Iterable  #Iterable【迭代器】
from collections import Iterator  #Iterator【可迭代】
class a:
    def __iter__(self):pass
    def __next__(self):pass

k = a()
print(isinstance(k,Iterable))  #isinstance用於檢查是否有特定的方法
print(isinstance(k,Iterator))
執行結果
True
True

from collections import Iterable  #Iterable【迭代器】
from collections import Iterator  #Iterator【可迭代】
class a:
    def __iter__(self):pass
    #def __next__(self):pass  #註釋next

k = a()
print(isinstance(k,Iterable))
print(isinstance(k,Iterator))
執行結果
True    #對iter沒有影響
False

from collections import Iterable  #Iterable【迭代器】既知足迭代器協議(iter&next)
from collections import Iterator  #Iterator【可迭代】既知足可迭代協議(iter)
class a:
    #def __iter__(self):pass   #註釋iter方法
    def __next__(self):pass

k = a()
print(isinstance(k,Iterable))
print(isinstance(k,Iterator))
執行結果
False 
False   #結果next也不能用了,結果說明next是依託於iter 

 由此也得出了迭代器協議,內部包含iter方法和next方法的就是迭代器

print([].__iter__())
# 執行結果
# <list_iterator object at 0x00000221B9B128D0>
# 一種就是在打印結裏面明確告訴你是一個iterator的時候
#第二種就就是後面直接給你返回了內存地址的有多是迭代器
#第三種,能夠被for循環
print(range(15))
# 執行結果;
# range(0, 15)
# 這種看似沒有返回什麼可用的信息,也有多是一個迭代器
總之要想知道是不是迭代器能夠去判斷他它,固然判斷的結果也就證實了它是否能夠被循環
print('__iter__' in dir(int))
迭代器的幾種形態

B:迭代器的優缺點

迭代器的優點:

1:for循環的本質就是在帶內部的迭代器方法在取值,全部你會以爲for比while好用,由於你不用關心你取的值都在什麼地方,一個個的去取,直到取完爲止。

2:節省內存空間,上面咱們用到了range()方法,爲何就只能打印兩個數,若是你將range賦值給list,你會發現比較慢,到了必定數目就會超出內存報錯,其實就是節省內存加快處理致使的,而for循環會隨着每次循環逐一開闢內存空間,並且咱們每次寫循環都會寫for循環,沒有說迭代器循環,這個是由於單純寫迭代器循環可能會出現報錯,for能夠把這些報錯內部處理了,全部後面寫代碼不須要出現iter和next方法,用for就行。

迭代器的缺點:

1:上面說迭代器其中一個好用的點就是內存的使用機制,但同時也是這個內存使用機制也致使了迭代器比較侷限,例如我不想開闢一大塊內存空間,想用的時候隨時去調用它取值,這個時候迭代器 就有侷限性了,固然可使用函數,裝飾器能解決,但仍是會很麻煩。

針對上面的缺點咱們就須要本身寫迭代器,咱們本身寫的迭代器都叫作生成器。


 ♣四:生成器

A:初識生成器

注意事項:
1:主要函數裏面含有關鍵字yield,它就是一個生成器
2:yield和return同樣不能在函數外面使用,且yield不能和return在一個函數裏面出現,也就是不能共用一個函數
def generator():
    print(1)
    yield 'a'
ret = generator()
print(ret)
執行結果
<generator object generator at 0x0000027F5BF18F68>
3:能夠看到執行以後沒有正常返回和打印我函數裏面的值,而是執行後會獲得一個生成器做爲返回值

def generator():
    print(1)
    yield 'a'
ret = generator()  #此處的ret就是生成器,而整個def函數就是生成器函數
print(ret)
print(ret.__next__()) #上面咱們說了咱們寫的迭代器就是生成器,反過來,咱們的生成器就含有迭代器的iter和next方法
執行結果
1
a
生成器函數
def generator(): #1:執行函數generator
    print(1)  #4:打印1
    yield 'a'#5:把a做爲返回值給到ret
    print(2)
    yield 'b'
ret = generator() #2:接下會執行到這裏獲取一個生成器
print(ret.__next__()) #3:執行打印,打印裏面有next方法,那他就會返回到def generator從頭開始執行
                   #6:最後打印,可是這個打印以後生成器函數不會結束。
# 執行結果
# 1
# a
# 發現我上面的2和b沒有執行。

def generator():
    print(1)
    yield 'a'
    print(2)
    yield 'b'
ret = generator()
print(ret.__next__())
print(ret.__next__()) #只有在此print的時候2和b才能被執行
                     #若是你在print(ret.__next__())就會報錯
執行結果
1
a
2
b
由此咱們能夠得出,生成器函數能夠去控制,能夠在制定的位置中止

def generator():
    print(1)
    yield 'a'
    print(2)
    yield 'b'
ret = generator()
for i in ret:
    print(i)
執行結果
1
a
2
b
上面既然是一個可迭代的函數,那麼就可使用for,能夠看到結果和我上面每次next執行結果一致,惟一不能控制的就是在哪裏中止
yield執行原理
# def generator():
#     for i in range(100000):  #打印10萬個數據
#         yield '每%s秒的數據'%i
# k = generator()
# count = 0
# for i in k:
#     count += 1
#     print(i)
#     if count > 50:  #我能夠指定取數據
#         break
# # 執行結果
# # 每48秒的數據
# # 每49秒的數據
# # 每50秒的數據
# # print('*****',k.__next__())  #並且我還不用去改count > 50的值,生成器會幫忙記住我上次執行到哪裏,我須要在哪裏接着執行
# # 執行結果
# # ***** 每51秒的數據
# #按照上面的原理,我還能夠接着取
# for i in k:
#     count += 1
#     print(i)
#     if count > 100:  #上下兩個數據不會重複打印,是先取完上面的50,讀到這兒繼續取後面的50
#         break
# # 執行結果
# # 每97秒的數據
# # 每98秒的數據
# # 每99秒的數據
# # 每100秒的數據

#那上面咱們既然說帶next方法的就是迭代器,相同的話for也能完成【續】取值的功能
l = [1,2,3,4,5]
for i in l:
    print(i)
    if i == 2:
        break

for i in l:
    print(i)
執行結果:
1
2
1
2
3
4
5
會看到前面1,2的for執行完以後,第二個for是從頭開始的,這個是由於你每執行一個for都是使用到了迭代器方法,可是都是在for內部執行的,你下面的for
和你上面的for沒有關係,同理生成器1和生成器2是沒有關係的
【續】取值

B:文件監聽實例

 經過一個函數去實時監聽一個文件,當文件內容更新實時給返回到屏幕上。

def tail(filename):
    f = open(filename,encoding='utf8')
    while True:
        line = f.readline()
        if line.strip():
            yield line.strip()
k = tail('file')
for i in k:
    print(i)
文件監聽

C:生成器函數的進階

def generator():
    print(123)
    counter = yield 'a' #正常函數執行到這裏就會經過yield將指返回,可是我在
                        #下面使用了send方法,這個方法利用了yield會暫停的特色
                        #而後把我send的指接收,以後再繼續執行下面的代碼
    print('****',counter)
    print(456)
    yield 'b'
k = generator()
ret = k.__next__()
print(ret)
ret = k.send('789') #send能夠在下一個yield以前給傳遞一個參數執行接收以後,在繼續執行個人代碼,send和next的效果同樣
print(ret)
# 執行結果:
# 123
# a
# **** 789
# 456
# b
send方法

send的效果和next的方法基本一致,只是在獲取下一個值的時候,給上一個值的位置傳遞一個數據

send的注意事項:

1:第一次使用生成器的時候,第一個執行必須使用next以後纔可使用send

2:最後一個yield不能接受外部的指,也就是不能使用send方法,若是要使用,請保證你的send不是在最後的一個yield上去執行,能夠在最後用一個yield收尾。

def generator():
    print(123)
    counter = yield 'a' 
    print('****',counter)
    print(456)
    yield 'b'
    ret = yield 'c'
    yield #在最後使用yield收尾,哪怕返回空值

因此send的基本用法就是在開始next和yield結尾之間來完成外部數據的傳遞

D:生成器實例(平均值計算器)

 需求標題是用戶調用函數傳值,將拿到的數字結合前面的數字計算平均值,公式爲:平均值=數字和/個數

def average():
    sum = 0
    count = 0
    avg = 0
    while True:
        num = yield avg  
        sum += num
        count += 1
        avg = sum/count
avg1 = average()
avg1.__next__()
avg2 = avg1.send(20)
avg2 = avg1.send(76)
print(avg2)
avg2 = avg1.send(24)
print(avg2)
執行結果
48.0
40.0

E:yield from用法

例若有一個字符串,字符串的個數足夠大,那要讀這個字符串可能就須要一批一批的讀,而後一個個的返回。

# #常規取值的方法:
# def wget():
#     a = 'qaz'
#     b = '889'
#     for i in a:
#         yield i
#     for i in b:
#         yield i
# w = wget()
# for i in w:
#     print(i)
# #執行結果
# # q
# # a
# # z
# # 8
# # 8
# # 9
#
# #yield from版本
# def wget():
#     a = 'qaz'
#     b = '889'
#     yield from a #yield from是在python3裏面新加的功能,他主要的做用是可以
#                     讓你在一個容器類型數據裏面集體返回,而後下面去一個一個接收
#     yield from b
# w = wget()
# for i in w:
#     print(i)
# #執行結果
# q
# a
# z
# 8
# 8
# 9
yield from用法

 ♣五:生成器表達式和各類推導式

A:生成器表達式

生產器表達式和列表推導式很類似,最直觀的就是中括號換成了園括號
k = (i for i in range(3))
print(k)
for i in k:
    print(i)
#執行結果
<generator object <genexpr> at 0x000001DFE8718F68>
0
1
2
1:能夠看到括號是有區別的
2:返回值不同,能夠看到直接print生成器是不能直接拿到指的
3:節省內存空間
4:功能單一,不能適用於全部的需求
生成器表達式

B:列表推導式

wat_list = ['西瓜%s'%k for k in range(3)]
print(wat_list)
#執行結果
['西瓜0', '西瓜1', '西瓜2']
能夠看到若是是咱們寫這樣的代碼的時候應該是須要append來完成的
# for k in range(10):
#     wat_list.append('習慣%s'%k)#正常是須要一個append的
# print(wat_list)
那爲何上面咱們沒有使用append就能夠完成,是由於上面這個是一個列表推導式
for循環每次循環都會拿到一個指,你想這個循環獲得的指放到這個列表裏是以什麼樣的形式
[i for i in xxx],這個裏面for前面的i加上先後的中括號就是列表推導式,而後你打印出來就行
print([i for i in range(10)])
#執行結果
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
列表推導式
咱們只要學會了列表推導式,能夠把咱們以前不少代碼簡化
例1:
print([i*2 for i in range(10)])
#執行結果
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
例2:
print([i*i for i in range(10)])
#執行結果
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
例3:
print([i/2 for i in range(10)])
#執行結果
[0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5]
例4:
print([i%2 for i in range(10)])
#執行結果
[0, 1, 0, 1, 0, 1, 0, 1, 0, 1]
是否是會以爲有些代碼很簡單,好比你要判斷計算結果是否爲0,爲0的數字打印出來等
列表推導式的基本用法(遍歷基本法)

列表推導式的語法

1:變量名=[遍歷循環的結果 for i in 可迭代元素]  #遍歷基本法
2:變量名=[刪選以後的結果 for i in if 元素和條件] #刪選基本法
# 50之內能被9整除的數字
ret = [i for i in range(50) if i%9==0]
print(ret)
# 執行結果
# [0, 9, 18, 27, 36, 45]
#50之內能被9整除數字的平方
ret = [i*2 for i in range(50) if i%9==0]
print(ret)
#執行結果
[0, 18, 36, 54, 72, 90]
# # 50之內大於20能被9整除數字的平方
ret = [i*2 for i in range(50) if i>20 if i%9==0]
print(ret)
# 執行結果:
# [54, 72, 90]
篩選基本法

C:字典推導式

鍵值對互換
mac = {'a':10,'b':20}
mac1={mac[i]:i for i in mac}
print(mac1)
執行結果
{10: 'a', 20: 'b'}
字典推導式

D:集合推導式

mac = {x*x for x in [4,4,2]}
print(mac)
# 執行結果
# {16, 4}
# 這個裏面利用了集合自帶的去重功能
# 4*4=16,4*4=16 兩個值計算的結果同樣,重複了,去掉重複的,只顯示一個
集合推導式

 上面咱們看到各類推導式,這些推導式就知足篩選基本法和遍歷基本法,並且這些推導式均可以把相應括號換成(圓括號),就能夠變成生成器表達式,並且實際生產中列表推導式比較多,另外的兩個相對比較少。

相關文章
相關標籤/搜索