Python (六) 函式
什麼時函式?
通俗的講,函式就是寫一段程式碼實現了某個小功能,然後把這些程式碼集中到一塊,起一個名字,下次可以根據這個名字再次使用。
函式有什麼作用?
- 方便程式碼的重用。
- 分解任務,簡化程式邏輯。
- 是程式碼更加模組化。
函式有哪幾類?
- 內建函式(Python自帶函式)
- 第三方函式(第三方公司或者第三方個人開發出來的函式)
- 自定義函式
引數傳輸的方式:
多個引數
使用位置實參:直接用形參和實參一一對應
使用關鍵字引數:引數名稱=引數(關鍵字引數)不需要嚴格按照順序
使用容器作為實參:可以將一個列表或者字典作為一串引數傳入函式。
不定長引數
1.在引數前面加一個*號代表不定長引數,函式中將會把這個變數作為元組來處理。(預設用*args)
之所以將這個變數當作元組處理而不作為列表處理,是因為在函式體中我們要儘可能直接改變引數。
2.在引數前面加倆個*號,以字典的方式來使用函式。這種方式定義的函式必須使用關鍵字引數進行傳遞。(預設使用**kwrgs)
這裡有一個裝包和拆包的概念,所謂裝包就是將單獨的個體整合成一個整體集合,例如使用*args和**kwrgs;而拆包則是將一個整體拆分為單獨的個體。讀取*args和**kwargs。
預設引數:
預設引數指的是引數在大多數情況下都是預設的情況,預設引數在定義的時候需要使用關鍵字引數的方式傳遞,下面的形參sombody就是一個預設引數。
def hit( sombody = "呼呼") : print("我想打",sombody)ha hit("nim")
函式引數的一些注意點:
首先要明確在函式中什麼時值傳遞,什麼是引用傳遞。
值傳遞:僅僅是將資料的副本傳遞過去,傳遞過去的值的地址跟原資料不一致,修改副本不會引起原來資料的變化。
引用傳遞:所謂引用傳遞就是地址傳遞,通過相同的地址操控同一份地址,在函式中改變形參會引起實參的變化。
一定要注意在Python中函式一般 都是引用傳遞。(地址傳遞)
如果要使用原資料的副本進行函式操作,可以使用切片的方法進行拷貝,傳遞過去的就是一份原資料的副本。
如果是不可變的資料型別,在函式中進行修改一定會改變它的地址;如果是可變資料型別,採用簡單的新增刪除不會修改地址,而直接重新賦值會出現新的地址。
函式的返回值:
當我們需要接收函式的處理結果時,就必須使用return來接收這個結果。一定注意:return只會執行一次,後面的程式碼不會再被執行。
return的結果可以時元組,列表,字典。
函式的表述資訊:
當我們在寫一個函式時一定要寫一個函式的描述資訊,寫在開頭用三個引號括起來。
在使用中可以使用help(函式名)來檢視函式的功能。
函式描述資訊必須包含的內容:
- 說明當前函式的功能。
- 說明函式引數的資訊含義,資料型別,有沒有預設值,有的話說明預設值。
- 說明函式的返回結果。
函式的高階功能:
偏函式:
什麼時偏函式?當一個函式的預設引數大多數情況下時時不變的,但是有希望在指定的情況下改變其預設值,以此生成新的函式就是偏函式。我們可以通過functools模板的partial函式來改變函式的預設引數。
下面是一個使用偏函式的例子:
import functools
def test(a, b, c, d=2):
print(a + b + c + d)
test(1, 2, 3)
newTest = functools.partial(test, c=2)
newTest(1,1)
高階函式:
當一個函式的引數接收的是另一個函式,那麼這個函式就是一個高階函式。
下面是一個高階函式的例子,其中的sorted()就是一個高階函式
l = [{"name":"s2","age":18} , {"name":"sz2","age":19} , {"name":"kc","age":20}]
def getKey(x):
return x["age"]
result = sorted(l , key = getKey)
print(result)
注意點:在將函式作為引數時使用的只是函式的名稱,也就是說不用帶上函式引數列表(括號)
返回函式:
返回引數指的是函式內部返回的資料是另外一個函式,把這樣的操作稱之為“返回引數”。
def getFun(flag) :
def sum( a, b , c) :
return a + b + c
def jianfa( a, b, c) :
return a - b - c
if flag == "+" :
return sum
elif flag == "-" :
return jianfa
function = getFun("+")
print(function(1, 2, 3))
匿名函式:
匿名函式稱之為lambda函式,顧名思義就是指沒有名字的函式,匿名函式只適合用於簡單操作的函式。
使用的格式:lambda 引數1,引數2....:表示式(只能寫一個表示式)表示式的結果就是匿名函式的返回值。總之先使用lambda作為統一函式名,再加上一個表示式,表示式的結果就是返回值。
下面是一個使用匿名函式的例子
result = (lambda x, y: x + y)(1, 2)
print(result)
l = [{"name": "s2", "age": 18}, {"name": "sz2", "age": 19}, {"name" :"kc","age": 20}]
result = sorted(l, key = lambda x: x["age"])
print(result)
閉包函式:
在函式巢狀的前提下,內層函式引用了外層函式變數(包括引數),而外層函式又把n內層函式當作返回值進行返回。
這個內層函式加上所引用的外層變數稱之為閉包。
def test():
a = 10
def test2():
print(a + 7)
return test2
newFun = test()
newFun()
def line_config(content, length):
def line():
print("-"*(length//2) + content + "-"*(length//2))
return line
line1 = line_config("還哦",40)
line1()
line2 = line_config("還有我",80)
line2()
注意點:
在使用閉包函式時,如果要修改外層函式,必須在變數前面加上nonlocal宣告,否則會被當作閉包內新定義的變數。
當函式被執行時裡面的程式碼才會執行,在定義時是不會被執行的。
函式裝飾器:
利用其它函式取裝飾原本的函式。
裝飾器適合做重複性,冗餘度非常大的程式碼段落。一些基礎業務邏輯程式碼的冗餘度非常大,導致程式碼的複用性比較差,程式碼的維護性比較差。當我們要修改程式碼時,儘量在重用性非常高的地方進行修改,這時候裝飾器的優勢非常大。
函式的使用要遵循單一職責,函式內容單一優先實現函式名的功能。
下面是一些使用函式裝飾器的案例:
def checklogin(func) :
"""
這是一個要用來做裝飾器的函式,這個函式可以讓其它函式增加登陸驗證的操作
"""
def inner() :
"""
這是一個閉包函式,用來接收其它函式功能,使他們的功能和登陸驗證結合起來
"""
print("正在登陸驗證....")
func()
return inner
@checklogin # 裝飾器的寫法,相當於對下面的函式進行更新,是下面的發說說函式增加一個登陸驗證操作
def fss() :
print("發說說!")
# fss = checklogin(fss)
@checklogin # 裝飾器的寫法,相當於對下面的函式進行更新,是下面的發圖片函式增加一個登陸驗證操作
def ftp():
print("發圖片!")
# ftp = checklogin(ftp)
fss()
ftp()
案例2:裝飾器疊加:從上到下進行疊加;從下到上進行疊加。
def print_line(funce2) :
"""
這是一個用來做裝飾器的函式,給其它函式增加列印------的功能
"""
def inner() :
print("-" * 30)
funce2()
return inner
def print_star(funce2) :
"""
這是一個用來做裝飾器的函式,給其它函式增加列印*****的功能
"""
def inner() :
print("*" * 30)
funce2()
return inner
@print_star #使用上面的倆個裝飾器連續修飾,從下往上看。
@print_line
def print_name() :
print("I am Linjunhan !")
案例3:對有引數的函式進行裝飾
def zsq(func) :
"""
這是一個用來做修飾器函式,用來列印-------資訊
"""
def inner(*args ,**kwargs) :
print("-" * 30)
func(*args ,**kwargs)
return inner
@zsq
def pnum(num) :
print(num)
@zsq
def pnum2(num1,num2) :
print(num1,num2)
案例4:對有返回值的函式進行修飾。
def zsq(func):
def inner(*args, **kwargs):
print("-" * 30)
res = func(*args, **kwargs)
return res
return inner
@zsq # pnum = zsq(pnum)
def pnum(num1, num2, num3):
return num1 + num2 + num3
print(pnum(1, 2, 3))
案例5:帶有裝飾的修飾器 ,通過@修飾器(引數)的方式呼叫這個函式,並傳遞引數;並把返回值再次當作修飾器來使用。
def zsqs(char):
def zsq(func):
def inner():
func()
print(char * 30)
return inner
return zsq
@zsqs("*") # 利用函式呼叫zsq修飾器。
def f1():
print("666666")
f1()
生成器(特殊的迭代器):
生成器具有迭代器的特徵:
惰性計算資料,節省記憶體,一個一個拿出來用。
能夠記錄資料,並通過next()函式,訪問下一個函式。
具備可迭代性。
建立方式:把列表推導式[]改為()
生成器函式:
利用表示式建立:
l = (i for i in range(1, 100000) if i % 2 == 0)
print(next(l))
print(next(l))
print(next(l))
建立方式2:
def test():
print("xxx")
yield 1
print("a")
yield 2
print("b")
yield 3
print("c")
yield 4
print("d")
yield 5
print("e")
g = test()
print(g)
超出範圍,迭代停止。
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
return 'done'
f = fib(10)
print('fib(10):', f)
for x in f:
print(x)
# 超出範圍,迭代停止
def test2() :
print("xxxx")
res1 = yield 1
print(res1)
res2 = yield 2
print(res2)
res3 = yield 3
print(res3)
g = test2()
# print(g.__next__()) ##採用.__next__()的方法來訪問迭代器
#利用生成器的.send方法,這個方法有一個引數,時用來指定上一次被掛起的yield的返回值,如果第一次呼叫,引數必須為空。
# print(g.send())
print(g.__next__())
print(g.send("6666"))
g.close() # 生成器的關閉方法
遞迴函式(在函式內部再次呼叫函式)
def jiecheng(n):
"""
這個遞迴函式用計算引數n的階乘
:param n:
:return:
"""
if n == 1:
return 1
return n * (n - 1)
print(jiecheng(3))