Day04 - Python基礎4

Day04的課程要點記錄
詳細教程地址:Day4 - Python基礎4 | python裝飾器

一、裝飾器

1.1 定義

裝飾器本質是函數,(裝飾其他函數)為其他函數添加附加功能。
原則:

  1. 不能修改被裝飾的函數的源代碼
  2. 不能修改被裝飾的函數的調用方式

1.2 實現裝飾器的知識儲備:

1.2.1 函數即“變量”
1.2.2 高階函數:
  • 把一個函數名當做實參傳給另外一個函數,可以在不修改被裝飾函數源代碼的情況下為其添加功能。
import time
def bar():
   time.sleep(3)
   print("in the bar")

def test1(func):
   start_time = time.time()
   func()  # 就是bar(),但是改變了函數的調用方式
   stop_time = time.time()
   print("the func is running in %s second." % (stop_time - start_time))

test1(bar)      # 相當于func = bar
  • 返回值中包含函數名,不修改函數的調用方式。
import time
def bar():
   time.sleep(3)
   print("in the bar")
def test2(func):
   print(func)
   return func

# print(test2(bar))
bar = test2(bar)     # 將bar的內存地址重新給回到bar
  • 嵌套函數
    在一個函數的函數體內用def去聲明一個函數

1.3 實現裝飾器

高階函數 + 嵌套函數 => 裝飾器
@裝飾器名 # 加在要用裝飾器函數的頭部

import time

def timer(func):        # timer(test1)  func = test1
   def deco():
       start_time = time.time()
       func()
       stop_time = time.time()
       print("Func is running %s second.." % (stop_time - start_time))
   return deco

@timer  # test1 = timer(test1)
def test1():
   time.sleep(3)
   print("in the test1")

test1()

被裝飾函數可能出現所需參數數量不等的情況,通用寫法如下:

import time

def timer(func):        # timer(test1)  func = test1
   def deco(*args, **kwargs):
       start_time = time.time()
       res = func(*args, **kwargs)
       stop_time = time.time()
       print("Func is running %s second.." % (stop_time - start_time))
       return res    # 返回被裝飾函數的值
   return deco

@timer  # test1 = timer(test1)
def test1():
   time.sleep(3)
   print("in the test1")

@timer  # test2 = timer(test2)
def test2(name, age):
   time.sleep(3)
   print("in the test2", name, age)

test1()
test2("Alice", 18)

1.4 分情況使用裝飾器的不同功能

其他類似,最外層加裝一層

import time
user, passwd = 'alice', '1234'
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("Welcome back! %s" % username)
                   res = func(*args, **kwargs)
                   print("---------Complete authentication---------")
                   return res
               else:
                   exit("Invalid username or password.")
           elif auth_type == 'ldap':
               print("Are you kidding me?")
       return wrapper
   return outer_wrapper

def index():
   print("Welcome to index page.")

@auth(auth_type="local")
def home():
   print("Welcome to home page.")

@auth(auth_type='ldap')
def forum():
   print("Welcome to forum page.")

index()
home()
forum()

二、列表生成式,迭代器&生成器

2.1 列表生成式

生成一個含有數字1-10的列表,并且每個數字要 * 2

# 1.循環
li = []
for i in range(1, 11):
    li.append(i * 2)
print(li)

代碼簡潔的列表生成式

# 2.列表生成式
li2 = [i * 2 for i in range(1, 11)]
print(li2)

for循環后面還可以加上if判斷

# 3.for循環后面還可以加上if判斷
li3 = [i * i for i in range(1, 11) if i % 2 == 0]   # 生成1-10的平方切且可以被2整除的列表
print(li3)

2.2 generator生成器

在Python中,這種一邊循環一邊計算的機制,稱為生成器:generator。

2.2.1 生成器特性
  1. 生成器只有在調用時才會生成相應的數據。
  2. 只記錄當前的位置。
  3. 只有一個__next__()方法。(2.7為next()
2.2.1 創建生成器

創建一個generator,有很多種方法。
第一種方法很簡單,只要把一個列表生成式的[]改成(),就創建了一個generator:

>>> l = [x * x for x in range(10)]
>>> l
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x * x for x in range(10))
>>> g
<generator object <genexpr> at 0x000002256EAB2E60>

可以通過next()函數獲得generator的下一個返回值,由于generator保存的是算法,每次調用next(g),就計算出g的下一個元素的值,直到計算到最后一個元素,沒有更多的元素時,拋出StopIteration的錯誤。

>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
>>> next(g)
16
>>> next(g)
25
>>> next(g)
36
>>> next(g)
49
>>> next(g)
64
>>> next(g)
81
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

但不斷調用next(g)實在是太變態了,正確的方法是使用for循環,因為generator也是可迭代對象:

>>> g = (x * x for x in range(10))
>>> for n in g:
...     print(n)
...
0
1
4
9
16
25
36
49
64
81

