Python 之路04 - Python基礎(chǔ)4

本節(jié)內(nèi)容

  1. 迭代器&生成器
  2. 裝飾器
  3. Json & pickle 數(shù)據(jù)序列化
  4. 軟件目錄結(jié)構(gòu)規(guī)范
  5. 作業(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)要解釋一下:

  1. bin/: 存放項(xiàng)目的一些可執(zhí)行文件庐镐,當(dāng)然你可以起名script/之類(lèi)的也行恩商。
  2. foo/: 存放項(xiàng)目的所有源代碼。(1)源代碼中的所有模塊焚鹊、包都應(yīng)該放在此目錄。不要置于頂層目錄韧献。(2)其子目錄tests/存放單元測(cè)試代碼末患; (3)程序的入口最好命名為main.py。
  3. docs/: 存放一些文檔锤窑。
  4. setup.py: 安裝璧针、部署、打包的腳本渊啰。
  5. requirements.txt:存放軟件依賴(lài)的外部Python包列表探橱。
  6. 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)證用裝飾器

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市坤次,隨后出現(xiàn)的幾起案子古劲,更是在濱河造成了極大的恐慌,老刑警劉巖缰猴,帶你破解...
    沈念sama閱讀 218,036評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件绢慢,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)胰舆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)骚露,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人缚窿,你說(shuō)我怎么就攤上這事棘幸。” “怎么了倦零?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,411評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵误续,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我扫茅,道長(zhǎng)蹋嵌,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,622評(píng)論 1 293
  • 正文 為了忘掉前任葫隙,我火速辦了婚禮栽烂,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘恋脚。我一直安慰自己腺办,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布糟描。 她就那樣靜靜地躺著怀喉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪船响。 梳的紋絲不亂的頭發(fā)上躬拢,一...
    開(kāi)封第一講書(shū)人閱讀 51,521評(píng)論 1 304
  • 那天,我揣著相機(jī)與錄音见间,去河邊找鬼聊闯。 笑死,一個(gè)胖子當(dāng)著我的面吹牛缤剧,可吹牛的內(nèi)容都是我干的馅袁。 我是一名探鬼主播域慷,決...
    沈念sama閱讀 40,288評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼荒辕,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了犹褒?” 一聲冷哼從身側(cè)響起抵窒,我...
    開(kāi)封第一講書(shū)人閱讀 39,200評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎叠骑,沒(méi)想到半個(gè)月后李皇,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,644評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評(píng)論 3 336
  • 正文 我和宋清朗相戀三年掉房,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了茧跋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,953評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡卓囚,死狀恐怖瘾杭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情哪亿,我是刑警寧澤粥烁,帶...
    沈念sama閱讀 35,673評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站蝇棉,受9級(jí)特大地震影響讨阻,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜篡殷,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評(píng)論 3 329
  • 文/蒙蒙 一钝吮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧贴唇,春花似錦搀绣、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,889評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至瓶您,卻和暖如春麻捻,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背呀袱。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,011評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工贸毕, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人夜赵。 一個(gè)月前我還...
    沈念sama閱讀 48,119評(píng)論 3 370
  • 正文 我出身青樓明棍,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親寇僧。 傳聞我的和親對(duì)象是個(gè)殘疾皇子摊腋,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評(píng)論 2 355