本節(jié)內(nèi)容
- 迭代器&生成器
- 裝飾器
- Json & pickle 數(shù)據(jù)序列化
- 軟件目錄結(jié)構(gòu)規(guī)范
- 作業(yè):ATM項(xiàng)目開(kāi)發(fā)
1. 裝飾器
定義:本質(zhì)是函數(shù),(裝飾其他函數(shù))就是為其他函數(shù)添加附加功能
原則:
- 不能修改被裝飾函數(shù)的源代碼
- 不能修改被裝飾函數(shù)的調(diào)用方式
實(shí)現(xiàn)裝飾器知識(shí)儲(chǔ)備:
1艺智、函數(shù)即“變量”
2迄委、高階函數(shù)
把一個(gè)函數(shù)名當(dāng)做實(shí)參傳給另一個(gè)函數(shù)(不修改被裝飾函數(shù)源代碼情況下為期添加功能)
import time
def bar():
time.sleep(3)
print('in the bar')
def test1(func):
start_time = time.time()
func()
stop_time = time.time()
print("the func run time is %s" %(stop_time-start_time))
test1(bar)
返回值中包含函數(shù)名(不修改函數(shù)的調(diào)用方式)
import time
def bar():
time.sleep(3)
print('in the bar')
def test2(func):
print(func)
return func #返回func內(nèi)存地址
t = test2(bar) #t是bar的內(nèi)存地址(寫(xiě)bar函數(shù)名傳遞的是內(nèi)存地址褐筛,寫(xiě)bar()傳遞的是運(yùn)行結(jié)果)
print(t)
t() #代表運(yùn)行bar()
bar = test2(bar)
bar() #加上test2的功能,沒(méi)有改變bar的調(diào)用方式
3叙身、嵌套函數(shù)
#函數(shù)嵌套
def foo():
print('in the foo')
def bar():
print('in the bar')
bar()
foo()
高階函數(shù)+嵌套函數(shù)==>裝飾器
裝飾器實(shí)例:
import time
def timer(func):
def deco(): #==>高階
start_time = time.time()
func()
stop_time = time.time()
print("the func run time is %s" %(stop_time-start_time))
return deco #==>嵌套
#高階函數(shù)+嵌套函數(shù)==>裝飾器
@timer #等同于:test1 = timer(test1)
def test1():
time.sleep(3)
print('in the test1')
#print((timer(test1))) #返回deco的內(nèi)存地址
#test1 = timer(test1)
#test1()
test1()
import time
def timer(func):
def deco(*args,**kwargs): #==>高階
start_time = time.time()
func(*args,**kwargs)
stop_time = time.time()
print("the func run time is %s" %(stop_time-start_time))
return deco #==>嵌套
高階函數(shù)+嵌套函數(shù)==>裝飾器
@timer #等同于:test1 = timer(test1)
def test1():
time.sleep(1)
print('in the test1')
@timer
def test2(name):
time.sleep(1)
print('in the test2',name)
#print((timer(test1))) #返回deco的內(nèi)存地址
#test1 = timer(test1)
#test1()
test1()
test2('Alex')
終極版死讹!
import time
user,passwd = 'Alex','abc123'
def auth(auth_type):
print("auth type:",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 authentication\033[0m")
res = func(*args,**kwargs)
return res
else:
exit("\033[31;1mInvalid username or password\033[0m")
elif auth_type == "ldap":
print("搞毛線dap 不會(huì)。曲梗。。")
return wrapper
return outer_wrapper
def index():
print("welcome to index page")
@auth(auth_type = "local")
def home():
print("welcome to home page")
return "from home"
@auth(auth_type = "ldap")
def bbs():
print("welcome to bbs page")
index()
print(home())
bbs()
2. 迭代器與生成器
1妓忍、列表生成式:
a = [i+1 for i in range(10)]
2虏两、生成器
通過(guò)列表生成式,我們可以直接創(chuàng)建一個(gè)列表世剖。但是定罢,受到內(nèi)存限制,列表容量肯定是有限的旁瘫。而且祖凫,創(chuàng)建一個(gè)包含100萬(wàn)個(gè)元素的列表,不僅占用很大的存儲(chǔ)空間酬凳,如果我們僅僅需要訪問(wèn)前面幾個(gè)元素惠况,那后面絕大多數(shù)元素占用的空間都白白浪費(fèi)了。
所以宁仔,如果列表元素可以按照某種算法推算出來(lái)稠屠,那我們是否可以在循環(huán)的過(guò)程中不斷推算出后續(xù)的元素呢?這樣就不必創(chuàng)建完整的list翎苫,從而節(jié)省大量的空間权埠。在Python中,這種一邊循環(huán)一邊計(jì)算的機(jī)制煎谍,稱(chēng)為生成器:generator攘蔽。
要?jiǎng)?chuàng)建一個(gè)generator,有很多種方法呐粘。第一種方法很簡(jiǎn)單满俗,只要把一個(gè)列表生成式的[]改成()转捕,就創(chuàng)建了一個(gè)generator:
L = [x * 2 for x in range(10)]
print(L)
>>>[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
g = (x * 2 for x in range(10))
print(g)
>>><generator object <genexpr> at 0x000001D69638F048>
創(chuàng)建L和g的區(qū)別僅在于最外層的[]和(),L是一個(gè)list漫雷,而g是一個(gè)generator瓜富。
我們可以直接打印出list的每一個(gè)元素,但我們?cè)趺创蛴〕鰃enerator的每一個(gè)元素呢降盹?
生成器只有在調(diào)用時(shí)才會(huì)生成相應(yīng)的數(shù)據(jù)与柑,如果要一個(gè)一個(gè)打印出來(lái),可以通過(guò)next()函數(shù)獲得generator的下一個(gè)返回值:
print(__next__(g))
print(__next__(g))
定義generator的另一種方法:如果一個(gè)函數(shù)定義中包含yield關(guān)鍵字蓄坏,那么這個(gè)函數(shù)就不再是一個(gè)普通函數(shù)价捧,而是一個(gè)generator
def fib(max):
n,a,b = 0,0,1
while n < max:
#print(b)
yield b #要把fib函數(shù)變成generator,只需要把print(b)改為yield b就可以了:
a,b = b,a+b
n += 1
return 'done'
data = fib(10)
print(data)
print(data.__next__())
print(data.__next__())
print("干點(diǎn)別的事")
print(data.__next__())
print(data.__next__())
generator和函數(shù)的執(zhí)行流程不一樣涡戳。函數(shù)是順序執(zhí)行结蟋,遇到return語(yǔ)句或者最后一行函數(shù)語(yǔ)句就返回。而變成generator的函數(shù)渔彰,在每次調(diào)用next()的時(shí)候執(zhí)行嵌屎,遇到y(tǒng)ield語(yǔ)句返回,再次執(zhí)行時(shí)從上次返回的yield語(yǔ)句處繼續(xù)執(zhí)行恍涂。
把函數(shù)改成generator后宝惰,我們基本上從來(lái)不會(huì)用next()來(lái)獲取下一個(gè)返回值,而是直接使用for循環(huán)來(lái)迭代:
for n in fib(6):
print(n)
但是用for循環(huán)調(diào)用generator時(shí)再沧,發(fā)現(xiàn)拿不到generator的return語(yǔ)句的返回值尼夺。如果想要拿到返回值,必須捕獲StopIteration錯(cuò)誤炒瘸,返回值包含在StopIteration的value中:
g = fib(6)
while True:
try:
x = next(g)
print('g:', x)
except StopIteration as e:
print('Generator return value:', e.value)
break
例:通過(guò)yield實(shí)現(xiàn)在單線程的情況下實(shí)現(xiàn)并發(fā)運(yùn)算的效果
import time
def consumer(name):
print("%s 準(zhǔn)備吃包子啦!" %name)
while True:
baozi = yield
print("包子[%s]來(lái)了,被[%s]吃了!" %(baozi,name))
def producer(name):
c = consumer('A')
c2 = consumer('B')
c.__next__()
c2.__next__()
print("老子開(kāi)始準(zhǔn)備做包子啦!")
for i in range(10):
time.sleep(1)
print("做了2個(gè)包子!")
c.send(i)
c2.send(i)
producer("alex")
3淤堵、迭代器
可以直接作用于for循環(huán)的對(duì)象統(tǒng)稱(chēng)為可迭代對(duì)象:Iterable。
可以直接作用于for循環(huán)的數(shù)據(jù)類(lèi)型有以下幾種:
一類(lèi)是集合數(shù)據(jù)類(lèi)型顷扩,如list拐邪、tuple、dict隘截、set庙睡、str等;
一類(lèi)是generator技俐,包括生成器和帶yield的generator function乘陪;
使用isinstance()判斷一個(gè)對(duì)象是否是Iterable對(duì)象:
>>> 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
可以被next()函數(shù)調(diào)用并不斷返回下一個(gè)值的對(duì)象稱(chēng)為迭代器:Iterator
可以使用isinstance()判斷一個(gè)對(duì)象是否是Iterator對(duì)象:
>>> from collections import Iterator
>>> isinstance((x for x in range(10)), Iterator)
True
>>> isinstance([], Iterator)
False
>>> isinstance({}, Iterator)
False
>>> isinstance('abc', Iterator)
False
生成器都是Iterator對(duì)象,但list雕擂、dict啡邑、str雖然是Iterable,卻不是Iterator井赌。
把list谤逼、dict贵扰、str等Iterable變成Iterator可以使用iter()函數(shù):
>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True
小結(jié)
- Python的Iterator對(duì)象表示的是一個(gè)數(shù)據(jù)流,Iterator對(duì)象可以被next()函數(shù)調(diào)用并不斷返回下一個(gè)數(shù)據(jù)流部,直到?jīng)]有數(shù)據(jù)時(shí)拋出StopIteration錯(cuò)誤戚绕。所以Iterator的計(jì)算是惰性的,只有在需要返回下一個(gè)數(shù)據(jù)時(shí)它才會(huì)計(jì)算枝冀。
- 凡是可作用于for循環(huán)的對(duì)象都是Iterable類(lèi)型舞丛;
- 凡是可作用于next()函數(shù)的對(duì)象都是Iterator類(lèi)型,它們表示一個(gè)惰性計(jì)算的序列果漾;
- 集合數(shù)據(jù)類(lèi)型如list球切、dict、str等是Iterable但不是Iterator绒障,不過(guò)可以通過(guò)iter()函數(shù)獲得一個(gè)Iterator對(duì)象吨凑。
- Python的for循環(huán)本質(zhì)上就是通過(guò)不斷調(diào)用next()函數(shù)實(shí)現(xiàn)的。
3. Json & pickle 數(shù)據(jù)序列化
1户辱、JSon序列化:可實(shí)現(xiàn)簡(jiǎn)單數(shù)據(jù)交互
json序列化:
#import json
info = {
'name':'alex',
'age':22,
}
f = open('test.text','w')
print(json.dumps(info))
f.write(json.dumps(info))
f.close()
json反序列化:
import json
f = open('test.text','r')
data = json.loads(f.read())
f.close()
print(data["age"])
2鸵钝、pickle序列化:可實(shí)現(xiàn)復(fù)雜的數(shù)據(jù)交互,只能在Python中使用
pickle序列化:
import pickle
def hello(name):
print('hello',name)
info = {
'name':'alex',
'age':22,
'func':hello
}
f = open('test.text','wb')
print(pickle.dumps(info))
f.write(pickle.dumps(info))
f.close()
pickle反序列化:
import pickle
def hello(name):
print('hello2',name)
f = open('test.text','rb')
data = pickle.loads(f.read())
f.close()
print(data["func"]('laala'))
4. 軟件目錄結(jié)構(gòu)規(guī)范
假設(shè)你的項(xiàng)目名為foo, 我比較建議的最方便快捷目錄結(jié)構(gòu)這樣就足夠了:
Foo/
|-- bin/
| |-- foo
|
|-- foo/
| |-- tests/
| | |-- __init__.py
| | |-- test_main.py
| |
| |-- __init__.py
| |-- main.py
|
|-- docs/
| |-- conf.py
| |-- abc.rst
|
|-- setup.py
|-- requirements.txt
|-- README
簡(jiǎn)要解釋一下:
- bin/: 存放項(xiàng)目的一些可執(zhí)行文件庐镐,當(dāng)然你可以起名script/之類(lèi)的也行恩商。
- foo/: 存放項(xiàng)目的所有源代碼。(1)源代碼中的所有模塊焚鹊、包都應(yīng)該放在此目錄。不要置于頂層目錄韧献。(2)其子目錄tests/存放單元測(cè)試代碼末患; (3)程序的入口最好命名為main.py。
- docs/: 存放一些文檔锤窑。
- setup.py: 安裝璧针、部署、打包的腳本渊啰。
- requirements.txt:存放軟件依賴(lài)的外部Python包列表探橱。
- README: 項(xiàng)目說(shuō)明文件。
關(guān)于README的內(nèi)容
這個(gè)我覺(jué)得是每個(gè)項(xiàng)目都應(yīng)該有的一個(gè)文件绘证,目的是能簡(jiǎn)要描述該項(xiàng)目的信息隧膏,讓讀者快速了解這個(gè)項(xiàng)目。
它需要說(shuō)明以下幾個(gè)事項(xiàng):
1嚷那、軟件定位胞枕,軟件的基本功能。
2魏宽、運(yùn)行代碼的方法: 安裝環(huán)境腐泻、啟動(dòng)命令等决乎。
3、簡(jiǎn)要的使用說(shuō)明派桩。
4构诚、代碼目錄結(jié)構(gòu)說(shuō)明,更詳細(xì)點(diǎn)可以說(shuō)明軟件的基本原理铆惑。
5范嘱、常見(jiàn)問(wèn)題說(shuō)明。
5. 本節(jié)作業(yè)
作業(yè)需求:
模擬實(shí)現(xiàn)一個(gè)ATM + 購(gòu)物商城程序
1鸭津、額度 15000或自定義
2彤侍、實(shí)現(xiàn)購(gòu)物商城,買(mǎi)東西加入 3逆趋、購(gòu)物車(chē)盏阶,調(diào)用信用卡接口結(jié)賬
4、可以提現(xiàn)闻书,手續(xù)費(fèi)5%
5名斟、每月22號(hào)出賬單,每月10號(hào)為還款日魄眉,過(guò)期未還砰盐,按欠款總額 萬(wàn)分之5 每日計(jì)息
6、支持多賬戶登錄
7坑律、支持賬戶間轉(zhuǎn)賬
8岩梳、記錄每月日常消費(fèi)流水
9、提供還款接口
10晃择、ATM記錄操作日志
11冀值、提供管理接口,包括添加賬戶宫屠、用戶額度列疗,凍結(jié)賬戶等。浪蹂。抵栈。
用戶認(rèn)證用裝飾器