通過for循環來迭代generator,不需要關心StopIteration的錯誤。
generator非常強大。如果推算的算法比較復雜,用類似列表生成式的for循環無法實現的時候,還可以用函數來實現。
比如,著名的斐波拉契數列(Fibonacci),除第一個和第二個數外,任意一個數都可由前兩個數相加得到:1, 1, 2, 3, 5, 8, 13, 21, 34, ...

def fib(max):
   n, a, b = 0, 0, 1
   while n < max:
       print(b)
       a, b = b, a + b
       n = n + 1
   return 'done'

可以輸出斐波那契數列的前N個數:

>>> fib(10)
1
1
2
3
5
8
13
21
34
55
done

上面的函數和generator僅一步之遙。要把fib函數變成generator,只需要把print(b)改為yield b就可以了:

def fib(max):
   n, a, b = 0, 0, 1
   while n < max:
       #print(b)
       yield  b
       a, b = b, a + b
       n += 1

同樣的,把函數改成generator后,可以使用__next__()來獲取下一個返回值,或者使用for循環來迭代。
通過yield實現在單線程的情況下實現并發運算的效果

import time
def customer(name):
   print("%s 準備吃包子了!" % name)
   while True:
       baozi = yield
       print("包子[%s]來了,被[%s]吃了!" % (baozi, name))

def producer(name):
   c = customer('A')
   c2 = customer('B')
   c.__next__()
   c2.__next__()
   print("%s要開始做包子了啊!" % name)
   for i in range(10):
       time.sleep(1)
       print("第%d批包子做好了!" % (i + 1))
       c.send(i + 1)
       c2.send(i + 1)

producer("Will")

三、迭代器

3.1 Iterable可迭代對象

可以直接作用于for循環的數據類型有以下幾種:

  1. 集合數據類型,如list、tuple、dict、set、str等;
  2. generator:包括生成器和帶yield的generator function。
    這些可以直接作用于for循環的對象統稱為可迭代對象:Iterable
    可以使用isinstance()判斷一個對象是否是Iterable對象:
>>> 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

3.2 Iterator迭代器

生成器不但可以作用于for循環,還可以被next()函數不斷調用并返回下一個值,直到最后拋出StopIteration錯誤表示無法繼續返回下一個值了。
可以被next()函數調用并不斷返回下一個值的對象稱為迭代器:Iterator
可以使用isinstance()判斷一個對象是否是Iterator對象:

>>> from collections import Iterator
>>> isinstance((x for x in range(10)), Iterator)
True
>>> isinstance([], Iterator)
False
>>> isinstance({}, Iterator)
False
>>> isinstance('abc', Iterator)
False

3.3 將Iterable可迭代對象變為Iterator迭代器

生成器都是Iterator對象,但list、dict、str雖然是Iterable,卻不是Iterator。
把list、dict、str等Iterable變成Iterator可以使用iter()函數:

>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True

3.4 為什么list、dict、str等數據類型不是Iterator

這是因為Python的Iterator對象表示的是一個數據流,Iterator對象可以被next()函數調用并不斷返回下一個數據,直到沒有數據時拋出StopIteration錯誤。可以把這個數據流看做是一個有序序列,但我們卻不能提前知道序列的長度,只能不斷通過next()函數實現按需計算下一個數據,所以Iterator的計算是惰性的,只有在需要返回下一個數據時它才會計算。
Iterator甚至可以表示一個無限大的數據流,例如全體自然數。而使用list是永遠不可能存儲全體自然數的。

3.5 小結

  1. 凡是可作用于for循環的對象都是Iterable類型;
  2. 凡是可作用于next()函數的對象都是Iterator類型,它們表示一個惰性計算的序列;
  3. 集合數據類型如list、dict、str等是Iterable但不是Iterator,不過可以通過Iter()函數獲得一個Iterator對象。
  4. Python的for循環本質上就是通過不斷調用next()函數實現的,例如:
for x in [1, 2, 3, 4, 5]:
    pass

五、匿名函數

匿名函數就是不需要顯式的指定函數

#這段代碼
def calc(n):
    return n**n
print(calc(10))
 
#換成匿名函數
calc = lambda n:n**n
print(calc(10))

匿名函數主要是和其它函數搭配使用的

>>> res = map(lambda x:x**2, [1, 5, 7, 4, 8])
>>> for i in res:print(i)
... 
1
25
49
16
64

四、內置函數

內置參數詳解

Built-in Functions

