python三大神器-迭代器、生成器、装饰器

迭代器

  • 迭代 / 可迭代对象(Iterable):用for循环的过程叫做迭代,能使用for循环遍历的对象叫做可迭代对象。
  • 判断是否为可迭代对象,使用"isinstance()"判断:result = isinstance((3, 5), Iterable);如果返回 True 表示是Iterabke类型,反之不是。
  • 自定义可迭代对象(__iter__):在类中定义"__iter__"方法:
    class mylist(object):
        def __init__(self):
            # 定义需要迭代的对象
            self.mylist = list()
    
        # 添加指定元素
        def apend_itme(self, itme):
            self.mylist.append(itme)
        
        # 设置可迭代器
        def __iter__(self):
         # 可迭代对象的本质:遍历可迭代对象的时候其实获取的是可迭代对象的迭代器, 然后通过迭代器获取对象中的数据
            pass     
    
    
    # 加入检测命令:
    my_list = Mylist()
    my_list.apend_itme(1)
    my_list.apend_itme(2)
    result = isinstance(my_list, Iterable)
    
    print(result)
    
    for value in my_list:
        print(value)
  • 打印结果如下:表示,遍历可迭代对象依次获取数据需要迭代器
    /usr/bin/python3.5 /home/python/Desktop/qwe.py
    True
    Traceback (most recent call last):
      File "/home/python/Desktop/qwe.py", line 24, in <module>
        for value in my_list:
    TypeError: iter() returned non-iterator of type 'NoneType'
    
    Process finished with exit code 1
  • 添加迭代器
    from collections import Iterable
    from collections import Iterator
    
    # 自定义可迭代对象: 在类里面定义__iter__方法创建的对象就是可迭代对象
    class MyList(object):
    
        def __init__(self):
            self.my_list = list()
    
        # 添加指定元素
        def append_item(self, item):
            self.my_list.append(item)
    
        def __iter__(self):
            # 可迭代对象的本质:遍历可迭代对象的时候其实获取的是可迭代对象的迭代器,
            # 然后通过迭代器获取对象中的数据,MyIterator为自定义的该可迭代对象的迭代器。
            # 循环调用该可迭代对象相当于调用该可迭代对象的迭代器中的__next__方法。
            my_iterator = MyIterator(self.my_list)
            # 返回的是调用该可迭代对象的迭代器的__next__方法的返回值
            return my_iterator
    
    
    # 自定义迭代器对象: 在类里面定义__iter__和__next__方法创建的对象就是迭代器对象
    class MyIterator(object):
    
        def __init__(self, my_list):
            #获取可迭代对象中传入的需要迭代的对象
            self.my_list = my_list
    
            # 记录当前获取数据的下标
            self.current_index = 0
    
            # 判断当前对象是否是迭代器
            result = isinstance(self, Iterator)
            print("MyIterator创建的对象是否是迭代器:", result)
     
        def __iter__(self):
            return self
    
        # 获取迭代器中下一个值,
        def __next__(self):
            if self.current_index < len(self.my_list)
                # 每次执行后下标加 1 ,为再次遍历到时的下标
                self.current_index += 1
                # 返回当前下标 self.current_index - 1 对应的值
                return self.my_list[self.current_index - 1]
            else:
                # 数据取完了,需要抛出一个停止迭代的异常
                raise StopIteration
    
    # 添加测试命令
    my_list = MyList()
    my_list.append_item(1)
    my_list.append_item(2)
    result = isinstance(my_list, Iterable)
    
    print(result)
    
    for value in my_list:
        print(value)
  • 打印结果如下:表明有迭代器对象的可迭代对象可以顺利遍历
    /usr/bin/python3.5 /home/python/Desktop/qwe.py
    True
    MyIterator创建的对象是否是迭代器: True
    1
    2
    
    Process finished with exit code 0
    
  • 遍历可迭代对象
    • for item in Iterable 循环的本质就是先通过iter()函数获取可迭代对象Iterable的迭代器,然后对获取到的迭代器不断调用next()方法来获取下一个值并将其赋值给item,当遇到StopIteration的异常后循环结束。(即实质就是遍历的是迭代器)
  • 遍历的迭代器
    • for item in Iterator 循环的迭代器,不断调用next()方法来获取下一个值并将其赋值给item,当遇到StopIteration的异常后循环结束。
  • 应用场景:如果每次返回的值不是在一个已有的对象中读取的,而是通过程序按照一定的规律生成的值,即此时所取的值不在已知对象中而是每次计算得出,此时使用迭代器可以避免未取到该值是提前运算并存储到某个对象中,从而节省内存及性能。
    • 例如:斐波那契数列,实现如下:
      class Fibonacci(object):
      
          def __init__(self, num):
              # num:表示生成多少fibonacci数字
              self.num = num
              # 记录fibonacci前两个值
              self.a = 0
              self.b = 1
              # 记录当前生成数字的索引
              self.current_index = 0
      
          def __iter__(self):
              return self
      
          def __next__(self):
              if self.current_index < self.num:
                  # 用resulf存储当前斐波那契数列的值
                  result = self.a
                  # 计算出当前斐波那契数列的值得后两位并保存,用于以后产生斐波那契数列的值使用
                  self.a, self.b = self.b, self.a + self.b
                  # 索引值加 1 及下次的索引值
                  self.current_index += 1
                  # 返回当前斐波那契数列的值
                  return result
              else:
                  # 当下标志不小于需要的斐波那契数列个数时抛出停止迭代异常
                  raise StopIteration
      
      # 实例化可迭代对象,并传入需要生成的个数
      fib = Fibonacci(5)
      # 通过next直接取出下一个值
      # value = next(fib)
      # print(value)
      
      # 循环遍历斐波那契数列
      for value in fib:
          print(value)

