装饰器,生成器,迭代器

装饰器

什么是装饰器

  • 装饰器本身是函数,用来给其他函数添加新的功能
  • 特点:不修改调用方式,不修改源代码

为什么使用装饰器

  • 使用方便
  • 节省开发时间

装饰器的使用场景

用户认证,判断用户是否登录

user,passwd = 'aaa','123'
def auth(func):
    def wrapper(username,password,*args,**kwargs):
        if user == username and password == passwd:
            print("User has passed authentication")
            res = func(username,password,*args,**kwargs)   #这里执行func()相当于执行调用的函数如home()
            return res          #为了获得home()函数返回值,可以将执行结果赋值给res然后返回print(home())结果是"from home"而不是"None"了
        else:
            raise ValueError("非合法用户")
    return wrapper

@auth
def home(username,password):
    print("welcome to home page")
    return "from home"

home('aaa','123')

计算函数运行时间(算是一个功能,在项目中用的不多)

记录日志

#! /usr/bin/env python
# -*- coding: utf-8 -*-
from functools import wraps
import traceback
def decoratore(func):
    @wraps(func)
    def log(*args,**kwargs):
        try:
            print("当前运行方法",func.__name__)
            return func(*args,**kwargs)
        except Exception as e:
            print(traceback.format_exc())  # 这里应该调用log模块来记录到日志里
    return log

@decoratore
def test():
    int('a')
    pass

if __name__ == '__main__':
    test()
    
    ''' 上面运行结果
    当前运行方法 test
    Traceback (most recent call last):
      File "C:/Users/tom/Desktop/alipay_demo/aaa/t2.py", line 11, in log
        return func(*args,**kwargs)
      File "C:/Users/tom/Desktop/alipay_demo/aaa/t2.py", line 18, in test
        int('a')
    ValueError: invalid literal for int() with base 10: 'a'
     22222
    '''

redis缓存

第一步:查询redis缓存是否存在这个key
第二步:如果存在这个key,不用去mysql中查询,直接从redis中取出数据即可(减轻了mysql压力)
第三步:如果查询的key不存在,然后到mysql中查询数据,让后设置到redis中,下次查询就有了

# coding:utf-8
from django.core.cache import cache

# 获取redis缓存的装饰器
def redis_cache(key, timeout):
    def __redis_cache(func):
        def warpper(*args, **kw):
            if cache.has_key(key):  # 判断缓存是否存在
                data = cache.get(key)
            else:
                # 若不存在则执行获取数据的方法
                # 注意返回数据的类型(字符串,数字,字典,列表均可)
                data = func(*args, **kw)   # 从数据库查询到数据设置到redis中
                cache.set(key, data, timeout)
            return data
        return warpper
    return __redis_cache

#键值为test,超时时间为60秒
@redis_cache('test', 60)
def get_test_data():
    # 获取Blog模型随机排序前3条数据
    # (Blog模型是我自己的模型,具体代码根据自己需求获取数据)
    # values执行结果,将返回一个字典。字典可以直接存入redis
    # data = Blog.objects.values('id', 'caption').order_by('?')[:3]
    data = '从数据库查询到了数据'
    return data

if __name__ == '__main__':
    get_test_data()

装饰器1----能够适应90%的业务需求

在装饰器中 @timer等价于 test1=timer(test1)

1. 在timer()函数中返回值是return deco

2. 所以timer(test1)作用是将函数test1内存地址当做参数传递给timer()

3. timer() 函数最后将运行后的函数deco内存地址作为返回值返回

4. test1=timer(test1)作用就是将将deco函数内存地址赋值给test1,所以最后运行test1()就相当于运行deco()

5. 所以最后调用时给test2()传入参数就相当于给deco传入参数

import time
def timer(func):   #timer(test1)  func=test1
    def deco(*args,**kwargs):
        start_time = time.time()
        func(*args,**kwargs)      #run test1
        stop_time = time.time()
        print("running time is %s"%(stop_time-start_time))
    return deco