函數名 函數功能 函數名 函數功能
abs() 取絕對值 all() 一個可迭代對象中的所有元素均為True,則返回True。為空時返回False
any() 一個可迭代對象中的任一元素為True,則返回True。為空時返回False ascii() 將一個對象變為可打印的字符串形式
bin() 將一個整數轉換為一個二進制字符串 bool() 根據返回布爾值,True or False
bytearray() 返回bytes數組,該數組可以修改 bytes() 返回為bytes對象
callable() 判斷是否可調用,True or False compile() 編譯對象
chr() 返回數字所對應的Unicode字符 ord() 返回字符所對應的Unicode編碼
dir() 查詢參數所屬類型的可用方法 divmod() 輸入2個參數,返回他們的商和余數
enumerate 枚舉 eval() 將字符串str當成有效的表達式來求值并返回計算結果
exec() 執行儲存在字符串或文件中的Python語句 filter() 按照條件過濾
map() 按照條件生成新的列表 frozenset() 不可修改的集合
globals() 以字典的形式返回整個程序文件內的變量情況 hash() 返回該對象的hash值
hax() 將數字轉為16進制 local() 返回局部變量
max() 返回參數中的最大值 min() 返回參數中的最小值
oct() 將數字轉為8進制 pow() 求冪x**y
round() 保留小數后n位 sorted() 轉為列表后排序
vars() 返回對象所有屬性名 zip() 將兩組列表以元組形式拼在一起

五、軟件目錄結構規范

5.1 為什么要設計好目錄結構?

"項目目錄結構"其實也是屬于"可讀性和可維護性"的范疇,我們設計一個層次清晰的目錄結構,就是為了達到以下兩點:

  1. 可讀性高: 不熟悉這個項目的代碼的人,一眼就能看懂目錄結構,知道程序啟動腳本是哪個,測試目錄在哪兒,配置文件在哪兒等等。從而非常快速的了解這個項目。
  2. 可維護性高: 定義好組織規則后,維護者就能很明確地知道,新增的哪個文件和代碼應該放在什么目錄之下。這個好處是,隨著時間的推移,代碼/配置的規模增加,項目結構不會混亂,仍然能夠組織良好。

5.2 目錄組織方式

關于如何組織一個較好的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

簡要解釋一下:

  1. bin/: 存放項目的一些可執行文件,當然你可以起名script/之類的也行。
  2. foo/: 存放項目的所有源代碼。(1) 源代碼中的所有模塊、包都應該放在此目錄。不要置于頂層目錄。(2) 其子目錄tests/存放單元測試代碼; (3) 程序的入口最好命名為main.py。
  3. docs/: 存放一些文檔。
  4. setup.py: 安裝、部署、打包的腳本。
  5. requirements.txt: 存放軟件依賴的外部Python包列表。
  6. README: 項目說明文件。

除此之外,有一些方案給出了更加多的內容。比如LICENSE.txt,ChangeLog.txt文件等,我沒有列在這里,因為這些東西主要是項目開源的時候需要用到。如果你想寫一個開源軟件,目錄該如何組織,可以參考這篇文章

關于README的內容
這個我覺得是每個項目都應該有的一個文件,目的是能簡要描述該項目的信息,讓讀者快速了解這個項目。
它需要說明以下幾個事項:

  1. 軟件定位,軟件的基本功能。
  2. 運行代碼的方法: 安裝環境、啟動命令等。
  3. 簡要的使用說明。
  4. 代碼目錄結構說明,更詳細點可以說明軟件的基本原理。
  5. 常見問題說明。

可以參考Redis源碼中Readme的寫法,這里面簡潔但是清晰的描述了Redis功能和源碼結構。

不同目錄間進行模塊調用

調用目錄


作業:員工信息表程序 - 詳細描述參考

  1. 實現增刪改查操作:
  2. 可進行模糊查詢,語法至少支持下面3種:
    select name,age from staff_table where age > 22
    select * from staff_table where dept = "IT"
    select * from staff_table where enroll_date like "2013"
  3. 查到的信息,打印后,最后面還要顯示查到的條數
  4. 可創建新員工紀錄,以phone做唯一鍵,staff_id需自增
  5. 可刪除指定員工信息紀錄,輸入員工id,即可刪除
  6. 可修改員工信息,語法如下:
    UPDATE staff_table SET dept = "Market" WHERE where dept = "IT"
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,412評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,514評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,373評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,975評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,743評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,199評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,262評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,414評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,951評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,780評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,527評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,218評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,649評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,889評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,673評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,967評論 2 374

推薦閱讀更多精彩內容

  • 1.1==,is的使用 ·is是比較兩個引用是否指向了同一個對象(引用比較)。 ·==是比較兩個對象是否相等。 1...
    TENG書閱讀 740評論 0 0
  • 教程總綱:http://www.runoob.com/python/python-tutorial.html 進階...
    健康哥哥閱讀 2,054評論 1 3
  • python學習筆記 聲明:學習筆記主要是根據廖雪峰官方網站python學習學習的,另外根據自己平時的積累進行修正...
    renyangfar閱讀 3,076評論 0 10
  • 切片 取一個list(列表)或tuple(元組)的部分元素是非常常見的操作Python提供了切片(Slice)操作...
    upupSue閱讀 550評論 0 1
  • easy_install和pip都是用來下載安裝Python一個公共資源庫PyPI的相關資源包的 首先安裝easy...
    2010jing閱讀 842評論 2 0