Day04 - Python基礎(chǔ)4

Day04的課程要點(diǎn)記錄
詳細(xì)教程地址:Day4 - Python基礎(chǔ)4 | python裝飾器

一、裝飾器

1.1 定義

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

  1. 不能修改被裝飾的函數(shù)的源代碼
  2. 不能修改被裝飾的函數(shù)的調(diào)用方式

1.2 實(shí)現(xiàn)裝飾器的知識(shí)儲(chǔ)備:

1.2.1 函數(shù)即“變量”
1.2.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()  # 就是bar(),但是改變了函數(shù)的調(diào)用方式
   stop_time = time.time()
   print("the func is running in %s second." % (stop_time - start_time))

test1(bar)      # 相當(dāng)于func = bar
  • 返回值中包含函數(shù)名,不修改函數(shù)的調(diào)用方式。
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的內(nèi)存地址重新給回到bar
  • 嵌套函數(shù)
    在一個(gè)函數(shù)的函數(shù)體內(nèi)用def去聲明一個(gè)函數(shù)

1.3 實(shí)現(xiàn)裝飾器

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

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()

被裝飾函數(shù)可能出現(xiàn)所需參數(shù)數(shù)量不等的情況,通用寫法如下:

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    # 返回被裝飾函數(shù)的值
   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 列表生成式

生成一個(gè)含有數(shù)字1-10的列表,并且每個(gè)數(shù)字要 * 2

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

代碼簡(jiǎn)潔的列表生成式

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

for循環(huán)后面還可以加上if判斷

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

2.2 generator生成器

在Python中,這種一邊循環(huán)一邊計(jì)算的機(jī)制,稱為生成器:generator。

2.2.1 生成器特性
  1. 生成器只有在調(diào)用時(shí)才會(huì)生成相應(yīng)的數(shù)據(jù)。
  2. 只記錄當(dāng)前的位置。
  3. 只有一個(gè)__next__()方法。(2.7為next()
2.2.1 創(chuàng)建生成器

創(chuàng)建一個(gè)generator,有很多種方法。
第一種方法很簡(jiǎn)單,只要把一個(gè)列表生成式的[]改成(),就創(chuàng)建了一個(gè)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()函數(shù)獲得generator的下一個(gè)返回值,由于generator保存的是算法,每次調(diào)用next(g),就計(jì)算出g的下一個(gè)元素的值,直到計(jì)算到最后一個(gè)元素,沒有更多的元素時(shí),拋出StopIteration的錯(cuò)誤。

>>> 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

但不斷調(diào)用next(g)實(shí)在是太變態(tài)了,正確的方法是使用for循環(huán),因?yàn)間enerator也是可迭代對(duì)象:

>>> 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循環(huán)來迭代generator,不需要關(guān)心StopIteration的錯(cuò)誤。
generator非常強(qiáng)大。如果推算的算法比較復(fù)雜,用類似列表生成式的for循環(huán)無法實(shí)現(xiàn)的時(shí)候,還可以用函數(shù)來實(shí)現(xiàn)。
比如,著名的斐波拉契數(shù)列(Fibonacci),除第一個(gè)和第二個(gè)數(shù)外,任意一個(gè)數(shù)都可由前兩個(gè)數(shù)相加得到: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'

可以輸出斐波那契數(shù)列的前N個(gè)數(shù):

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

上面的函數(shù)和generator僅一步之遙。要把fib函數(shù)變成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

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

import time
def customer(name):
   print("%s 準(zhǔn)備吃包子了!" % 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可迭代對(duì)象

可以直接作用于for循環(huán)的數(shù)據(jù)類型有以下幾種:

  1. 集合數(shù)據(jù)類型,如list、tuple、dict、set、str等;
  2. generator:包括生成器和帶yield的generator function。
    這些可以直接作用于for循環(huán)的對(duì)象統(tǒng)稱為可迭代對(duì)象:Iterable
    可以使用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

3.2 Iterator迭代器

生成器不但可以作用于for循環(huán),還可以被next()函數(shù)不斷調(diào)用并返回下一個(gè)值,直到最后拋出StopIteration錯(cuò)誤表示無法繼續(xù)返回下一個(gè)值了。
可以被next()函數(shù)調(diào)用并不斷返回下一個(gè)值的對(duì)象稱為迭代器: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

3.3 將Iterable可迭代對(duì)象變?yōu)?code>Iterator迭代器

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

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

3.4 為什么list、dict、str等數(shù)據(jù)類型不是Iterator

