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
代码运行时间装饰器

在不断的学习过程中,我们的代码会越来越多,这个时候我们想计算出代码执行的时长,就可以使用到装饰器。

在实际的生产中,每位程序员都需要相互协作,这就很有可能你开发的函数会被别人调用,这样一来你的函数就等于封板的函数,如果需要调整肯定是需要申请的,为了不改变原有函数的调用方式,还想在原来的函数前后添加功能 ,就可以使用到装饰器,这样通过闭包的装饰器来调用前后的函数完成不改变原有函数功能的情况下实现其它功能。

B:装饰器的原则

开放封闭原则

开放:对扩展是开放的

封闭:对修改是封闭的

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次就锁定,那么用户在登录之前就需要判断次数了,而不是客户登陆的过程中再去判断次数,这样实际需求更加合理。


 ♣三:迭代器

A:初识迭代器

先说明一个小知识点,我们平常在使用xxx.这样的时候,发现只要是带点就可以出来好多方法,其中你可以看到好多__xxx__双下划线的方法,这些带双下划线的方法我们叫“双下方法”,其实我们写代码的时候直接写1+2这样的代码,执行,python就能执行并计算出结果,我们就以为计算机能识别+,-,*,/这样的符号,其实计算机是不能识别这种符号的,但是为什么能执行是因为其实我们执行1+2的时候,python解释器会进行识别,你是要执行加法运算,那么我直接去调用__xxx__这样的方法去执行,然后返回一个结果给你。

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 两个值计算的结果一样,重复了,去掉重复的,只显示一个
集合推导式

 上面我们看到各种推导式,这些推导式就满足筛选基本法和遍历基本法,而且这些推导式都可以把相应括号换成(圆括号),就可以变成生成器表达式,而且实际生产中列表推导式比较多,另外的两个相对比较少。

 

posted on 2018-08-25 12:42  ppc_server  阅读(231)  评论(0编辑  收藏  举报

导航