生成器

  • 一类特殊的迭代器
  • 第一种创建方式:将列表生成式的 [ ] 改成 ( )
    my_list = [i * 2 for i in range(5)]
  • 第二种创建方式:在函数中yield关键字(yideld:当程序执行到此处是停止执行斌返回结果,当再次执行时从此处接着执行)
    # 创建生成器(生产斐波那契数列)
    def fibonacci(num):
        # 设置前两个值
        a = 0
        b = 1
        # 记录生成fibonacci数字的下标
        current_index = 0
        # print("--11---")
    
        # 多次调用的时候循环执行该循环
        while current_index < num:
            # 记录当前fibonacci数字
            result = a
            # 计算出当前斐波那契数列的值得后两位并保存,用于以后产生斐波那契数列的值使用
            a, b = b, a + b
            # 索引值(生成fibonacci数字的下标)加 1 ,下次调用时判断是否停止迭代
            current_index += 1
            # print("--22---")
            # 代码执行到yield会暂停,然后把结果返回出去,下次启动生成器会在暂停的位置继续往下执行
            yield result
            # print("--33---")
    
            # return
    
    
    
    # 测试命令
    fib = fibonacci(5)
    value = next(fib)
    print(value)
    value = next(fib)
    print(value)
    value = next(fib)
    print(value)
    
    # for value in fib:
    #     print(value)
    • 生成器里面使用return关键字语法上没有问题,但是代码执行到return语句会停止迭代,抛出停止迭代异常,在python3里面可以使用return关键字,python2不支持
    •  return 和 yield的区别:
      • yield: 每次启动生成器都会返回一个值,多次启动可以返回多个值,也就是yield可以返回多个值(使用的yield后不是函数就变成了生产器)
      • return: 只能返回一次值,代码执行到return语句就停止迭代,并且在python3中支持,python2中不支持

使用send方法启动生成器并传参

def gen():
    i = 0
    while i<5:
    # temp用于结束send启动时传入的参数
        temp = yield i
        print(temp)
        i+=1

# 使用send启动生成器的时候会向生成器中的yield处传入参数
# 第一次启动时使用send启动的话参数只能传None,所以第一次一般使用next启动


# 测试
f = gen()
# f.send('haha')
next(f)
f.send('haha')
f.__next__()
f.send('haha')

装饰器

  • 在不改变原有函数的调用方式的同时对其添加新的功能。(使用了闭包)
  • 闭包:在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包
    # 定义一个函数
    def test(number):
    
        # 在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包
        def test_in(number_in):
            print("in test_in 函数, number_in is %d" % number_in)
            # 返回内函数的执行结果
            return number+number_in
        # 其实这里返回的就是闭包的结果(返回内函数的引用)
        return test_in
    
    
    
    # 测试
    # 给test函数赋值,这个20就是给参数number(外函数的参数)
    # 赋值之后再次调用ret()时不会发生改变,保存不变数据
    #如果要要改变外部参数的值可以在内函数中使用 nonlocal number 声明number的作用域然后对其进行操作。
    ret = test(20)
    
    # 注意这里的100其实给参数number_in(内函数的参数)
    print(ret(100))
    
    #注 意这里的200其实给参数number_in(内函数的参数)
    print(ret(200))
    
    
    # 结果:
    """
    
    
    in test_in 函数, number_in is 100
    120
    
    in test_in 函数, number_in is 200
    220
    
    
    """
    
    • 闭包在某些情况下可以增加代码的复用性和可移植性但是由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,会消耗内存。
  • 无参装饰器
    def w1(func):
        def inner():
            print('新功能')
            func()
        return inner
    
    @w1  # 其实就是执行了f1 = w1(f1)
    def f1():
        print('功能')
    
    
    f1()
    # 解释装饰器执行过程
    '''
    代码开始执行首先到def w1,完成对w1的定义(不执行w1)
    执行到@w1是 相当于执行了 f1 = w1(f1)  (参数f1是@w1下边的f1),
    此时执行w1函数将f1赋值给w1的func,
    当执行到def inner时完成对inner的的定义,(不执行)
    然后执行return将inener的引用返回
    然后赋值给f1(这个f1不是(@w1下边的f1))
    在执行def f1 完成定义不执行,此时func是这个f1的引用
    
    到现在函数类似f1存的是inner的引用,
    func存了函数f1的引用
    
    代码继续执行,执行到f1(),此时f1存储inner的引用,及执行inner函数打印“新功能”,
    继续执行,执行func(),func存储的是函数f1的引用相当于执行f1函数,打印“功能”
    代码执行结束。
    @w1的作用是获取@w1下的函数,然后执行fi = w1(fi),具体实现不清楚。目前会用就好
    
    最终执行结果是:
    功能
    新功能
    '''
    

    (下边的以后再解释)

  • 有参装饰器
    from time import ctime, sleep
    
    def timefun(func):
        def wrapped_func(*args, **kwargs):
            print("%s called at %s"%(func.__name__, ctime()))
            func(*args, **kwargs)
        return wrapped_func
    
    @timefun
    def foo(a, b, c):
        print(a+b+c)
    
    foo(3,5,7)
    sleep(2)
    foo(2,4,9)
  • 有返回值的装饰器
    from time import ctime, sleep
    
    def timefun(func):
        def wrapped_func():
            print("%s called at %s" % (func.__name__, ctime()))
            return func()
        return wrapped_func
    
    @timefun
    def foo():
        print("I am foo")
    
    @timefun
    def getInfo():
        return '----hahah---'
    
    foo()
    sleep(2)
    foo()
    
    
    print(getInfo())
    
    
    print(getInfo())
  • “万能装饰器”
    from time import ctime, sleep
    
    def timefun(func):
        def wrapped_func(*args, **kwargs):
            print("%s called at %s" % (func.__name__, ctime()))
            return func(*args, **kwargs)
        return wrapped_func
    
    @timefun
    def foo(a = 1):
        print("I am foo")
    
    
    
    @timefun
    def foo2(a = 1, b =2):
        print("I am foo2")
    
    