這是因?yàn)镻ython的Iterator對(duì)象表示的是一個(gè)數(shù)據(jù)流,Iterator對(duì)象可以被next()函數(shù)調(diào)用并不斷返回下一個(gè)數(shù)據(jù),直到?jīng)]有數(shù)據(jù)時(shí)拋出StopIteration錯(cuò)誤??梢园堰@個(gè)數(shù)據(jù)流看做是一個(gè)有序序列,但我們卻不能提前知道序列的長(zhǎng)度,只能不斷通過next()函數(shù)實(shí)現(xiàn)按需計(jì)算下一個(gè)數(shù)據(jù),所以Iterator的計(jì)算是惰性的,只有在需要返回下一個(gè)數(shù)據(jù)時(shí)它才會(huì)計(jì)算。
Iterator甚至可以表示一個(gè)無限大的數(shù)據(jù)流,例如全體自然數(shù)。而使用list是永遠(yuǎn)不可能存儲(chǔ)全體自然數(shù)的。

3.5 小結(jié)

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

五、匿名函數(shù)

匿名函數(shù)就是不需要顯式的指定函數(shù)

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

匿名函數(shù)主要是和其它函數(shù)搭配使用的

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

四、內(nèi)置函數(shù)

內(nèi)置參數(shù)詳解

Built-in Functions

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

五、軟件目錄結(jié)構(gòu)規(guī)范

5.1 為什么要設(shè)計(jì)好目錄結(jié)構(gòu)?

"項(xiàng)目目錄結(jié)構(gòu)"其實(shí)也是屬于"可讀性和可維護(hù)性"的范疇,我們?cè)O(shè)計(jì)一個(gè)層次清晰的目錄結(jié)構(gòu),就是為了達(dá)到以下兩點(diǎn):

  1. 可讀性高: 不熟悉這個(gè)項(xiàng)目的代碼的人,一眼就能看懂目錄結(jié)構(gòu),知道程序啟動(dòng)腳本是哪個(gè),測(cè)試目錄在哪兒,配置文件在哪兒等等。從而非??焖俚牧私膺@個(gè)項(xiàng)目。
  2. 可維護(hù)性高: 定義好組織規(guī)則后,維護(hù)者就能很明確地知道,新增的哪個(gè)文件和代碼應(yīng)該放在什么目錄之下。這個(gè)好處是,隨著時(shí)間的推移,代碼/配置的規(guī)模增加,項(xiàng)目結(jié)構(gòu)不會(huì)混亂,仍然能夠組織良好。

5.2 目錄組織方式

關(guān)于如何組織一個(gè)較好的Python工程目錄結(jié)構(gòu),已經(jīng)有一些得到了共識(shí)的目錄結(jié)構(gòu)。在Stackoverflow的這個(gè)問題上,能看到大家對(duì)Python目錄結(jié)構(gòu)的討論。
假設(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/之類的也行。
  2. foo/: 存放項(xiàng)目的所有源代碼。(1) 源代碼中的所有模塊、包都應(yīng)該放在此目錄。不要置于頂層目錄。(2) 其子目錄tests/存放單元測(cè)試代碼; (3) 程序的入口最好命名為main.py。
  3. docs/: 存放一些文檔。
  4. setup.py: 安裝、部署、打包的腳本。
  5. requirements.txt: 存放軟件依賴的外部Python包列表。
  6. README: 項(xiàng)目說明文件。

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

關(guān)于README的內(nèi)容
這個(gè)我覺得是每個(gè)項(xiàng)目都應(yīng)該有的一個(gè)文件,目的是能簡(jiǎn)要描述該項(xiàng)目的信息,讓讀者快速了解這個(gè)項(xiàng)目。
它需要說明以下幾個(gè)事項(xiàng):

  1. 軟件定位,軟件的基本功能。
  2. 運(yùn)行代碼的方法: 安裝環(huán)境、啟動(dòng)命令等。
  3. 簡(jiǎn)要的使用說明。
  4. 代碼目錄結(jié)構(gòu)說明,更詳細(xì)點(diǎn)可以說明軟件的基本原理。
  5. 常見問題說明。

可以參考Redis源碼中Readme的寫法,這里面簡(jiǎn)潔但是清晰的描述了Redis功能和源碼結(jié)構(gòu)。

不同目錄間進(jìn)行模塊調(diào)用

調(diào)用目錄


作業(yè):?jiǎn)T工信息表程序 - 詳細(xì)描述參考

  1. 實(shí)現(xiàn)增刪改查操作:
  2. 可進(jìn)行模糊查詢,語法至少支持下面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. 查到的信息,打印后,最后面還要顯示查到的條數(shù)
  4. 可創(chuàng)建新員工紀(jì)錄,以phone做唯一鍵,staff_id需自增
  5. 可刪除指定員工信息紀(jì)錄,輸入員工id,即可刪除
  6. 可修改員工信息,語法如下:
    UPDATE staff_table SET dept = "Market" WHERE where dept = "IT"
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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

友情鏈接更多精彩內(nèi)容