Python学习第二阶段,day1, 装饰器,生成器,迭代器

装饰器

不得不说,这是对初学者最难以理解的概念了,虽然我学过面向对象,但还是被搞懵逼了。。前面还好理解,主要是后面“装饰器的装饰器”我理解不了。装饰器工厂,根据传入的参数不同去返回不同的装饰器,我不得不在网上查了很多资料去弄懂它,如果没搞清楚这个概念,我是没法安安心心往下学的~~

这里特别要感谢刘志军的这篇文章,完美的解决了我的疑惑:

https://zhuanlan.zhihu.com/p/27449649

装饰器可以在不修改源代码,和原调用方式的基础上,给原有函数增加功能。当然,可以增加功能,只能在原有函数的前面或者后面增加功能,但是没办法改变原有函数的逻辑。要实现一个装饰器,要先掌握几个基本的概念:

1.函数也是一种“变量”    2.高阶函数   3.嵌套函数   4,闭包。

1.函数也是一种“变量”如何理解?

例如,def  sayhi()

      print ("hello,everyone!")

如果打印sayhi,就是0x0000000001ECE730,一个内存地址。

如果调用sayhi(),内存地址后面加个括号就调用这个函数。

函数定义,把函数体当成字符串存放在内存中,用函数名指向它,调用之前,并不会管函数体是什么内容

2.高阶函数。把一个函数名当做参数传给另一个函数,或者返回值中包含函数名

def  dec(func):

  print("in  decorator")

  func()

函数名作为参数传给函数

3,引用计数。

Python内存回收机制,当没有变量名指向某个常量时,即改常量引用计数为0,Python就回收该常量,函数也是一样,没有函数名指向它,或者程序结束就回收该函数体内容

匿名函数  lambda x : x*3 没有函数名指向它,如果给它赋值,calc = lambda x:x*3 就不会马上回收

4.嵌套函数。在函数里定义一个新函数,并返回新函数

def  dec(func):

  def wrapper(*args, **kwargs):

    #  原函数前面增加功能

    res = func(*args,  **kwargs)

    #  原函数后面增加功能

    return res

  return wrapper

这就是个装饰器decorator,在被装饰函数前面加上:func = dec(func)

改成方便的语法糖写法:@dec

5 .闭包closure

def  closure(str):

  def decrator(func):

    def wrapper():

      ....

      if (str)

        func()

      else

        ....

      ....

    return wrapper

  return decrator

@closure(str='1') 相当于  func = closure('1')(func)

@closure(str='2') 相当于  func = closure('2')(func)

把参数给了装饰器,可以生产各种类型的装饰器。。根据传的值str判断

生成器

生成器是一次生成一个值的 特殊类型函数(是一个函数的内存地址)。可以将其视为可恢复函数。调用该函数将返回一个可用于生成连续 x 值的 生成器【Generator】
简单的说就是在函数的执行过程中, yield语句会把你需要的值返回给调用生成器的地方,然后退出函数,下一次调用生成器函数的时候又从上次中断的地方开始执行,而生成器内的所有变量参数都会被保存下来供下一次使用。

列表生成式:gen = [i  for i in range(10)]  生成一个list 名为gen = [0 , 1, 2, 3, 4, 5, 6, 7, 8, 9]这个列表已经保存在内存中,可以根据下标访问其中任意一个元素。

想一下,如果元素有几百万个,是不是都要全部在内存中放着准备取用呢?而且可能并不会用到第几百万个元素,该如何解决内存问题呢??

这时候生成器generator就产生了:

 在上面列表生成式方括号改为圆括号变成一个生成器   gen =  (i   for i in range(10))

这时候gen = generator object <genexpr> at 0x0000000001DF87D8变成了一个地址。当然也就无法通过下标去访问某个元素了。

要访问某个元素就只能通过gen.next()方法去访问。访问一个生成一个。直到出现你想访问的元素。把上面的列表生产式改写一下,换一种写法:

 

def gen(i):
    "i表示长度,而且每次取数据都是从1开始循环,从1取到i,yield返回n并记录该中断点"
    n = 1
    while n <= i:
        yield n
        n += 1
    return "done-----"

genner = gen(10)

