装饰器(本质:函数)

  1. 定义:为其他函数添加附加功能(装饰器对于被装饰函数是“透明的”、“不存在的”);执行过程:调用被装饰函数实际上是调用修饰函数

  2. 原则:

    1. 不能修改被装饰函数的源代码;

    2. 不能修改被装饰函数的调用方式。

  3. 实现装饰器的知识补充:

    1. 函数即“变量”:

      1. 调用前先定义(即要先有内存地址空间、函数的执行顺序)

      2. 可赋值给其他函数名

        python3 基础(4)-装饰器、生成器、迭代器_装饰器

        def bar():

           print("in the bar")

        #函数bar()在以下三处位置,只有1、2处时,调用foo()才正确执行

        ------------------------------

        #1#

        def foo():

           print("in teh foo")

           bar()

        #2#

        foo()

        #3#

    2. 高阶函数

      1. 条件一:一个函数(被装饰的函数)当作实参传给另一个函数(装饰函数);(满足原则一不修改被装饰函数的源代码)

      2. 条件二:返回值中包含函数名(装饰函数)。(满足原则二不修改被装饰函数的调用方式)

    3. 函数嵌套

  4. 举例:

    1. 统计运行时间的装饰器

      带无参数、固定参数、无固定参数的函数

      print("分割线".center(50,"="))

      import time

      def timmer(func):

         def wrapper(*args,**kwargs):

             start_time=time.time()

             res = func(*args,**kwargs)   #这里的参数由wrapper函数的参数传递

             stop_time=time.time()

             print("func run time is %s"%(stop_time-start_time))

             return res            #返回被装饰函数的返回值

         return wrapper   #返回装饰函数wrapper的内存地址

      @timmer  #同test1=timmer(test1);

                 # 理解:执行timmer(test1),得到返回值wrapper内存地址,再把wrapper内存地址赋值给test1

      def test1():

         time.sleep(1)

         print("my name is test1")

         return "test1"

      @timmer   #同test2=timmer(test2);

                 # 理解:执行timmer(test2),得到返回值wrapper内存地址,再把wrapper内存地址赋值给test2

      def test2(name,age):

         time.sleep(1)

         print("my name is %s,I'm %s" %(name,age))

      test1()   #要配合上面@timmer使用,同timmer(test1)()

      print(test1())

      test2("chen",40)   #要配合上面@timmer使用,同timmer(test2)("chen",40)

      print(test2("chen",40))

      #结果

      my name is test1

      func run time is 1.000598430633545

      my name is test1

      func run time is 1.0012288093566895

      test1

      my name is chen,I'm 40

      func run time is 1.0007030963897705

      my name is chen,I'm 40

      func run time is 1.0007030963897705

      None

    2. 页面登录认证

      无参装饰器:

      有参装饰器:

      user,passwd = "chen","123456"

      def auth(func):

         def wrapper(*args,**kwargs):

             username = input("Username: ").strip()

             password = input("Password: ").strip()

             if user == username and passwd == password:

                 print("\033[32;1m通过本地认证!\033[0m")

                 return func(*args,**kwargs)

             else:

                 exit("\033[31;1m错误的用户名或密码\033[0m")

         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")

         return "from bbs"

      index()

      home()

      bbs()

      #结果

      welcome to index page

      Username: chen

      Password: 123456

      通过本地认证!

      welcome to home page

      Username: chen

      Password: 123456

      通过本地认证!

      welcome to bbs page

      user,passwd = "chen","123456"

      def auth(auth_type):

         def outer_wrapper(func):

             def wrapper(*args,**kwargs):

                 if auth_type == "local":

                     username = input("Username: ").strip()

                     password = input("Password: ").strip()

                     if user == username and passwd == password:

                         print("\033[32;1m通过本地认证!\033[0m")

                         return func(*args,**kwargs)

                     else:

                         exit("\033[31;1m错误的用户名或密码\033[0m")

                 elif auth_type == "ldap":

                     print("\033[32;1m远程认证!\033[0m")

             return wrapper

         return outer_wrapper

      def index():

         print("welcome to index page")

      @auth(auth_type="local")   #同home=auth(auth_type="local")(home)

      def home():

         print("welcome to home page")

         return "from home"

      @auth(auth_type="ldap")   #同bbs=auth(auth_type="ldap")(bbs)

      def bbs():

         print("welcome to bbs page")

         return "from bbs"

      index()

      home()

      bbs()

      #结果

      welcome to index page

      Username: chen

      Password: 123456

      通过本地认证!

      welcome to home page

      远程认证!

