早知如此!來看看 Python 函數(shù)的七個秘密
Python 函數(shù)的七個重要知識點:類型提示以增加代碼可讀性;*args和**kwargs、Lambda函數(shù)和高階函數(shù)提供代碼靈活性;裝飾器讓我們無需修改原函數(shù)即可改變其行為;生成器函數(shù)實現(xiàn)惰性評估;最后,魔術方法在Python類中定義了特殊行為。這些知識點是每個Python開發(fā)者的必備工具。
(1)類型提示
下面的代碼在工作中隨處可見!
def magic(a, b):
return a + b
我們可以輕易地編寫如上的函數(shù),因為 Python 是動態(tài)類型的——也就是說,變量數(shù)據(jù)類型是在運行時確定的。
def magic(a: int, b: int) -> int:
return a + b
不過,也可以編寫如上與先前完全相同,但帶有類型提示的函數(shù):
- a應為整數(shù)
- b也應為整數(shù)
- 函數(shù)的返回值應該是一個整數(shù)
當代碼庫變的更大時,類型提示在保持代碼盡可能地具有可讀性方面變得越來越重要。想想看,有 10,000 個函數(shù),你需要推斷它們所接受的數(shù)據(jù)類型以及它們的返回類型,那就太“好玩”了。
def test1(ls: list[int], x: float) -> list[float]:
pass
ls是整數(shù)列表,x應該是浮點數(shù),函數(shù)應返回浮點數(shù)列表。
def test2(a: str, b: bool, c: dict[str, str]) -> dict[str, int]:
pass
a應為字符串,b應為布爾值,c應為字典,其中鍵為字符串,值為字符串。返回值應為一個字典,其鍵為字符串,但值為整數(shù)。
請注意:類型提示只是提示,并不強制執(zhí)行。如果我們沒有遵循類型提示給出的類型,Python 仍會允許這個操作。
(2)*args 和 **kwargs
在真實的開發(fā)中,會有大量的*args 和**kwargs參數(shù):
- args* 允許函數(shù)接受無限數(shù)量的位置參數(shù)。
- kwargs 允許函數(shù)接受無限數(shù)量的關鍵字參數(shù)。
所有的位置參數(shù)都會被捕獲為元組參數(shù),所有的關鍵字參數(shù)都會被捕獲為字典參數(shù)。
magic(1, 2, 'apple', a=4, b=5, c=[1, 2, 3])
- 1、2 和 'apple'是位置參數(shù)。
- a=4、b=5 和 c=[1,2,3]是關鍵字參數(shù)。
def magic(*args, **kwargs):
print('args =', args)
print('kwargs =', kwargs)
magic(1, 2, 'apple', a=4, b=5, c=[1, 2, 3])
# args = (1, 2, 'apple')
# kwargs = {'a':4, 'b':5, 'c':[1, 2, 3]}
- 所有位置參數(shù)都被捕獲為元組參數(shù)。
- 所有關鍵字參數(shù)都被捕獲為字典參數(shù)。
(3)Lambda 函數(shù)
Lambda 函數(shù)是一種小型匿名函數(shù)。例如:
def add(x, y):
return x + y
可以將它重寫為 lambda 函數(shù):
add = lambda x, y: x + y
函數(shù)的輸入在":"之前,返回值在":"之后:
# 另一個例子
def add10(x):
return x + 10
# 等同于
add10 = lambda x: x + 10
Lambda 函數(shù)可以是匿名的,并且在需要將一個函數(shù)作為另一個函數(shù)的參數(shù)時,Lambda 函數(shù)可能非常有用:
def apply(func, x):
return func(x)
x = apply(lambda x: x + 10, 7)
print(x) # 17
(4)高階函數(shù)
高階函數(shù)可以接受另一個函數(shù)作為參數(shù),或者返回另一個函數(shù),或者同時滿足這兩個條件。比如以下的 apply 函數(shù):
def apply(func, x):
return func(x)
x = apply(lambda x: x + 10, 7)
print(x) # 17
在上述示例中,apply函數(shù)就是一個高階函數(shù),因為它接受了另一個函數(shù)即func作為參數(shù)。
能熟練運用高階函數(shù),會讓你的代碼更為靈活強大。
(5)裝飾器
裝飾器是能夠接受并返回函數(shù)的特殊高階函數(shù),它的目標是在不改變原函數(shù)源代碼的情況下改變函數(shù)的行為。
注意:裝飾器函數(shù)既接受函數(shù)又返回函數(shù)。
# 這是 decorator 函數(shù)
def add_exclamation(func):
def wrapper(name):
return func(name) + '!'
return wrapper
# 這是被裝飾的函數(shù)
def hello(name):
return 'hello ' + name
# 實際的裝飾動作在此進行
hello = add_exclamation(hello)
# 現(xiàn)在,我們的函數(shù)的行為略有變化
print(hello('tom')) # hello tom!
我們可以用@add_exclamation替代hello=add_exclamation(hello),因為實際上它們做的事情是完全一樣的。
def add_exclamation(func):
def wrapper(name):
return func(name) + '!'
return wrapper
@add_exclamation
def hello(name):
return 'hello ' + name
print(hello('tom')) # hello tom!
裝飾器可以用于日志記錄、計時、處理異常、驗證身份、REST API等任務,是一個非常有用的工具!
(6)生成器函數(shù)
你是否曾遇到過這樣的類似于<generator object x at 0x1029b4a90>的信息?當我們使用生成器函數(shù)時,就會出現(xiàn)這種情況。
當我們使用yield時,函數(shù)就變成了生成器函數(shù)。yield和return關鍵字類似,都是從函數(shù)中輸出一個值,但與return不同的是,yield并不會停止整個函數(shù)。
# 常規(guī)函數(shù)
def test():
return [1, 2, 3]
x = test()
print(x) # [1, 2, 3]
# 生成器函數(shù),但我們?nèi)匀环Q之為標準函數(shù)
def test():
yield 1
yield 2
yield 3
x = test()
print(x) # <generator object x at 0x1029b4a90>
# 生成器函數(shù),但我們使用循環(huán)調(diào)用它
def test():
yield 1
yield 2
yield 3
for i in test():
print(i, end=' ')
# 1 2 3
生成器執(zhí)行了惰性評估——也就是說,它只在絕對必要的情況下起作用(例如,當我們使用循環(huán)時)。這使代碼在某些方面更為高效。
(7)特殊的魔法(Dunder)方法
這些在企業(yè) Python 代碼中也隨處可見,特別是在涉及到面向對象編程時。魔術方法以兩個下劃線字符開始和結束,例如__init__,__str__,__getitem__,__add__等等。它們在 Python 類中定義了特殊行為。
class Dog:
# 定義如何為 dog 分配屬性
def __init__(self, name, age):
self.name = name
self.age = age
# 定義當我們對 dog 執(zhí)行 str() 時會返回什么
def __str__(self):
return 'Dog!'
# 定義當我們執(zhí)行 dog + something 時會返回什么
def __add__(self, something):
return 'Dog ' + str(something)
# 定義當我們執(zhí)行 dog[something] 時會返回什么
def __getitem__(self, something):
return 123
了解更多的魔術方法可以讓你的類更靈活,滿足更多場景的需求。
總結
本文概述了 Python 函數(shù)的七個重要知識點:類型提示以增加代碼可讀性;*args和**kwargs、Lambda函數(shù)和高階函數(shù)提供代碼靈活性;裝飾器讓我們無需修改原函數(shù)即可改變其行為;生成器函數(shù)實現(xiàn)惰性評估;最后,魔術方法在Python類中定義了特殊行為。這些知識點是每個Python開發(fā)者的必備工具。
希望這篇文章對你有所幫助,讓你能更好地理解 python 函數(shù)。