三大器(装饰器,迭代器,生成器)

装饰器

装饰器本质上就是一个闭包函数,他可以在不改变源码的情况下,增加其他的功能

闭包:内层函数对外层函数的非全局变量的引用叫闭包,他的本质是函数的嵌套

应用场景:小范围的需要给类增加其他功能就可以使用装饰器

插入日志,性能测试,缓存,事务处理等场景

原则:装饰器遵循开放封闭原则

1、对外扩展是开放的
任何一个程序,不可能在设计之初就想好了所有的功能,并且未来不做任何更新和修改.所以我们允许代码扩展、添加新功能
2、对内修改时封闭的
一个程序在修改的时候,可能已经交付给他人使用了,如果这时候我们对其进行修改,很有可能影响其他已经在使用该程序的用户

无参数装饰器

# 性能测试
import time
def timer(func):
    def inner(*args,**kwargs):
        start = time.time()
        ret = func(*args,**kwargs)  # func() 的返回值赋值给 ret
        time.sleep(1)
        print('所用时间:',time.time() - start - 1)
        return ret  # inner函数的返回值 返回给调用者 func1
    return inner

@timer # 没有参数 timer = timer(func1)
def func1(*args,**kwargs):
    print('打印参数:',args)
    print('打印参数:',*args)
    return 'func1 的返回值' # 因为是 func() 调用的所以返回给 func()

print(func1(666,555,444))   # 得到 ret 打印得到 :func1 的返回值

补充:函数的有效信息

def login(username, password):
    """
    函数的注释信息
    :param (参数) username: 用户名
    :param (参数)password: 密码
    :return: (返回值) True 登录成功  False  登录失败
    """
    print(login.__name__)   # 打印这个函数的函数名
    print(login.__doc__)    # 打印这个函数的注释信息
    return username + password


print login('你好', '中国')

#-------------打印结果----------------
login

    函数的注释信息
    :param (参数) username: 用户名
    :param (参数)password: 密码
    :return: (返回值) True 登录成功  False  登录失败
    
你好中国

装饰器修复技术

函数的有效信息通常记录着一些有用信息,但是一旦和装饰器联系在一块的话,就会出现打印为空的现象

def wrapper(func):
    def inner(*args, **kwargs):
        print("在前面执行的代码。。。。")
        func()
        print("在后面执行的代码...")
    return inner


@wrapper
def f():
    """
    这是一个用来测试装饰器修复技术的函数
    """
    print("哈哈哈")


if __name__ == '__main__':
    print("执行的函数名:", f.__name__)
    print("函数的注释:", f.__doc__)

# 打印结果
   执行的函数名: inner
   函数的注释: None

这是因为我们所执行的f() 函数实际上执行的是装饰器中的 inner() 函数,所以打印为空

添加装饰器修复技术@wraps
from functools import wraps # 导入 weaps模块
def wrapper(func):
    @wraps(func)  # 装饰 inner函数
    def inner(*args, **kwargs):
        print("在前面执行的代码。。。。")
        func()
        print("在后面执行的代码...")
    return inner

@wrapper
def f():
    """
    这是一个用来测试装饰器修复技术的函数
    """
    print("哈哈哈")

if __name__ == '__main__':
    print("执行的函数名:", f.__name__)
    print("函数的注释:", f.__doc__)

# 打印结果
    执行的函数名: f
    函数的注释: 这是一个用来测试装饰器修复技术的函数

但在django框架中使用的装饰器修复技术并不是这个

# CBV装饰器:
    from django.utils.decorateors import method_decorator(是给CBV视图的)

# csrf相关的装饰器:

  from django.views.decoraters.csrf import csrf_exempt,csrf_protect

    csrf_exempt  # 取消伪造保护

    csrf_protect  # 当全局没有保护校验时,可以通过其获取保护

   示例:@method_decorator(csrf_exempt)
   注意:不能直接加在  get 或 post 方法上,可以加在 dispatch上或类上(指定装饰的方法)
   @method_decorator(csrf_exempt, name='get')

带参数装饰器

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(True)  # 这是两步 1. timer = outer(True) 将参数传进去
                # 2.@timer == timer=timer(func)
def func():
    print(111)

func()

多个装饰器装饰一个

在函数中如果有装饰器,计算机会将有语法糖的代码和下面被装饰的代码视为一条,所以他才会执行完语法糖后找到被装饰的函数,如果有多个装饰器,则优先执行离函数近的语法糖,最后依次向上执行其他的语法糖

def xxx1(f):    # f = inner2
    def inner1():
        print('''装饰前1''')
        rte = f()
        print('''装饰后1''')
    return inner1
def xxx2(f):  # f = func 函数
    def inner2():
        print('''装饰前2''')
        rte = f()
        print('''装饰后2''')
    return inner2
@xxx1 # func = xxx1(func) * 里面的func = inner2, 外面的最新变量func = inner1
@xxx2 # 优先执行 func = xxx2 (func) * 里面的 func 是下面的函数,外面的func = inner2
def func():
    print('函数')
func()    由此得 func = inner1
结果:
  装饰前1
  装饰前2
  函数
  装饰后2
  装饰后1

在哪里用过装饰器:用户登录
在哪里用过带参数的装饰器:flask的路由系统


迭代 重复做一些事很多次(就现在循环中做的那样)

可迭代对象 Iterable 内部实现 __iter__方法 (可以直接作用于for循环的对象统称为可迭代对象,可以使用isinstance() 来判断 Iterable)