@timer     # test1=timer(test1)
def test1():
    time.sleep(3)
    print("in the test1")
@timer
def test2(name):
    print("in the test2",name)
test1()
test2("tom")

装饰器2----对特定网页进行身份验证

import time
user,passwd = 'aaa','123'
def auth(func):
    def wrapper(*args,**kwargs):
        username = input("Username:").strip()
        password = input("Password:").strip()
        if user == username and password == passwd:
            print("User has passed authentication")
            res = func(*args,**kwargs)   #这里执行func()相当于执行调用的函数如home()
            return res          #为了获得home()函数返回值,可以将执行结果赋值给res然后返回print(home())结果是"from home"而不是"None"了
        else:
            exit("Invalid username or password")
    return wrapper
def index():
    print("welcome to index page")
@auth
def home():
    print("welcome to home page")
    return "from home"
@auth
def bbs():
    print("welcome to bbs page")
index()
print(home())   #在这里调用home()相当于调用wrapper()
bbs()

装饰器3----终极篇:实现对不同网页不同方式的身份认证

@auth(auth_type=“local”)代码作用

  • 在上面的代码中使用@auth相当于先将home函数的内存地址当做变量传入auth()函数,执行结果后home()相当于wrapper()
  • 而在这里验证的时候犹豫@auth(auth_type=“local”)中有()括号,那么就相当于将执行auth()函数而且是将auth_type="local当做参数传入到auth()函数执行
  • 所以outer_wrapper函数也会执行,outer_wrapper函数的执行结果返回的就是wrapper()函数的内存地址
  • 所以最终结果同样是执行home()函数就相当于执行wrapper函数
  • 但是有所不同的是着这个版本中我们可以在外层的auth函数中传入新的参数帮组我们根据需求判断

import time
user,passwd = 'aaa','123'
def auth(auth_type):
    print("auth func:",auth_type)
    def outer_wrapper(func):
        def wrapper(*args, **kwargs):
            print("wrapper func args:", *args, **kwargs)
            if auth_type == "local":
                username = input("Username:").strip()
                password = input("Password:").strip()
                if user == username and passwd == password:
                    print("\033[32;1mUser has passed authentication\033[0m")
                    res = func(*args, **kwargs)  # from home
                    print("---after authenticaion ")
                    return res
                else:
                    exit("\033[31;1mInvalid username or password\033[0m")
            elif auth_type == "ldap":
                print("搞毛线ldap,不会。。。。")

        return wrapper
    return outer_wrapper

def index():
    print("welcome to index page")
@auth(auth_type="local") # home = wrapper()
def home():
    print("welcome to home  page")
    return "from home"

@auth(auth_type="ldap")
def bbs():
    print("welcome to bbs  page")

index()
print(home()) #wrapper()
bbs()

三级装饰器

#! /usr/bin/env python
# -*- coding: utf-8 -*-
import time
def auth(auth_type):
    print("auth func:",auth_type)
    def outer_wrapper(func):
        def wrapper(*args, **kwargs):
            print("wrapper func args:", *args, **kwargs)
            print('运行前')
            func(*args, **kwargs)
            print('运行后')
        return wrapper
    return outer_wrapper

@auth(auth_type="local") # home = wrapper()
def home():
    print("welcome to home  page")
    return "from home"
home()

常用的内置装饰器

  • staticmethod
    类似实现了静态方法 注入以后,可以直接 : 类名.方法
  • property
    经过property装饰过的函数 不再是一个函数,而是一个property,类似实现get,set方法
1 @property
2 def width(self):
3 return self.__width
4 
5 @width.setter
6 def width(self, newWidth):
7 self.__width = newWidth

使用闭包实现装饰器功能

闭包概念

  • 在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用,这样就构成了一个闭包
  • 一般情况下,在我们认知当中,如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失
  • 但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束
#! /usr/bin/env python
# -*- coding: utf-8 -*-
import time