#  第一种调用生成器的方式,用whil true + next
while True:  #  循环到第10个以后就不能next了,因为跳出了while循环yield不了,就抛异常StopIteration
        try:
            print(genner.__next__())
        except StopIteration as e:
            print(e.value)
            break

#  第二种调用生成器的方式,用for循环,这里看出gen(10)是一个迭代器了
for i in gen(10):
    print(i)

是不是可以认为,生成器用for循环的方式去访问,就叫做“迭代器”??????

注意:生成器的牛逼之处在于yield关键字,你定义的时候就指明了这个生成器的长度。后面每次next()都是接着上一个netx()的后面。所以可以在next()中间做其他事情,因为不会影响结果。

还可以利用send()给yield关键字传值并激活yield,   next()只能激活yield

生成器的并行计算:

'''
    简单的消费者生产者模型,又是一个简单的协程。


    send(a)的作用是将a的值传给yield,并且激活yield。而next()的作用只是激活yield。


    消费者模型中,有Alex和Lee两个消费者,吃包子的过程实际上是顺序执行的,只是计算机的速度太快


    如果把他们分开显示,可以看成是同步发生的,这就是一个简单的协程
'''

import time

def consummer(name):
    print("%s,准备开始吃包子了!!" % name)
    while True:
        baozi = yield
        print("包子%s 来了,被%s 吃了!" %(baozi, name))

def producer(name):
    c1 = consummer("Alex")
    c2 = consummer("Lee")
    c1.__next__()
    c2.__next__()
    for i in range(6):
        time.sleep(1)
        print("%s 做了包子%s分两半。。。。。。。。" %(name, i))
        c1.send(i)
        c2.send(i)

producer("bob")
------------------------------执行结果-------------------------------------

Alex,准备开始吃包子了!!
Lee,准备开始吃包子了!!
bob 做了包子0分两半。。。。。。。。
包子0 来了,被Alex 吃了!
包子0 来了,被Lee 吃了!
bob 做了包子1分两半。。。。。。。。
包子1 来了,被Alex 吃了!
包子1 来了,被Lee 吃了!
bob 做了包子2分两半。。。。。。。。
包子2 来了,被Alex 吃了!
包子2 来了,被Lee 吃了!
bob 做了包子3分两半。。。。。。。。
包子3 来了,被Alex 吃了!
包子3 来了,被Lee 吃了!
bob 做了包子4分两半。。。。。。。。
包子4 来了,被Alex 吃了!
包子4 来了,被Lee 吃了!
bob 做了包子5分两半。。。。。。。。
包子5 来了,被Alex 吃了!
包子5 来了,被Lee 吃了!

迭代器

1可迭代对象(Iterable): 可直接作用于for循环的对象统称为Iterable可迭代对象,例如 list tuple  dict  set   str 等,判断方法 isinstance(xxx, Iterable)

2.迭代器(Iterator):可以被next()函数调用并不断返回下一个值的对象,它们表示一个惰性的计算序列,可以看出:生成器一定是迭代器,而迭代器不一定是生成器   判断方法:  isinstance(xxx,Iterator)

3.Iterable变为Iterator: iter()方法

from collections import Iterator, Iterable

def gen(i):
    "i表示长度,而且每次取数据都是从1开始循环,从1取到i,yield返回n并记录该中断点"
    n = 1
    while n <= i:
        yield n
        n += 1
    return "done-----"

genner = gen(10)
list1  = [5,2,3,4,5,6,9]

print(isinstance(genner, Iterator))  #  判断是否为迭代器
print(isinstance(genner,Iterable))   #  判断是否为可迭代对象
lst = iter(list1)                    #  用iter()方法把list1变成迭代器
print(lst.__next__())                #  打印结果5

为什么list   dict  str  等不是迭代器???

因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被netx()函数调用并不断返回下一个数据,知道没有数据时抛出StopIteration错误。

可以把数据流看成一个有序序列,但不知道序列的长度,只能不断通过netx()按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要下一个数据时才计算

Iterator甚至可以表示一个无限大的数据流,如全体自然数。而list是永远不可能存储全体自然数的。

 

转载于:https://www.cnblogs.com/revo/p/7201979.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值