python三大器

装饰器

装饰器的作用:

  • 装饰器的本质:一个闭包函数 (高阶函数+嵌套函数)
  • 装饰器的功能:在不修改原函数及其调用方式的情况下对原函数功能进行扩展

闭包原理

装饰器执行流程

带多个参数函数

import time
def timer(func):
    def inner(*args,**kwargs):
        start = time.time()
        re = func(*args,**kwargs)
        print(time.time() - start)
        return re
    return inner

@timer   #==> func2 = timer(func2)
def func2(a):
    print('in func2 and get a:%s'%(a))
    return 'fun2 over'

func2('aaaaaa')
print(func2('aaaaaa'))

正常我们情况下查看函数的一些信息的方法在此处都会失效

def index():
    '''这是一个主页信息'''
    print('from index')

print(index.__doc__)    #查看函数注释的方法
print(index.__name__)   #查看函数名的方法

functools的应用

  • functools的wraps能够将原有的函数名返回
from functools import wraps

def deco(func):
    @wraps(func) #加在最内层函数正上方
    def wrapper(*args,**kwargs):
        return func(*args,**kwargs)
    return wrapper

@deco
def index():
    '''哈哈哈哈'''
    print('from index')

print(index.__doc__)
print(index.__name__)

给装饰器带参数

def outer(flag):
    def timer(func):
        def inner(*args,**kwargs):
            if flag:
                print('''执行函数之前要做的''')
            re = func(*args,**kwargs)
            if flag:
                print('''执行函数之后要做的''')
            return re
        return inner
    return timer

@outer(False)
def func():
    print(111)
func()

迭代器

迭代和可迭代协议

可以被for循环的都是可迭代的

 字符串、列表、元组、字典、集合都可以被for循环,说明他们都是可迭代的

 证明:

from collections import Iterable
                             
l = [1,2,3,4]                
t = (1,2,3,4)                
d = {1:2,3:4}                
s = {1,2,3,4}                
                             
print(isinstance(l,Iterable))
print(isinstance(t,Iterable))
print(isinstance(d,Iterable))
print(isinstance(s,Iterable))

结合我们使用for循环取值的现象,再从字面上理解一下,其实迭代就是我们刚刚说的,可以将某个数据集内的数据“一个挨着一个的取出来”,就叫做迭代

可迭代协议

从结果分析原因,能被for循环的就是“可迭代的”,但是for怎么知道谁是可迭代的呢?

假如我们自己写了一个数据类型,希望这个数据类型里的东西也可以使用for被一个一个的取出来,那我们就必须满足for的要求。这个要求就叫做“协议”。

可以被迭代要满足的要求就叫做可迭代协议。可迭代协议的定义非常简单,就是内部实现了__iter__方法。

接下来我们就来验证一下:

print(dir([1,2]))
print(dir((2,3)))
print(dir({1:2}))
print(dir({1,2}))

[.....__iter__.....]
[.....__iter__.....]
[.....__iter__.....]
[.....__iter__.....]

可以被for循环的都是可迭代的,要想可迭代,内部必须有一个__iter__方法.

迭代器协议