def timer(func):                    #timer(test1)  func=test1
    def deco(*args,**kwargs):       # # 函数嵌套
        start_time = time.time()
        func(*args,**kwargs)        # 跨域访问,引用了外部变量func   (func实质是函数内存地址)
        stop_time = time.time()
        print "running time is %s"%(stop_time-start_time)
    return deco                    # 内层函数作为外层函数返回值

def test(name):
    print "in the test2",name
    time.sleep(2)

test = timer(test)   # 等价于 ==》 @timer语法糖
test("tom")
'''
运行结果:
in the test2 tom
running time is 2.00302696228
'''

python装饰器补充之functools包中的wraps

Python被装饰函数其实已经是另外一个函数

#! /usr/bin/env python
# -*- coding: utf-8 -*-
def login_required(view_func):
    def wrapper(*args,**kwargs):
        pass
    return wrapper

@login_required
def test1():
    '''test1...'''
    print('test1')

print (test1.__name__)   # 结果:wrapper  (标识test1函数已经变成wrapper函数了)

使用functools的wrap,它能保留原有函数的名称

#! /usr/bin/env python
# -*- coding: utf-8 -*-
from functools import wraps

def login_required(view_func):
    @wraps(view_func)
    def wrapper(*args,**kwargs):
        pass
    return wrapper

@login_required
def test1():
    '''test1...'''
    print('test1')

print (test1.__name__)   # 结果:test1  (解决了改变函数的问题,能保留原有函数的名称)

生成器

生成器的定义

  • 生成器,即生成一个容器
  • 生成器就是一个特殊的迭代器
  • 在Python中,一边循环,一边计算的机制,称为生成器。
  • 生成器可以理解为一种数据类型,这种数据类型自动实现了迭代器协议(其他数据类型需要调用自己的内置iter() 方法或__iter__()的内置函数)所以,生成器就是一个可迭代对象

为什么使用生成器

  • 节省空间
  • 高效

生成器的工作原理

  • 生成器是这样一个函数,它记住上一次返回时在函数体中的位置
  • 对生成器函数的第二次(或第 n 次)调用跳转至该函数中间,而上次调用的所有局部变量都保持不变
  • 生成器不仅“记住”了它数据状态;生成器还“记住”了它在流控制构造(在命令式编程中,这种构造不只是数据值)中的位置
  • 生成器是一个函数,而且函数的参数都会保留
  • 迭代到下一次的调用时,所使用的参数都是第一次所保留下的,即是说,在整个所有函数调用的参数都是第一次所调用时保留的,而不是新创建的

生成器的场景应用?

  1. 生成器是一个概念,我们平常写代码可能用的并不多,但是python源码大量使用
  2. 比如我们tornado框架就是基于 生成器+协程
  3. 在我们代码中使用举例
    比如我们要生成一百万个数据,如果用生成器非常节省空间,用列表浪费大量空间
import time
t1 = time.time()
g = (i for i in range(100000000))
t2 = time.time()
lst = [i for i in range(100000000)]
t3 = time.time()
print('生成器时间:',t2 - t1)  # 生成器时间: 0.0
print('列表时间:',t3 - t2)    # 列表时间: 5.821957349777222

yield生成器运行机制

  1. 在Python中,yield就是这样的一个生成器。
  2. 当你问生成器要一个数时,生成器会执行,直至出现 yield 语句,生成器把yield 的参数给你,之后生成器就不会往下继续运行
  3. 当你问他要下一个数时,他会从上次的状态开始运行,直至出现yield语句,把参数给你,之后停下。如此反复
  4. 在python中,当你定义一个函数,使用了yield关键字时,这个函数就是一个生成器
  5. 它的执行会和其他普通的函数有很多不同,函数返回的是一个对象,而不是你平常所用return语句那样,能得到结果值。如果想取得值,那得调用next()函数
  6. 每当调用一次迭代器的next函数,生成器函数运行到yield之处,返回yield后面的值且在这个地方暂停,所有的状态都会被保持住,直到下次next函数被调用,或者碰到异常循环退出

