zoukankan      html  css  js  c++  java
  • python 06 装饰器、迭代器、生成器

    Python基础学习06


    实现装饰器知识储备

    装饰器

    生成器

    迭代器

    目录结构


    一、实现装饰器知识储备

    1、函数即“变量

     1 x = 1
     2 y = 2
     3 print(x,y)
     4 
     5 y = 2
     6 x = 1
     7 print(x,y)
     8 
     9 def bar():
    10     print("in the bar")
    11 def foo():
    12     print("in the foo")
    13     bar()
    14 foo()
    15 
    16 def foo():
    17     print("in the foo")
    18     bar()
    19 def bar():
    20     print("in the bar")
    21 foo()
    View Code

    函数调用顺序:其他高级语言类似,Python 不允许在函数未声明之前,对其进行引用或者调用

    python为解释执行,函数foo在调用前已经声明了bar和foo,所以bar和foo无顺序之分(和变量的调用相同)

    2、高阶函数

    满足下列条件之一就可成函数为高阶函数:

    a:把一个函数名当做实参传给另外一个函数(在不修改被装饰函数源代码的情况下为其添加功能)

     1 #把一个函数名当做实参传给另外一个函数
     2 def bar():
     3     print("in the bar")
     4 def test_1 (func):
     5     print(func)
     6 test_1(bar)    #输出:<function bar at 0x00000264BDEED488>  即bar()函数的内存地址
     7 
     8 def bar():
     9     print("in the bar")
    10 def test_1 (func):
    11     print(func)   #<function bar at 0x00000264BDEED488>
    12     func()        #in the bar    (bar()函数的运行结果)
    13 test_1(bar)
    14 
    15 #应用:统计函数运行时间
    16 import time
    17 def bar():
    18     print("in the bar")
    19     time.sleep(1)
    20 def test_1 (func):
    21     start_time = time.time()
    22     func()        #in the bar    (bar()函数的运行结果)
    23     stop_time = time.time()
    24     print("the func run time:%s "%(stop_time - start_time))
    25 test_1(bar)
    View Code

    b:返回值中包含函数名(不修改函数的调用方式)

     1 import time
     2 def bar():
     3     print("in the bar")
     4     time.sleep(1)
     5 def test_2(func):
     6     print(func)
     7     return func
     8 t = test_2(bar)   #获取bar()函数的内存地址,把其当做返回值传给变量t
     9 t()        #运行函数
    10 
    11 def bar():
    12     print("in the bar")
    13     time.sleep(1)
    14 def test_2(func):
    15     print(func)
    16     return func
    17 bar = test_2(bar)   #获取bar()函数的内存地址,把其当做返回值传给变量bar
    18 bar()        #不修改函数的调用方式
    View Code

    3、嵌套函数

    定义:在一个函数体内创建另外一个函数;即在一个函数的函数体内用def去声明一个新的函数(而不是去调用)

    1 #最简单的嵌套函数
    2 def foo():
    3     print("in the foo")
    4     def bar():         #相当于局部变量
    5         print("in the bar")
    6     bar()            #调用bar()函数
    7 foo()      #调用函数
    View Code

    局部作用域和全局作用域的访问顺序:

     1 x=0
     2 def grandpa():
     3     # x=1
     4     def dad():
     5         x=2
     6         def son():
     7             x=3
     8             print(x)
     9         son()
    10     dad()
    11 grandpa()
    View Code

    二、装饰器

    定义:本质是函数(装饰其他函数)就是为其他函数添加附加功能
    原则
    a、不能修改被装饰的函数的源代码
    b、不能修改被装饰的函数的调用方式
    即装饰器对其装饰的函数是完全透明的

    高阶函数+嵌套函数=》装饰器

    简单装饰器:(不能传参)

     1 #统计运行时间
     2 import time
     3 def timer(func):
     4     def deco ():
     5         start_time = time.time()
     6         func()        
     7         stop_time = time.time()
     8         print("the func run time:%s "%(stop_time - start_time))
     9     return deco
    10 def test_1 ():
    11     time.sleep(1)
    12     print("in the test_1")
    13 def test_2 ():
    14     time.sleep(2)
    15     print("in the test_2")
    16 
    17 test_1 = timer(test_1)    #即:@timer
    18 test_2 = timer(test_2)    #即:@timer
    19 test_1()     #没有修改被装饰的函数的源代码,也没有修改被装饰的函数的调用方式,并且添加了新功能
    20 test_2()     #没有修改被装饰的函数的源代码,也没有修改被装饰的函数的调用方式,并且添加了新功能
    21 
    22 #正规写法
    23 import time
    24 def timer(func):
    25     def deco ():
    26         start_time = time.time()
    27         func()       
    28         stop_time = time.time()
    29         print("the func run time:%s "%(stop_time - start_time))
    30     return deco
    31 @timer      #即:test_1 = timer(test_1)
    32 def test_1 ():
    33     time.sleep(1)
    34     print("in the test_1")
    35 @timer      #即:test_2 = timer(test_2)
    36 def test_2 ():
    37     time.sleep(2)
    38     print("in the test_2")
    39 
    40 test_1()     #没有修改被装饰的函数的源代码,也没有修改被装饰的函数的调用方式,并且添加了新功能
    41 test_2()     #没有修改被装饰的函数的源代码,也没有修改被装饰的函数的调用方式,并且添加了新功能
    View Code

    加入参数:

     1 import time
     2 def timer(func):
     3     def deco (*args,**kwargs):      #deco() 即 test_1、test_2 在此传入参数
     4         start_time = time.time()
     5         func(*args,**kwargs)
     6         stop_time = time.time()
     7         print("the func run time:%s "%(stop_time - start_time))
     8     return deco
     9 @timer      #即:test_1 = timer(test_1)
    10 def test_1 ():
    11     time.sleep(1)
    12     print("in the test_1")
    13 @timer      #即:test_2 = timer(test_2)
    14 def test_2 (name):
    15     time.sleep(0.5)
    16     print("test_2",name)
    17 
    18 test_1()
    19 test_2("zz")
    View Code

    真正的装饰器:

     1 #在原来函数上添加认证登录功能,并且允许用户选择多种方式进行登录
     2 import time
     3 user,passwd = 'zz','123'
     4 def auth(auth_type):
     5     def out_wrapper(func):
     6         def wrapper(*args,**kwargs):
     7             if auth_type == "local":
     8                 username = input("username:").strip()
     9                 password = input("password:").strip()
    10                 if username == user and password ==passwd:
    11                     print("login!!")
    12                     res = func(*args,**kwargs)
    13                     return res                #之前的装饰器没有返回值,如果有返回值则需要定义返回值
    14                 else:
    15                     exit("认证失败")
    16             elif auth_type == "ldap":
    17                 print("还不会ldap!")
    18         return wrapper
    19     return out_wrapper
    20 
    21 def index():
    22     print("welcome to index page!")
    23 @auth(auth_type = "local")  #home = auth()
    24 def home():
    25     print("welcome to home page!")
    26     return "from home"
    27 @auth(auth_type = "ldap")
    28 def bbs():
    29     print("welcome to bbs page!")
    30 
    31 index()
    32 print(home())  #有返回值的调用
    33 bbs()
    View Code

    三、生成器

    1、列表生成式

    1 a = [1,2,3]       #正常定义列表(数据是写死的)
    2 b = [i*2 for i in range(5)]   #列表生成式
    3 print(b)     #[0, 2, 4, 6, 8]
    4 
    5 b = []      #与上面效果一样但是代码量大
    6 for i in range(5):
    7     b.append(i*2)
    8 print(b)
    View Code

    2、生成器

    在Python中一边循环一边计算的机制,称为生成器:generator。

    要创建一个generator,有很多种方法。

    第一种方法:只要把一个列表生成式的[]改成(),就创建了一个generator:

    1 L = [x * x for x in range(10)]
    2 g = (x * x for x in range(10))
    3 print(g)   #<generator object <genexpr> at 0x0000022A5A330AF0>
    View Code

    创建L和g的区别仅在于最外层的[]和(),L是一个list,而g是一个generator

     我们可以直接打印出list的每一个元素,但我们需要通过next()函数获得generator的下一个返回值:

    1 L = [x * x for x in range(10)]
    2 g = (x * x for x in range(10))
    3 print(g)   #<generator object <genexpr> at 0x0000022A5A330AF0>
    4 
    5 print(g.__next__())  # 0
    6 print(g.__next__())  # 1
    7 print(g.__next__())  # 4
    8 print(g.__next__())  # 9
    9 print(g.__next__())  # 16
    View Code

    generator也是可迭代对象,可以用for循环:

     1 g = (x * x for x in range(10))
     2 print(g)   #<generator object <genexpr> at 0x0000022A5A330AF0>
     3 for i in g :
     4     print(i)
     5 print(g.__next__())  
     6 '''
     7 Traceback (most recent call last):
     8     print(g.__next__())  # 0
     9 StopIteration
    10 '''
    View Code


    generator保存的是算法,每次调用__next__(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误

    另一种方法:如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator:

     1 #用函数生成斐波那契数列
     2 def fib(max):
     3     n, a, b = 0, 0, 1
     4     while n < max:
     5         print(b)
     6         a, b = b, a + b
     7         '''a, b = b, a + b相当于:
     8         t = (b, a + b) # t是一个tuple
     9         a = t[0]
    10         b = t[1]
    11         '''
    12         n = n + 1
    13     return 'done'
    14 fib(10)
    15 
    16 #print(b)改为yield b 就变成了生成器
    17 def fib(max):
    18     n, a, b = 0, 0, 1
    19     while n < max:
    20         yield b
    21         a, b = b, a + b
    22         n = n + 1
    23     return 'done'
    24 print(fib(10))  #<generator object fib at 0x000002371FC30AF0>
    25 f = fib(10)
    26 print(f.__next__())  # 1
    27 print(f.__next__())  # 1
    28 print(f.__next__())  # 2
    29 print("_____")      #_____
    30 print(f.__next__())  # 3
    View Code


    同样的,把函数改成generator后,我们基本上从来不会用next()来获取下一个返回值,而是直接使用for循环来迭代,但是用for循环调用generator时,发现拿不到generator的return语句的返回值,计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误,如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中:

     1 def fib(max):
     2     n, a, b = 0, 0, 1
     3     while n < max:
     4         yield b
     5         a, b = b, a + b
     6         n = n + 1
     7     return 'done'
     8 g = fib(5)
     9 while True:
    10     try:
    11         x = next(g)
    12         print('g:', x)
    13     except StopIteration as e:
    14         print('Generator return value:', e.value)
    15         break
    16 '''
    17 g: 1
    18 g: 1
    19 g: 2
    20 g: 3
    21 g: 5
    22 Generator return value: done
    23 '''
    View Code

    特性:a、生成器只有在调用时才会生成数据。

    b、只记录当前位置。

    c、只有一个__next__()方法。

    四、迭代器

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

    可以使用isinstance()判断一个对象是否是Iterable对象:

    1 from collections import Iterable
    2 print(isinstance([], Iterable))  #True
    3 print(isinstance({}, Iterable))  #True
    4 print(isinstance('abc', Iterable))  #True
    5 print(isinstance((x for x in range(10)), Iterable))  #True
    6 print(isinstance(100, Iterable))  #False
    View Code

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

    可以使用isinstance()判断一个对象是否是Iterator对象:

    1 from collections import Iterator
    2 print(isinstance((x for x in range(10)), Iterator))  #True
    3 print(isinstance([], Iterator))  #False
    4 print(isinstance({}, Iterator))  #False
    5 print(isinstance('abc', Iterator))  #False
    View Code

    生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator,把list、dict、str等Iterable变成Iterator可以使用iter()函数: 

    1 isinstance(iter([]), Iterator)  #True
    2 isinstance(iter('abc'), Iterator)  #True
    View Code

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

     1 for x in [1, 2, 3, 4, 5]:
     2     pass
     3 #等价于:
     4 # 首先获得Iterator对象:
     5 it = iter([1, 2, 3, 4, 5])
     6 # 循环:
     7 while True:
     8     try:
     9         # 获得下一个值:
    10         x = next(it)
    11     except StopIteration:
    12         # 遇到StopIteration就退出循环
    13         break
    View Code

    五、目录结构 

    规范的目录结构能更好的控制程序结构,让程序具有更高的可读性。
    "项目目录结构"其实也是属于"可读性和可维护性"的范畴,设计一个层次清晰的目录结构,就是为了达到以下两点:
    1.可读性高: 不熟悉这个项目的代码的人,一眼就能看懂目录结构,知道程序启动脚本是哪个,测试目录在哪儿,配置文件在哪儿等等。从而非常快速的了解这个项目。
    2.可维护性高: 定义好组织规则后,维护者就能很明确地知道,新增的哪个文件和代码应该放在什么目录之下。这个好处是,随着时间的推移,代码/配置的规模增加,项目结构不会混乱,仍然能够组织良好。

     常用目录树:

    项目名/
    |-- bin/
    |   |-- 可执行文件
    |
    |-- 项目/
    |   |-- tests/
    |   |   |-- __init__.py
    |   |   |-- test_main.py
    |   |
    |   |-- __init__.py
    |   |-- main.py
    |
    |-- docs/
    |   |-- conf.py
    |   |-- abc.rst
    |
    |-- setup.py
    |-- requirements.txt
    |-- README

    1.bin/: 存放项目的一些可执行文件。
    2.项目/: 存放项目的所有源代码。(1) 源代码中的所有模块、包都应该放在此目录。不要置于顶层目录。(2) 其子目录tests/存放单元测试代码; (3) 程序的入口最好命名为main.py。
    3.docs/: 存放一些文档。
    4.setup.py: 安装、部署、打包的脚本。
    5.requirements.txt: 存放软件依赖的外部Python包列表。
    6.README: 项目说明文件。

    README需要说明以下几个事项:
    1.软件定位,软件的基本功能。
    2.运行代码的方法: 安装环境、启动命令等。
    3.简要的使用说明。
    4.代码目录结构说明,更详细点可以说明软件的基本原理。
    5.常见问题说明。

  • 相关阅读:
    ABP框架理论研究总结(典藏版)
    ABP系列文章总目录:
    使用NServiceBus开发分布式应用
    shell脚本进阶 详解及其实例(一)
    linux模拟实现主机跨路由通信
    网络管理之基础知识详解
    RAID RAID 大揭秘~
    磁盘管理(一)磁盘结构
    如何在centos7上安装源码包
    压缩与解压缩
  • 原文地址:https://www.cnblogs.com/hy0822/p/9064695.html
Copyright © 2011-2022 走看看