装饰器带参数,在原有装饰器的基础上,设置外部变量

  • from time import ctime, sleep
    
    def timefun_arg(pre="hello"):
        def timefun(func):
            def wrapped_func():
                print("%s called at %s %s" % (func.__name__, ctime(), pre))
                return func()
            return wrapped_func
        return timefun
    
    # 下面的装饰过程
    # 1. 调用timefun_arg("itcast")
    # 2. 将步骤1得到的返回值,即time_fun返回, 然后time_fun(foo)
    # 3. 将time_fun(foo)的结果返回,即wrapped_func
    # 4. 让foo = wrapped_fun,即foo现在指向wrapped_func
    @timefun_arg("itcast")
    def foo():
        print("I am foo")
    
    @timefun_arg("python")
    def too():
        print("I am too")
    
    foo()
    sleep(2)
    foo()
    
    too()
    sleep(2)
    too()
  •  多个装饰器装饰同一函数是按从上到下装饰,从下到上执行
  • 类装饰器
    class Test(object):
        def __init__(self, func):
            print("---初始化---")
            print("func name is %s"%func.__name__)
            self.__func = func
        def __call__(self):
            print("---装饰器中的功能---")
            self.__func()
    '''
    说明:
    1. 当用Test来装作装饰器对test函数进行装饰的时候,首先会创建Test的实例对象
      并且会把test这个函数名当做参数传递到__init__方法中
      即在__init__方法中的属性__func指向了test指向的函数
    
    2. test指向了用Test创建出来的实例对象
    
    3. 当在使用test()进行调用时,就相当于让这个对象(),
        因此会调用这个对象的__call__方法
    
    4. 为了能够在__call__方法中调用原来test指向的函数体,
        所以在__init__方法中就需要一个实例属性来保存这个函数体的引用
        所以才有了self.__func = func这句代码,
        从而在调用__call__方法中能够调用到test之前的函数体
    '''
    @Test
    def test():
        print("----test---")
    test()  # 如果把这句话注释,重新运行程序,依然会看到"--初始化--"

 property属性的使用

  • class Person(object):
    
        def __init__(self, name, money):
            self.name = name
            self.__money = money
    
        # 通过@property语法糖修改money函数(有一个self实例参数 并且必须有返回值)得到money属性,
        # 获取money属性对应的值就会自动调用money方法中
        @property  
        def money(self):
            # 将私有属性在外界获取之前做一些统一的逻辑处理
            print("获取私有属性money的值")
            return "¥" + self.__money
    
        @money.setter  # 给money属性添加一个设置值的方法, 以后只要给money属性设置值就会自动执行到这个money方法
        def money(self, new_value):
            # 在修改钱这个属性的时候按照规定来修改
            print("开始设置money私有属性")
            if new_value.isdigit():
    
                self.__money = new_value
            else:
                print("请输入合法的金额数字")
    
        @money.deleter
        def money(self):
            print("开始删除moeny属性")
    
    
    p = Person("老王","100,000,000.00")
    
    print(p.money)
    
    p.money = "1000"
    # print(p.money)
    
    del p.money
    
    property
    

错误之处欢迎指出

猜你喜欢

转载自blog.csdn.net/jlb1024/article/details/81708250
今日推荐