使用yield函数实现斐波那契数列

def fib(max_num):
    a,b = 1,1
    while a < max_num:
        yield b
        a,b=b,a+b

g = fib(10)               #生成一个生成器:[2, 3, 5, 8, 13]
print(g.__next__())       #第一次调用返回:1
print(list(g))   

生成器读写文件:

#!/usr/bin/python
# -*- coding: utf-8 -*-
def read_big_file_v(fname):
    block_size = 1024 * 8
    with open(fname,encoding="utf8") as fp:
        while True:
            chunk = fp.read(block_size)
            # 当文件没有更多内容时,read 调用将会返回空字符串 ''
            if not chunk:
                break
            print(chunk)
path = r'C:\aaa\luting\edc-backend\tttt.py'
read_big_file_v(path)

yield实现单线程下的并发效果

  • yield相当于 return 返回一个值,并且记住这个返回的位置,下次迭代时,代码从yield的下一条语句开始执行。
  • send() 和next()一样,都能让生成器继续往下走一步(下次遇到yield停),但send()能传一个值,这个值作为yield表达式整体的结果

迭代器

迭代器的定义

  • 迭代器是访问集合元素的一种方式
  • 所谓的迭代器就是具有next方法的对象。
  • 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。使用inter()函数创建迭代器
  • 迭代器仅是一容器对象,它实现了迭代器协议

迭代器的两个基本方法:

① next方法
  返回容器的下一个元素
  
② __iter__方法
  返回迭代器自身

  • 在调用next方法时,迭代器会返回它的下一个值,如果next方法被调用,但迭代器中没有值可以返就会引发一个StopIteration异常
a = iter([1,2,])              #生成一个迭代器
print(a.__next__())
print(a.__next__())
print(a.__next__())           #在这一步会引发  “StopIteration” 的异常

迭代器和可迭代对象

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

迭代器和生成器的区别

  • 在使用生成器时,创建一个函数,在使用迭代器时,使用内置函数iter()和next(),在生成器中,使用关键字‘yield’来每次生成/返回一个对象
  • 每次’yield’暂停循环时,生成器会保存本地变量的状态,而迭代器并不会使用局部变量,只需要一个可迭代对象进行迭代
  • 使用类可以实现迭代器,但无法实现生成器,生成器运行速度快,语法简单,迭代器更能节约内存
list = [1,2,3,4,5]  # 列表是一个可迭代对象,不是一个迭代器
print dir(list)  # 所以 list 中有 __iter__() 方法,没有 __next__()方法
iter_obj = list.__iter__()  # __iter__()方法返回迭代器对象本身(这个迭代器对象就会有 next 方法了)

print '###################################\n'
print iter_obj.next()     # 1
print iter_obj.next()     # 2
print iter_obj.next()     # 3

判断是迭代器和可迭代对象

注:列表,元组,字典是可迭代的但不是迭代器

from collections import Iterable
print(isinstance([],Iterable)) #True                         
print(isinstance({},Iterable)) #True                              
print(isinstance((),Iterable)) #True                              
print(isinstance("aaa",Iterable)) #True                          
print(isinstance((x for x inrange(10)),Iterable))          #True

列表不是迭代器,只有生成器是迭代器

from collections import Iterator
t = [1,2,3,4]
print(isinstance(t,Iterator))           #False
t1 = iter(t)
print(isinstance(t1,Iterator))          #True

自定义迭代器

class MyRange(object):
    def __init__(self, n):
        self.idx = 0
        self.n = n

    def __iter__(self):
        return self

    def next(self):
        if self.idx < self.n:
            val = self.idx
            self.idx += 1
            return self.n[val]
        else:
            raise StopIteration()

l = [4,5,6,7,8]
obj = MyRange(l)
print obj.next()      # 4
print obj.next()      # 5
print obj.next()      # 6
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值