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()
函数调用顺序:其他高级语言类似,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)
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() #不修改函数的调用方式
3、嵌套函数
定义:在一个函数体内创建另外一个函数;即在一个函数的函数体内用def去声明一个新的函数(而不是去调用)
1 #最简单的嵌套函数 2 def foo(): 3 print("in the foo") 4 def bar(): #相当于局部变量 5 print("in the bar") 6 bar() #调用bar()函数 7 foo() #调用函数
局部作用域和全局作用域的访问顺序:
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()
二、装饰器
定义:本质是函数(装饰其他函数)就是为其他函数添加附加功能
原则:
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() #没有修改被装饰的函数的源代码,也没有修改被装饰的函数的调用方式,并且添加了新功能
加入参数:
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")
真正的装饰器:
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()
三、生成器
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)
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>
创建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
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 ‘‘‘
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
同样的,把函数改成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 ‘‘‘
特性: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
可以被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
生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator,把list、dict、str等Iterable变成Iterator可以使用iter()函数:
1 isinstance(iter([]), Iterator) #True 2 isinstance(iter(‘abc‘), Iterator) #True
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
五、目录结构
规范的目录结构能更好的控制程序结构,让程序具有更高的可读性。
"项目目录结构"其实也是属于"可读性和可维护性"的范畴,设计一个层次清晰的目录结构,就是为了达到以下两点:
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.常见问题说明。
原文地址:https://www.cnblogs.com/hy0822/p/9064695.html