复习----python基础4
本节内容 1.列表生成式 2.迭代器&生成器 3.装饰器 4.软件目录结构规范 ? ? 1.列表生成式 1 a = [i**2 for i in range(10)] 2 3 print(a) 4 # [1,2,3,4,5,6,7,8,9,10] 这就叫列表生成式。 ? ? 2.迭代器&生成器 生成器 生成器是一次生成一个值的特殊类型函数。可以将其视为可恢复函数。 生成器也是一种迭代器,但是你只能对其迭代一次 。这是因为它们并没有把所有的值存在内存中,而是在运行时生成值。你通过遍历来使用它们,要么用一个“for”循环,要么将它们传递给任意可以进行迭代的函数和结构。大多数时候生成器是以函数来实现的。然而,它们并不返回一个值,而是yield(暂且译作“生出”)一个值。 ? 通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。 所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。 要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的 1 L = [x*x for x in range(10)] 2 print(L) 3 # [0,1,16,25,36,49,64,81] 4 g = (x*x for x in range(10)) 5 print(g) 6 # <generator object <genexpr> at 0x0000023B6E345A98> 创建 ? 我们可以直接打印出list的每一个元素,但我们怎么打印出generator的每一个元素呢? 如果要一个一个打印出来,可以通过 1 g = (x*x for x in range(10)) 2 print(g) 3 # <generator object <genexpr> at 0x0000023B6E345A98> 4 5 print(next(g)) #0 6 print(next(g)) #1 7 print(next(g)) #4 8 print(next(g)) #9 9 print(next(g)) #16 10 print(next(g)) #25 11 print(next(g)) #36 12 print(next(g)) #49 我们讲过,generator保存的是算法,每次调用 当然,上面这种不断调用 1 g = (x*x for x in range(10)) 2 print(g) 3 # <generator object <genexpr> at 0x0000023B6E345A98> 4 for n in g: 5 print(n) 6 #输出 7 # 0 8 # 1 9 # 4 10 # 9 11 # 16 12 # 25 13 # 36 14 # 49 15 # 64 16 # 81 所以,我们创建了一个generator后,基本上永远不会调用 ? generator非常强大。如果推算的算法比较复杂,用类似列表生成式的 比如,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到: 1,13,21,34,... 斐波拉契数列用列表生成式写不出来,但是,用函数把它打印出来却很容易: 1 def fib(max): 2 n,a,b = 0,1 3 while n < max: 4 print(b) 5 a,b = b,a + b 6 n = n + 1 7 return ‘done‘ 8 9 print(fib(8)) 10 # 输出 11 # 1 12 # 1 13 # 2 14 # 3 15 # 5 16 # 8 17 # 13 18 # 21 19 # done 仔细观察,可以看出, 也就是说,上面的函数和generator仅一步之遥。要把 1 def feibo(max): 2 n,before,after=0,1 3 while n < max: 4 yield before 5 before,after=after,before+after 6 n += 1 这就是定义generator的另一种方法。如果一个函数定义中包含 ? 这里,最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到 ? ? 迭代器 迭代器(iterator)是一种对象,它能够用来遍历标准模板库容器中的部分或全部元素,每个迭代器对象代表容器中的确定的地址。迭代器修改了常规指针的接口,所谓迭代器是一种概念上的抽象:那些行为上像迭代器的东西都可以叫做迭代器。然而迭代器有很多不同的能力,它可以把抽象容器和通用算法有机的统一起来。 ? 我们已经知道,可以直接作用于 一类是集合数据类型,如 一类是 这些可以直接作用于 可以使用 1 from collections import Iterable 2 print(isinstance([],Iterable)) 3 # True 4 print(isinstance({},Iterable)) 5 # True 6 print(isinstance(‘abc‘,Iterable)) 7 # True 8 print(isinstance((x for x in range(10)),Iterable)) 9 # True 10 print(isinstance(100,Iterable)) 11 # False ? 而生成器不但可以作用于 可以被 可以使用 1 from collections import Iterator 2 print(isinstance([],Iterator)) 3 # False 4 print(isinstance({},Iterator)) 5 # False 6 print(isinstance(‘abc‘,Iterator)) 7 # False 8 print(isinstance((x for x in range(10)),Iterator)) 9 # True ? 生成器都是 把 1 print(isinstance(iter([]),Iterator)) 2 # True 3 print(isinstance(iter(‘abc‘),Iterator)) 4 # True ? 你可能会问,为什么 这是因为Python的
? 小结 凡是可作用于 凡是可作用于 集合数据类型如 Python的 1 for x in [1,5]: 2 3 pass 实际上完全等价于: 1 # 首先获得Iterator对象: 2 it = iter([1,5]) 3 # 循环: 4 while True: 5 try: 6 # 获得下一个值: 7 x = next(it) 8 except StopIteration: 9 # 遇到StopIteration就退出循环 10 break ? ? 3.装饰器 装饰器之闭包 1 def outer(): 2 x=10 3 def inner(): 4 print(x) 5 return ‘boom‘ 6 return inner 7 8 print(outer()()) 9 # 10 10 # boom 闭包=内部函数+定义函数时的环境 outer()()执行时分两步,按理说第一步执行后外层函数会清掉内存(包括里面的函数),然而并没有。 这就是闭包函数。 ? 装饰器 ?装饰器是一个函数,是为其他函数增添功能的函数。 1 import time 2 3 def show_time(f): 4 def inner(): 5 start=time.time() 6 f() 7 end=time.time() 8 print("执行时间:%s"%(end-start)) 9 return inner 10 11 @show_time #foo=show_time(foo) 12 def foo(): 13 print("foo.......") 14 time.sleep(2) 15 16 foo() 17 #输出 18 # foo....... 19 # 执行时间:2.00065016746521 20 21 @show_time #bar=show_time(bar) 22 def bar(): 23 print("bar.......") 24 time.sleep(3) 25 26 bar() 27 #输出 28 # bar....... 29 # 执行时间:3.000929832458496 30 31 # 功能函数想加参数,则装饰器函数一起加 32 # 装饰器函数想加参数,则外层多嵌一层函数 ? ? 4.软件目录结构规范(搬照https://www.cnblogs.com/alex3714/articles/5765046.html) 为什么要设计好目录结构?"设计项目目录结构",就和"代码编码风格"一样,属于个人风格问题。对于这种风格上的规范,一直都存在两种态度:
我是比较偏向于后者的,因为我是前一类同学思想行为下的直接受害者。我曾经维护过一个非常不好读的项目,其实现的逻辑并不复杂,但是却耗费了我非常长的时间去理解它想表达的意思。从此我个人对于提高项目可读性、可维护性的要求就很高了。"项目目录结构"其实也是属于"可读性和可维护性"的范畴,我们设计一个层次清晰的目录结构,就是为了达到以下两点:
所以,我认为,保持一个层次清晰的目录结构是有必要的。更何况组织一个良好的工程目录,其实是一件很简单的事儿。 ? 目录组织方式关于如何组织一个较好的Python工程目录结构,已经有一些得到了共识的目录结构。在Stackoverflow的这个问题上,能看到大家对Python目录结构的讨论。 这里面说的已经很好了,我也不打算重新造轮子列举各种不同的方式,这里面我说一下我的理解和体会。 假设你的项目名为foo,我比较建议的最方便快捷目录结构这样就足够了: Foo/ |-- bin/ | |-- foo | |-- foo/ | |-- tests/ | | |-- __init__.py | | |-- test_main.py | | | |-- __init__.py | |-- main.py | |-- docs/ | |-- conf.py | |-- abc.rst | |-- setup.py |-- requirements.txt |-- README ? 简要解释一下:
? 除此之外,有一些方案给出了更加多的内容。比如 下面,再简单讲一下我对这些目录的理解和个人要求吧。 ? 关于README的内容这个我觉得是每个项目都应该有的一个文件,目的是能简要描述该项目的信息,让读者快速了解这个项目。 它需要说明以下几个事项:
? 我觉得有以上几点是比较好的一个 可以参考Redis源码中Readme的写法,这里面简洁但是清晰的描述了Redis功能和源码结构。 ? 关于requirements.txt和setup.pysetup.py一般来说,用 这个我是踩过坑的。 ? 我刚开始接触Python写项目的时候,安装环境、部署代码、运行程序这个过程全是手动完成,遇到过以下问题:
?
setuptools的文档比较庞大,刚接触的话,可能不太好找到切入点。学习技术的方式就是看他人是怎么用的,可以参考一下Python的一个Web框架,flask是如何写的:?setup.py 当然,简单点自己写个安装脚本( ? requirements.txt这个文件存在的目的是:
? 这个文件的格式是每一行包含一个包依赖的说明,通常是 ? ? 关于配置文件的使用方法注意,在上面的目录结构中,没有将
|