
文章目錄
- 前言
- 函式的不定長引數
- lambda函式
- 創建包
- 生成器
- 什么是生成器
- 為什么要有生成器
- 如何創造一個生成器
- 把一個串列推導式的[]改成()
- yield關鍵字
- 生成器的作業原理
- 代碼示例
- 裝飾器
- 嵌套函式
前言
時隔20天,也該給這個系列畫上一個句號啦,
后期我會對這個系列進行一個整合,預計會整理為13篇,不用擔心,是原地整理,一般不會刪博客,
這一篇不是爬蟲相關,是我重新梳理了Python中的相關知識之后,發現了一些和C++異同之處,覺得應該再整理一份,
函式的不定長引數
有的時候,你會需要一個函式去處理比它預設時更多的引數,這時候如果沒有預先占位,就比較尷尬了,
python自定義函式中有兩中不定長引數,第一種是*name,第二種是**name,加了星號 * 的引數會以元組(tuple)的形式匯入,存放所有未命名的變數引數,加了兩個星號 ** 的引數會以字典的形式匯入,
第一種形式的不定長引數,在傳入額外的引數時可以不用指明引數名,直接傳入引數值即可,第二種因為回傳的是字典,所以傳入時需要指定引數名,
下面是兩個簡單的栗子:
def funA(a, b, *args):
print(a)
print(b)
print(args)
funA(1, 2, 3, 5, 6, 7)
輸出如下:
1
2
(3, 5, 6, 7)
def funB(a, b, **vardict):
print(a)
print(b)
print(vardict)
print(vardict['l'])
funB(1, 2, l=3, m=4)
輸出結果如下:
1
2
{'l': 3, 'm': 4}
3
至于它的真實應用場景,小白是用不上了,不過你們以后要往深了走肯定是要碰上的,
有興趣的話可以看一下C++版的:argc和argv的妙用
lambda函式
匿名函式lambda:是指一類無需定義識別符號(函式名)的函式或子程式,lambda 函式可以接收任意多個引數 (包括可選引數) 并且回傳單個運算式的值,
說明:我將它們用在需要封裝特殊的、非重用代碼上,避免令我的代碼充斥著大量單行函式,
代碼示例:
p = lambda x,y:x+y
print(p(4,6))
a=lambda x:x*x
print(a(3)) # 注意:這里直接a(3)可以執行,但沒有輸出的,前面的print不能少
a = lambda x,y,z:(x+8)*y-z
print(a(5,6,8)
在C++當中這叫做宏定義函式,各位不要懼怕C++,更不要去排斥,如果還年輕,要在程式員這一行越走越遠,C++是個不錯的首選,
我們來看一下C++中的實作:C++ #define
創建包
包其實就是檔案夾,更確切的說,是一個包含“init.py”檔案的檔案夾,
因此,如果我們想手動創建一個包,只需進行以下 2 步操作:
1、新建一個檔案夾,檔案夾的名稱就是新建包的包名;
2、在該檔案夾中,創建一個 __init__.py 檔案(前后各有 2 個下劃線‘_’),該檔案中可以不撰寫任何代碼,當然,也可以撰寫一些 Python 初始化代碼,則當有其它程式檔案匯入包時,會自動執行該檔案中的代碼
生成器
什么是生成器
在Python中,一邊回圈一邊計算的機制,稱為生成器:generator,
為什么要有生成器
串列所有資料都在記憶體中,如果有海量資料的話將會非常耗記憶體,
如:僅僅需要訪問前面幾個元素,那后面絕大多數元素占用的空間都白白浪費了,
如果串列元素按照某種演算法推算出來,那我們就可以在回圈的程序中不斷推算出后續的元素,這樣就不必創建完整的list,從而節省大量的空間,
簡單一句話:我又想要得到龐大的資料,又想讓它占用空間少,那就用生成器!
如何創造一個生成器
把一個串列推導式的[]改成()
L = [x * x for x in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
g = (x * x for x in range(10))
<generator object <genexpr> at 0x1022ef630>
yield關鍵字
如果一個函式中包含yield關鍵字,那么這個函式就不再是一個普通函式,而是一個generator,呼叫函式就是創建了一個生成器(generator)物件,
生成器的作業原理
(1)生成器(generator)能夠迭代的關鍵是它有一個next()方法,作業原理就是通過重復呼叫next()方法,直到捕獲一個例外,
(2)帶有 yield 的函式不再是一個普通函式,而是一個生成器generator,可用next()呼叫生成器物件來取值,next 兩種方式 t.next() | next(t),(基本上不會用next()來獲取下一個回傳值,而是直接使用for回圈來迭代),
(3)yield相當于 return 回傳一個值,并且記住這個回傳的位置,下次迭代時,代碼從yield的下一條陳述句開始執行,
(4).send() 和next()一樣,都能讓生成器繼續往下走一步(下次遇到yield停),但send()能傳一個值,這個值作為yield運算式整體的結果
換句話說,就是send可以強行修改上一個yield運算式值,比如函式中有一個yield賦值,a = yield 5,第一次迭代到這里會回傳5,a還沒有賦值,第二次迭代時,使用.send(10),那么,就是強行修改yield 5運算式的值為10,本來是5的,那么a=10,
代碼示例
來看一段楊輝三角的代碼示例:
def triangle():
_list, new_list = [], []
while True:
length = len(_list)
if length == 0:
new_list.append(1)
else:
for times in range(length + 1):
if times == 0:
new_list.append(1)
elif times == length:
new_list.append(1)
else:
temp = _list[times - 1] + _list[times]
new_list.append(temp)
yield new_list #回傳值,然后掛起函式,等待下一次呼叫
_list = new_list.copy()#呼叫后會繼續執行下去
new_list.clear()
n = 0
for result in triangle():
n += 1
print(result)
if n == 10:
break
[1]
[1, 1]
[1, 2, 1]
[1, 3, 3, 1]
[1, 4, 6, 4, 1]
[1, 5, 10, 10, 5, 1]
[1, 6, 15, 20, 15, 6, 1]
[1, 7, 21, 35, 35, 21, 7, 1]
[1, 8, 28, 56, 70, 56, 28, 8, 1]
[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
如果覺得太難啦,那就換一個斐波那契吧哈哈哈哈
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
# 拿去列印ab看看
生成器這個東西嘛,說實在的,學C++兩年來沒見過類似的,
裝飾器
一看這個名字,我就想到了裝飾者模式,
Python的裝飾器(decorator)可以說是Python的一個神器,它可以在不改變一個函式代碼和呼叫方式的情況下給函式添加新的功能,
牛逼都吹成這樣了,我也不想多說什么,直接上代碼吧:
import time
def time_it(func):
def inner():
start = time.time()
func()
end = time.time()
print('用時:{}秒'.format(end-start))
return inner
@time_it
def func1():
time.sleep(2)
print("Func1 is running.")
if __name__ == '__main__':
func1()
能看懂不?怪我,我的裝飾者模式寫了兩篇,也沒寫出精華來,就不能放鏈接給你們了,
看了裝飾器,我才知道裝飾著模式的精妙之處,強!!!
運行結果:
Func1 is running.
用時:2.0056326389312744
嵌套函式
講到裝飾器,就不得不講一下內函式(我也不知道為什么,每本書上都是這么說的)
我們先來看一個最簡單的嵌套函式的例子,
def outer():
x = 1
def inner():
y = x + 1
print(y)
inner()
outer() #輸出結果 2
可曾有見過這類函式 ?
def outer():
x = 1
def inner():
y = x + 1
print(y)
return inner
outer() # 輸出<function outer.<locals>.inner at 0x039248E8>
f1 = outer()
f1() # 輸出2
上面那倆比較簡單,我們來看個抽象的:
def decorator(func):
def inner(*args, **kwargs):
add_other_actions()
return func(*args, **kwargs)
return inner
能轉的過彎嗎?
這里就要用到裝飾器了,
我們現在可以開始動手寫個名為hint的裝飾器了,其作用是在某個函式運行前給我們提示,這里外函式以hint命名,內函式以常用的wrapper(包裹函式)命名,
def hint(func):
def wrapper(*args, **kwargs):
print('{} is running'.format(func.__name__))
return func(*args, **kwargs)
return wrapper
@hint
def hello():
print("Hello!")
我們現在對hello已經進行了裝飾,當我們呼叫hello()時,我們可以看到如下結果,
hello()
hello is running.
Hello!
值得一提的是被裝飾器裝飾過的函式看上去名字沒變,其實已經變了,當你運行hello()后,你會發現它的名字已經悄悄變成了wrapper,這顯然不是我們想要的,這一點也不奇怪,因為外函式回傳的是由wrapper函式和其外部參考變陣列成的閉包,
為了解決這個問題保證裝飾過的函式__name__屬性不變,我們可以使用functools模塊里的wraps方法,先對func變數進行wraps,
from functools import wraps
def hint(func):
@wraps(func)
def wrapper(*args, **kwargs):
print('{} is running'.format(func.__name__))
return func(*args, **kwargs)
return wrapper
@hint
def hello():
print("Hello!")
這是一個通用裝飾器的模板,要不要收藏起來就看你個人了,
就算你不想看,我也要再放一個高級的裝飾器通用模板,因為我自己早晚用得上:
from functools import wraps
def hint(coder):
def wrapper(func):
@wraps(func)
def inner_wrapper(*args, **kwargs):
print('{} is running'.format(func.__name__))
print('Coder: {}'.format(coder))
return func(*args, **kwargs)
return inner_wrapper
return wrapper
@hint(coder="John")
def hello():
print("Hello!")
這就叫做:書又不是讀給別人的,
類裝飾器也一并放這兒了:
from functools import wraps
#類的裝飾器寫法, 不帶引數
class Hint(object):
def __init__(self, func):
self.func = func
@wraps(func)
def __call__(self, *args, **kwargs):
print('{} is running'.format(self.func.__name__))
return self.func(*args, **kwargs)
#類的裝飾器寫法, 帶引數
class Hint(object):
def __init__(self, coder=None):
self.coder = coder
def __call__(self, func):
@wraps(func)
def wrapper(*args, **kwargs):
print('{} is running'.format(func.__name__))
print('Coder: {}'.format(self.coder))
return func(*args, **kwargs) # 正式呼叫主要處理函式
return wrapper
裝飾器部分到此告一段落,
到這里,
這個系列,就到這里啦,感謝大家一路相伴,
要是想我了,或者說一個人學Python有些孤單,這里有個傳送門:傳送門

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/234905.html
標籤:python