生成器 

    创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

    而生成器节省大量的空间,因为生成器只有在调用时才会生成相应的数据。

  1. 知识补充:

    1. 列表生成式(列表解析):

      a = [i*2 for i in range(10)]

      print(a)

      输出:

      [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

    2. 斐波那契数列:

      def fib(max):

         n, a, b = 0, 0, 1

         while n < max:

             print(b)

             a, b = b, a + b

             n = n + 1

         return 'done'

      fib(10)

      输出:

      1 1 2 3 5 8 13 21 34 55

  2. 生成器特点:

    1. 生成器只有在调用时才会生成相应的数据

    2. 只记录当前位置,用到哪记录到哪

    3. 只有一个方法__next__(),python2 为next()

  3. 生成器的实现:

    1. 方法一:列表生成式。

      a = ( i*2 for i in range(10))

      print(a)

      输出:

      <generator object <genexpr> at 0x054D1F30>

    2. 方法二:在函数定义中包含yield关键字。

    3. 这时,这个函数就不再是一个普通函数,首次调用__next__()的时候执行生成器函数,遇到yield语句时返回,再次执行(__next__()或send()或for、while等)时将从上次返回的yield语句处继续执行。

      def fib(max):

         n, a, b = 0, 0, 1

         while n < max:

             # print(b)

             yield b

             a, b = b, a + b

             n = n + 1

         return "done"

      fib(5)

      print(fib(5))   #此时不会获取return的值

      输出:

      <generator object fib at 0x04F8AF90>


  4. 获取生成器的值:

    1. 使用__next__()

      print(a.__next__())

      print(a.__next__())

      print(a.__next__())

      f = fib(5)

      print(f.__next__())

      print(f.__next__())

    2. for、while循环

      f = fib(5)

      for i in f:

          print(i)

  5. 应用:

    1. 当使用__next__()获取生成器的值的数量超过总的数量时:

      def fib(max):

         n, a, b = 0, 0, 1

         while n < max:

             # print(b)

             yield b

             a, b = b, a + b

             n = n + 1

         return "done"   #作为错误提示信息

      f = fib(100)

      #当获取生成器的值的数量超过总的数量时会报错

      while True:

          x = f.__next__()

          print('f:', x)

      #解决方式:捕获StopIteration错误

      while True:

          try:

              x = f.__next__()

              print('f:', x)

          except StopIteration as e:

              print('Generator return value:', e.value)

              break

    2. 在单线程实现并发运算的效果(携程??)

      补充:send()用于给yield传值,但是send传值时,要求生成器已执行到yield语句处(就是send前面至少要有一个__next__(),这样才能保证生成器运行到yield处

      import time

      def consumer(name):

         print("%s 准备吃包子啦!" %name)

         while True:

             baozi = yield   #这里的yield由send传值

             print("[%s]包子来了,被[%s]吃了!" %(baozi,name))

      def producer(name):

         c = consumer('A')

         c2 = consumer('B')

         c3 = consumer('C')

         c.__next__()

         c2.__next__()

         c3.__next__()

         print("师傅开始蒸包子啦!")

         for i in ["猪肉馅","韭菜馅","白菜馅","豆沙馅"]:

             time.sleep(1)

             print("%s包子出炉了!"%i)

             c.send(i)

             c2.send(i)

             c3.send(i)

      producer("alex")

迭代器

  1. Iterable对象:可以直接作用于for循环的对象统称为可迭代对象

    1. 集合数据类型,如list、tuple、dict、set、str等;

    2. 生成器generator,包括带yield的generator function。

    3. 内置函数:map()、filter()、zip(a,b)

  2. Iterator对象:可以被next()函数调用并不断返回下一个值的对象称为迭代器对象

    1. 生成器generator

    2. 内置函数:map()、filter()、zip(a,b)

  3. 判断一个对象是否是Iterable对象:

    #使用isinstance()

    >>> from collections import Iterable

    >>> isinstance([], Iterable)

    True

    >>> isinstance({}, Iterable)

    True

    >>> isinstance('abc', Iterable)

    True

    >>> isinstance((x for x in range(10)), Iterable)

    True

    >>> isinstance(100, Iterable)

    False


  4. 判断一个对象是否是Iterator对象:

    #使用isinstance()

    >>> from collections import Iterator

    >>> isinstance((x for x in range(10)), Iterator)

    True

    >>> isinstance([], Iterator)

    False

    >>> isinstance({}, Iterator)

    False

    >>> isinstance('abc', Iterator)

    False


  5. 把Iterable对象变成Iterator对象:

    #使用iter()函数:

    >>> isinstance(iter([]), Iterator)

    True

    >>> isinstance(iter('abc'), Iterator)

    True

    #将列表变为iterator对象

    a = [1,2,3,4]

    b=iter(a)

    print(type(b))

    print(b.__next__())

    print(b.__next__())

    #输出

    <class 'list_iterator'>

    1

    2


  6. 你可能会问,为什么list、dict、str等数据类型不是Iterator?

    1. 这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。

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

  7. 补充:

    1. Python的for循环本质上就是通过不断调用next()函数实现的,例如:

      for x in [1, 2, 3, 4, 5]:

         pass

      #等价于

      it = iter([1, 2, 3, 4, 5])   # 首先获得Iterator对象:

      while True:

         try:

             x = next(it)   # 获得下一个值:

         except StopIteration:   # 遇到StopIteration就退出循环

             break