不知道為什么網上總有人說 Python 的參數類型有 4 種啊,5 種啊,殊不知其實有 7 種。Python 的 7 種參數分別是?默認參數、位置參數、關鍵字參數、可變長位置參數、可變長關鍵字參數、僅位置參數?和?僅關鍵字參數。小白可能沒見過“可變長參數”,但是大部分人可能都沒見過“僅參數”,“僅參數”一般只會在開發模塊時才會用到,那么我為什么會知道呢?您猜……下面就讓我為你細細道來。
先看段代碼,一般人可看不懂哦:
def function(a, /, b, c=1, *, d=2, **e) -> None: ...
上面的這段代碼是可以正常運行的,它幾乎體現了所有參數類型,幾乎哈,不是完全。因為有幾種參數類型互相之間無法共存。
一、默認參數(Default Parameter)
默認參數簡單,就是字面意思,當你不給它值的時候,它會有個默認值,因為某些時候不傳具體的值,是缺省的,因此它也叫缺省參數。
def function(default_parameter: int = 1) -> int:return default_parameter
上面的函數,若不傳值給它,它會默認返回 1,若是傳了值,那就返回你傳入的值。是不是很簡單?若是這樣想,那你就大錯特錯了,您猜下面的代碼輸出是什么?
def function(default_parameter=[1]) -> None:print(default_parameter)default_parameter.append(default_parameter[-1] + 1)function()
function()
function()
下面是輸出的結果:
[1]
[1, 2]
[1, 2, 3]
具體解釋見(鏈接文章的第七點):Python 易錯點大集合
這是默認參數中最常見的坑,一般人都會往里跳,曾經有個公司的業務代碼中就犯了這個致命錯誤,直接導致服務器崩潰,因為那個列表變得異常大……
二、位置參數(Positional Parameter)
位置參數就是我們天天用的了,喜聞樂見啊,大家都會用的那種。
def function(a: int, b: float, c: str) -> None:print(a + b, c)
所謂“位置”,意思就是參數是按位置來傳遞的,位置參數的位置不是嚴格要求的,你可以像關鍵字參數那樣傳參,舉個栗子:
def function(a: int, b: int) -> None:print(a, b)function(1, 2)
function(b=2, a=1)
?上面的兩種調用方式輸出結果相同,都是 1,2。實際上,位置參數的本質是用元組進行傳參,后面會細講。
三、關鍵字參數(Keyword Parameter)
關鍵字參數,也是從字面意思上就可以理解,它很“關鍵”,必須要指明它的名字來進行傳參,就這么簡單。
上面講位置參數的時候也講了一點關鍵字參數的內容,關鍵字參數不關心參數的位置,只要指明了參數名即可。關鍵字參數和位置參數類似,實際是用字典進行傳參,字典的鍵是參數名,字符串形式,值是對應參數的值。
四、可變長位置參數(Variable-length Positional Parameter)
?可變長位置參數,也叫變長位置參數或者不定長位置參數,一般寫作 *args,args 是英文單詞 argument 的復數形式的縮寫,“變長”是指參數的個數是不確定的意思,也就是說,它沒有參數數量的限制。
def function(*args: int) -> tuple[int]:for arg in args:print(arg)return argsfunction(1)
function(1, 2)
function(1, 2, 3)
上面的函數返回一個元組,也就是說,args 實際就是一個元組,這和位置參數是元組傳遞構成了某種聯系……
其實上面的代碼還可以這樣寫:
def function(*args: int) -> tuple[int]:print(*args)return args
有人在想,*args 是什么意思呢?這涉及到 Python 的序列解包知識(是不是又沒聽過這個呀?)具體內容見:Python 星號的妙用 —— 靈活的序列解包
print 函數就是 Python 中最典型的使用了可變長參數的函數,它的函數原型是這樣的(兩個重載):
def print(*values: object,sep: str | None = " ",end: str | None = "\n",file: SupportsWrite[str] | None = None,flush: Literal[False] = False,
) -> None: ...def print(*values: object,sep: str | None = " ",end: str | None = "\n",file: _SupportsWriteAndFlush[str] | None = None,flush: bool
) -> None: ...
?那個 *values 就是可變長參數了。實際上,位置參數就是可變長位置參數序列解包后再進行參數傳遞的,而可變長位置參數是直接整個元組進行傳值的。
那么我們擴展延伸一下,大家是不是總在網上看到說 Python 的函數可以有多個返回值?這是真的嗎?這是錯誤的認知!實際上,Python 的函數返回值仍然只有一個,當寫出多個值的時候,Python 自動給你弄成元組了,下面的代碼可以驗證:
def function():a = 1b = 2return a, bc = 1, 2print(type(c))
print(type(function()))
輸出結果都是 tuple。?
五、可變長關鍵字參數(Variable-length Keyword Parameter)
可變長關鍵字參數呢,可以類比可變長位置參數,將元組換成字典就行,其他的都一樣,這里就不再贅述了。
六、僅位置參數(Position-only Parameter)
好,終于到重點了,看僅位置參數之前先給大家介紹一位兄弟,就是一個斜杠(/),它在 Python 里面不僅僅是除法的含義,還有一個含義,而且有專門的名字,叫 僅位置參數分隔符(Position-only argument separator),不知有多少人認識這位兄弟呀?
在參數列表中就直接寫一個斜杠就行,它強制其前面的參數是位置參數,無法用關鍵字參數進行傳遞!
在 Python 的內置函數 isinstance 中就有出現:
def isinstance(__obj: object,__class_or_tuple: _ClassInfo,/ # 這里!這里!這兒出現了僅位置參數分隔符!
) -> bool
在 int 中也有:
class int(__x: str | ReadableBuffer | SupportsInt | SupportsIndex | SupportsTrunc = ...,/ # 這里!這里!這兒有僅位置參數分隔符!
)
我對這個東西給的理解是(非官方理解),這個東西的作用一般是為了防止一些一般人看不懂代碼,有些東西是約定俗成的,不需要刻意地寫出來,比如參數名,畢竟你見過誰用 int 的時候像下面這樣寫了???
int(__x=1)
上面是錯誤的代碼!盡管這個參數名確實是 __x,但是僅位置參數分隔符強制了它為位置參數,不允許將 __x 寫出來(寫出來就是關鍵字參數了)!?且寫出來只會讓人誤解且讀起來費勁!
七、僅關鍵字參數(Keyword-only Parameter)
同樣的,看僅關鍵字參數之前先給大家介紹一位兄弟,就是一個星號(*),它在 Python 里面不僅僅是乘法的含義,以及序列解包的含義,還有一個含義,而且也有專門的名字,叫 僅關鍵字參數分隔符(Keyword-only argument separator),不知又有多少人認識這位兄弟呀?
在參數列表中就直接寫一個星號就行,它強制其后面的參數是關鍵字參數,無法用位置參數的方式進行參數傳遞!
我對這個理解(非官方理解)是,它非常方便于修改模塊和項目,對不同版本的兼容性很好,不像位置參數,一旦中間少了或者多了一個參數,后面的參數全部錯位,導致出現不可預計的問題。而這個僅關鍵字參數分隔符寫了之后可以強制別人用你的函數時必須按照關鍵字參數的方式進行傳遞,防止出現參數錯位的情況。這個一般在參數比較多的情況下會使用,比如 Python 內置模塊 tkinter 某些控件類初始化的參數,多到離譜:
def __init__(self: Canvas,master: Misc | None = None,cnf: dict[str, Any] | None = {},*, # 這里!這里!這兒有個僅關鍵字參數分隔符!background: str = ...,bd: _ScreenUnits = ...,bg: str = ...,border: _ScreenUnits = ...,borderwidth: _ScreenUnits = ...,closeenough: float = ...,confine: bool = ...,cursor: _Cursor = ...,height: _ScreenUnits = ...,highlightbackground: str = ...,highlightcolor: str = ...,highlightthickness: _ScreenUnits = ...,insertbackground: str = ...,insertborderwidth: _ScreenUnits = ...,insertofftime: int = ...,insertontime: int = ...,insertwidth: _ScreenUnits = ...,name: str = ...,offset: ... = ...,relief: _Relief = ...,scrollregion: tuple[_ScreenUnits, _ScreenUnits, _ScreenUnits, _ScreenUnits] | tuple[()] = ...,selectbackground: str = ...,selectborderwidth: _ScreenUnits = ...,selectforeground: str = ...,state: Literal['normal', 'disabled'] = ...,takefocus: _TakeFocusValue = ...,width: _ScreenUnits = ...,xscrollcommand: _XYScrollCommand = ...,xscrollincrement: _ScreenUnits = ...,yscrollcommand: _XYScrollCommand = ...,yscrollincrement: _ScreenUnits = ...
) -> None
講完了,現在有個問題,看完這些知識的你,回過頭再看開篇的那段代碼,你能說出哪些參數類型互相之間是沖突和矛盾,導致無法并存的嗎?評論區里留下你的答案(我不會寫出答案的【doge】)?
我本人平時就在自己寫一些模塊的代碼,所以呢,對這些比較了解,它們都是 Python 的編程利器!建議大家也掌握這些知識,雖然可能沒什么用,但是技多不壓身啊!
看到這里,不知您是否漲知識了呢?喜歡的話不妨?點贊、收藏?加?轉發??如果可以 關注 的話,那更好了!!!
該文章已被收入到專欄中,專欄在文章頂部可以看到,里面有更多讓你匪夷所思的 Python 知識!