Long Way To Go 之 Python 4
装饰器(decorator)
(“器”代表的是函数)
定义:本质是函数
组成:高阶函数+嵌套函数 --> 装饰器
作用:用来装饰其他函数 in other words,为其他函数添加附加功能
(eg. 假如程序已上线,如果想新增功能,但是又不能改源代码)
原则:
1.不能修改被装饰的函数的源代码
2.不能修改被装饰的函数的调用方试
Q: 高阶\嵌套函数是个什么鬼???
高阶函数:
1.把一个函数当做实参传给另一个函数(在不修改被装饰函数源代码的情况下为其添加功能)
import time def bar(): time.sleep(3) print("in the bar") def test1(func): start_time = time.time() func() # run bar() stop_time = time.time() print("the func run time is %s"%(stop_time -start_time)) # 统计的是bar的运行时间 test1(bar) # func = bar 所以可以进行func() 传的内存地址 test1(bar()) # 把bar()的返回值传进去了 传的返回值 # BUT !调用方式变了,不是装饰器
2.返回值中包含函数名(不修改函数的调用方式)
import time def bar(): time.sleep(3) print("in the bar") def test2(func): print(func) # 打印func的内存地址,也就是bar的内存地址 return func # return bar的内存地址 bar = test2(bar) bar() # run bar
嵌套函数:
在一个函数的函数体内用def去申明另一个函数
#嵌套函数 def foo(): print("in the foo") def bar(): # 相当于局部变量,所以不能在外面直接调用bar(),所以要写在里面 print("in the bar") bar() foo() # 这个不是函数的嵌套,这叫函数的调用 def test1(): test2() test1()
EXAMPLE 1
优化(@timmer)
def timer(func): #timer(test1) 把test1的内存地址传给func,func = test1 def deco(): start_time = time.time() func() # run test1 stop_time = time.time() print("the func run time is %s"%(start_time-stop_time)) return deco # 返回这个函数的内存地址 @timer # 这就相当于 test1 = timer(test1) def test1(): time.sleep(3) print("in the test1") @timer def test2(): time.sleep(3) print("in the test2") test1() test2() # 其实调用的是deco这个函数,因为timer(test1)的返回值是deco的内存地址
优化(调用时传入多个参数)
def timer(func): #timer(test1) 把test1的内存地址传给func,func = test1 def deco(*args,**kwargs): start_time = time.time() func(*args,**kwargs) # run test1 stop_time = time.time() print("the func run time is %s"%(start_time-stop_time)) return deco # 返回这个函数的内存地址 @timer # 这就相当于 test1 = timer(test1) = deco def test1(): time.sleep(1) print("in the test1") @timer # test2 = timer(test2) = deco # test2() = deco() 所以在deco里加args def test2(name,age): print("test2:",name,age,) test1() test2("alex",22)
EXAMPLE 2 !!
import time user,passwd = "alex","abc123" def auth(auth_type): print("auth func:",auth_type) def outer_wrapper(func): def wrapper(*args,**kwargs): print("wrapper func args:",*args,**kwargs) if auth_type =="local": username = input("username:").strip() password = input("password:").strip() if user == username and passwd == password: print("\033[32;1mUser has passed authenfication\033[0m") res = func(*args,**kwargs) # return from home print("------after authenficaiton") return res else: exit("\033[31;1mInvalid username or password\033[0m") elif auth_type == "ladp": print("毛线ldap,不会------") return wrapper return outer_wrapper def index(): # 首页 print("welcome to index page") @auth(auth_type = "local") # 本地认证 & home = wrapper() def home(): # 用户登录后的首页 print("welcome to home page") return "from home" @auth(auth_type = "ldap") # 远程认证 def bbs(): # 论坛 print("welcome to bbs page") index() print(home()) # return None 如果没有return func()的话 bbs()
ps:
函数及变量
# 没问题 def bar(): print("in the bar") def foo(): print("in the foo") bar() foo() # bar定义到下面也是可以运行的 def foo(): print("in the foo") bar() def bar(): print("in the bar") foo() # 会报错,因为函数和变量一样,是先定义,在调用。所以会报错 def foo(): print("in the foo") bar() foo() def bar(): print("in the bar")
生成器(generator)
原理:只有在调用时才会生成相应的数据(假如一个大数据,只循坏了前5次,那么后面的数据是没有准备好的)
作用:省内存
特点:生成器只记住当前位置(不管前后),不能后退回返,也不能跳着走
方法:只有一个next方法, __next__()
#生成器 #数据规律的情况下 >>>print(i*2 for i in range(10)) <generator object <genexpr> at 0x0000000001E00E60> #数据不规律 #fibonacci >>>def fib(max): n,a,b = 0,0,1 while n < max: yield b # 用yield就是生成器了,用来保留函数中断的状态 a,b = b,a+b # 每次向右移一个数 n = n+1 return "----done---" >>>print(fib(10)) <generator object fib at 0x0000000002180E60>
next方法的使用
f = fib(10) # 用next方法,一个一个数字的打印 print(f.__next__()) print(f.__next__()) print(f.__next__()) # 用for循环就不会打印done for i in f: print(i) # 如果出现异常,比如只有10个数据,但是取了11次 while True: try: x = next(f) print("f:",x) except StopIteration as e: print("Generator return value:",e.value) # e.value = ----done---- break
EXAMPLE
生成器并行
import time def consumer(name): print("%s 准备吃包子啦!"%name) while True: baozi = yield # yield作用是保存当前状态,然后返回 print("包子[%s]来了,被[%s]吃了!"%(baozi,name)) #c = consumer("chenronghua") #c.__next__() #xxx 准备吃包子啦! #c.__next__() # next只是调用yield,但是不传值,所以包子出来的是none #b1 = "韭菜馅" #c.send(b1) # send 不仅调用,还可以给yield传值 #c.__next__() # 这里又传值了,所以出来的是none def producer(name): c = consumer("A") # 如果只写这一行,不写next,相当于只是把函数变成生成器,而使用next之后,才能执行到baozi = yield c2 = consumer("B") c.__next__() c2.__next__() print("老子开始准备做包子啦!") for i in range(2): time.sleep(1) print("做了1个包,分两半!") c.send(i) c2.send(i) producer("alex")
ps:
列表生成式
>>>print([i*2 for i in range(10)]) [0, 2, 4, 6, 8, 10, 12, 14, 16, 18] >>> >>>[func(i) for i in range(10)] # 传函数也可以
迭代器(Iterator)
定义:可被next()函数调用并不断返回下一个值的对象称为迭代器(Iterator)
没有next方法,就不叫迭代器,有next方法,就是~
作用:
可以表示一个无限大的数据流(eg.全体自然数),而list是无法存储无穷尽的数
我们不知道它的长度,只能不断用next()函数计算下一个数据,所以说,Iterator是惰性的,只有需要返回下一个数据的时候它才会计算
补充:
1. 可直接作用于for循环的数据类型
a) 集合数据类型:
list, tuple, dict, set,str...
b) 生成器:
生成器和带yield的generator function
2. 可迭代对象(Iterable)
可以直接用于for循环的对象叫“可迭代对象”
(eg. a = [1,2,3], 是Iterable,但不是Iterator,因为没有next调用)
总结:
----- !!!迭代器不一定是生成器,但生成器肯定是一个迭代器!!!
----- 可作用于for循坏的对象都是Iterable
可作用于next()函数的对象都是Iterator对象
----- list,dict,str...虽然是Ierable,却不是Iterator
但可以用 iter() 函数来得到一个Iterator
----- isinstance()
# 判断对象是否是一个可迭代对象或者迭代器 >>>from collections import Iterable,Iterator >>>isinstance([], Iterable) True >>>isinstance((), Iterable) True >>>isinstance("abc", Iterable) True >>>isinstance(100, Iterable) False >>>isinstance((x for x in range(10)), Iterable) True >>>isinstance((x for x in range(10)) ,Iterator) True
Q:为什么list, dict, str ... 数据类型不是Iterator?
因为python的Iterator对象表示的是一个数据流,Iterator对象可被next()函数调用并不断返回下一个数据(戳一下,动一下),直到没有数据取出并抛出StopIteration 错误
软件目录开发规范
不同目录间进行模块调用
自定义模块
# 目录假设:Atm-->bin, core--->atm, main import os import sys #print(__file__) # 当前相对路径,看起来是绝对路径,只是因为是在pycharm里,在cmd里就是相对路径 print(os.path.abspath(__file__)) # 绝对路径 print(os.path.dirname(os.path.abspath(__file__))) # 返回目录名,不要文件名 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) print(BASE_DIR) sys.path.append(BASE_DIR) # 添加坏境变量 # 导入模块 from conf import settings from core import main # run main里的login()函数 main.login()
C:/Users/apple/PycharmProjects/s14/day4/Atm/bin/atm.py
C:\Users\apple\PycharmProjects\s14\day4\Atm\bin\atm.py
C:\Users\apple\PycharmProjects\s14\day4\Atm\bin
C:\Users\apple\PycharmProjects\s14\day4\Atm