迭代器 Iterator__next__ 方法的就是迭代器,迭代器是从第一个值开始一个一个的取,直到取完才结束,中间只能前进不能后退,也不能生成值可以通过 isinstance()判断对象是否是 Iterator

生成器 具有 yeild方法,生成器是基于迭代器的,不仅能取值,还能生成值.被执行后返回的是一个生成器 生成器的本质就是一个迭代器

生成器

通过列表生成式,我们可以直接创建一个列表.但是,受到内存限制,列表容量可定是有限的.而且,我们创建一个包含100万个元素的列表,不仅占用很大的储存空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了.
在Python中像列表生成器这样 一边循环一边计算的机制称为生成器generator

要创建一个生成器generator 有很多中方法

  • 列表推导式来实现 将 [] 改成 ()
L = [ x for x in range(2) ]
G = ( x for x in range(2))

print(L)
print(G.__next__())
print(G.__next__())
结果:
[0, 1]
0
1
如果再加一个 print G.__next__() 就会报错,因为这里面只有两个,找不到第三个
报错信息:
Traceback (most recent call last):
  File "D:/xxx/生成器.py", line 368, in <module>
    print(G.__next__())
StopIteration

注意:只有调用 __next__一次,就会生成一个值,直到生成最后一个元素,其他时候就是一段普通的代码
不断的调用 __next__ 很不方便,正确的方法是 for 循环,因为生成器也是可迭代对象,而且通过 for 不用关心 StopIteration 的错误

手写 for 循环
G = ( x for x in range(3) )
while 1:
    try:
        print(G.__next__())
    except StopIteration as f:
        print('没了',f.value)
        break       # 跳出循环

结果
0
1
2
没了 None
  • 函数来实现,在定义中包含 yield 关键字

generator 非常强大.如果推算的算法比较复杂,用类似列表生成式的for循环无法实现就可以用函数来实现.
比如,著名的斐波拉契数列(Finonacci) 除去第一个和第二个以外,任意一个数都可有前两个数相加得到
1,1,2,3,5,8,13,21,34........

用函数实现 斐波拉契数列
def fib(max):
    n,a,b = 0,0,1   # 循环次数,ab 前两位数
    li = []
    while n < max:
        a,b = b,a + b
        n = n + 1
        li.append(a)
    return li

通过观察,fib定义了斐波拉契数列的推算规则,可以从第一个元素开始推算出后续任意的元素,他和 generator 的距离 仅有一个yield

def fib(max):
    n,a,b = 0,0,1   # 循环次数,ab 前两位数
    while n < max:
        a,b = b,a + b
        n = n + 1
        yield b    # 不在需要列表来保存,调用一次,生成一个
        yield  '不返回 return'
        return '返回 return'

x = fib(6)
print('return',x)
print( x.__next__() )
print( x.__next__() )
结果:
return <generator object fib at 0x0000019C749541A8>
1
不返回 return

由此可知 函数时顺序执行,遇到 return或最后一行就返回,而生成器的函数,是每次调用 __next__ 的执行,遇到 yield 返回,再次执行,从上一次返回的yield继续执行

迭代器

Python中迭代器的本质上每次调用next()方法都返回下一个元素或抛出StopIteration的容器对象
由于Python中没有“迭代器”这个类,因此具有以下两个特性的类都可以称为“迭代器”类
  1.有__next__()方法,返回容器的下一个元素或抛出StopIteration异常
  2.有__iter__()方法,返回迭代器本身

迭代器和可迭代对象:都可以使用for循环,当然,迭代器内部多了一个 __next__ 方法
判断迭代器和可迭代的方法
1. 判断内部是否含有 __next__ 方法
2. lterable 判断是不是可迭代对象
3. Iterator 判断是不是迭代器

判断是否为可迭代对象
from collections import Iterable
print( isinstance([],Iterable ))
print( isinstance({},Iterable ))
print( isinstance('abc',Iterable ))
print( isinstance((1,2,3),Iterable ))
print( isinstance({1,2,3},Iterable ))
print( isinstance(100,Iterable ))
print( isinstance( range(10),Iterable )) # range
True
True
True
True
True
False
True

# 判断是否为迭代器
from collections import Iterator
print( isinstance([],Iterator ))
print( isinstance({},Iterator ))
print( isinstance('abc',Iterator ))
print( isinstance((1,2,3),Iterator ))
print( isinstance({1,2,3},Iterator ))
print( isinstance(100,Iterator ))
print( isinstance( range(10),Iterator )) # range
print( isinstance(( x for x in range(10)),Iterator ))  # 生成器
print( isinstance(map(lambda x:x,[1,2,3]),Iterator )) # map
False
False
False
False
False
False
False
True
True

range函数 是一个可迭代的,但不是迭代器
map函数 自带迭代器

虽然列表、字符串.....这些不是迭代器,但是我们可以把它变成迭代器

为什么list、dict、str等数据类型不是Iterator?
这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。
Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。

使用 Iter() 可以将迭代对象转化为迭代器

>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True

迭代器小结

  • 凡是可作用于for循环的对象都是Iterable(可迭代对象)类型;
  • 凡是可作用于__next__(next()函数)的对象都是Iterator(迭代器)类型,它们表示一个惰性计算的序列;
  • 集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。
  • Python的for循环本质上就是通过不断调用next()函数实现的
for x in [1, 2, 3, 4, 5]:
    pass
实际上完全等价于:

# 首先获得Iterator对象:
it = iter([1, 2, 3, 4, 5])
# 循环:
while True:
    try:
        # 获得下一个值:
        x = next(it)
    except StopIteration:
        # 遇到StopIteration就退出循环
        break
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容