'''
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__())
#根据索引值指定从哪里开始迭代
print('*',iter_l.__setstate__(4))
#一个一个的取值
print('**',iter_l.__next__())
print('***',iter_l.__next__())

在for循环中,就是在内部调用了__next__方法才能取到一个一个的值

用迭代器的next方法的遍历。

l = [1,2,3,4]
l_iter = l.__iter__()
item = l_iter.__next__()
print(item)
item = l_iter.__next__()
print(item)
item = l_iter.__next__()
print(item)
item = l_iter.__next__()
print(item)
item = l_iter.__next__()
print(item)
  • 但是一旦超出了迭代的范围就会报错(StopIteration)告诉我们里面没有元素了
  • for循环内部帮助我们捕捉了异常

迭代器遵循迭代器协议:必须拥有__iter__方法和__next__方法。

  • for循环就是基于迭代器协议提供了一个统一的可以遍历所有对象的方法
  • 即在遍历之前,先调用对象的__iter__方法将其转换成一个迭代器
  • 使用迭代器协议去实现循环访问(__next__)方法
  • 这样所有的对象就都可以通过for循环来遍历了

生成器

什么是生成器

我们知道的迭代器有两种:一种是调用方法直接返回的,一种是可迭代对象通过执行iter方法得到的,迭代器有的好处是可以节省内存。

如果在某些情况下,我们也需要节省内存,就只能自己写。我们自己写的这个能实现迭代器功能的东西就叫生成器。

 

Python中提供的生成器:

1.生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重它离开的地方继续执行

2.生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表

 

生成器Generator:

  本质:迭代器(所以自带了__iter__方法和__next__方法,不需要我们去实现)

  特点:惰性运算,开发者自定义

生成器函数

一个包含yield关键字的函数就是一个生成器函数。

yield可以为我们从函数中返回值,但是yield又不同于return,return的执行意味着程序的结束,调用生成器函数不会得到返回的具体的值,而是得到一个可迭代的对象。

每一次获取这个可迭代对象的值,就能推动函数的执行,获取新的返回值。直到函数执行结束。

import time
def genrator_fun1():
    a = 1
    print('现在定义了a变量')
    yield a
    b = 2
    print('现在又定义了b变量')
    yield b

g1 = genrator_fun1()
print('g1 : ',g1)       #打印g1可以发现g1就是一个生成器
print('-'*20)   #我是华丽的分割线
print(next(g1))
time.sleep(1)   #sleep一秒看清执行过程
print(next(g1))

send:

def generator():
    print(123)
    content = yield 1
    print('=======',content)
    print(456)
    yield2

g = generator()
ret = g.__next__()
print('***',ret)
ret = g.send('hello')   #send的效果和next一样
print('***',ret)

#send 获取下一个值的效果和next基本一致
#只是在获取下一个值的时候,给上一yield的位置传递一个数据
#使用send的注意事项
    # 第一次使用生成器的时候 是用next获取下一个值
    # 最后一个yield不能接受外部的值

生成器表达式

把列表解析的[]换成()得到的就是生成器表达式

ret=(x ** 2 for x in range(4))

生成器总结

  • 列表解析与生成器表达式都是一种便利的编程方式,只不过生成器表达式更节省内存
  • Python不但使用迭代器协议,让for循环变得更加通用。大部分内置函数,也是使用迭代器协议访问对象的。例如, sum函数是Python的内置函数,该函数使用迭代器协议访问对象,而生成器实现了迭代器协议,所以,我们可以直接这样计算一系列值的和:
    sum(x ** 2 for x in range(4))

生成器面试题

def add(n,i):
    return n+i

def test():
    for i in range(4):
        yield i

g=test()
for n in [1,10]:
    g=(add(n,i) for i in g)

print(list(g))

迭代器生成器总结

可迭代对象:

  拥有__iter__方法

  特点:惰性运算

  例如:range(),str,list,tuple,dict,set

迭代器Iterator:

  拥有__iter__方法和__next__方法

  例如:iter(range()),iter(str),iter(list),iter(tuple),iter(dict),iter(set),reversed(list_o),map(func,list_o),filter(func,list_o),file_o

生成器Generator:

  本质:迭代器,所以拥有__iter__方法和__next__方法

  特点:惰性运算,开发者自定义

迭代器和生成器
    -迭代器
        -可迭代协议--含有iter方法的都可以迭代的
        -迭代器协议--含有next和iter的都是迭代器
        特点:
            -节省内存空间
            -方便逐个取值,一个迭代器只能取一次
    -生成器 
        所有的生成器都是迭代器
        -含有yield 关键字的函数都是生成器函数
        -生成器函数特点:
            -调用之后函数内的代码不执行,返回生成器
            -每从生成器中取一个值就会执行一段代码,遇见yield就停止
            如何从生成器中取值?
                -for : 如果没有break会一直取完
                -next : 每次只取一次
                -send: 不能用在一第一个,取下一个值的时候给上一个位置传一个新的值
                - 数据类型强制转换:会一次性把所有的数据都读到内存中
        生成器表达式:
            (条件成立想放在生成器中的值 for i in 可迭代的 if 条件)

转自http://www.cnblogs.com/Eva-J/articles/7213953.html

原文地址:https://www.cnblogs.com/chenxuming/p/9489257.html