首頁 >後端開發 >Python教學 >Python程式設計:定義函數輸入參數很簡單?這些規則掌握了沒?

Python程式設計:定義函數輸入參數很簡單?這些規則掌握了沒?

WBOY
WBOY轉載
2023-05-04 08:46:061066瀏覽

本文內容主要涉及以下幾個主題方法,文章有點長,請收藏以便查閱。

函數參數即為函數的輸入,可分類為五組。

  • 位置或關鍵字參數:同時允許位置和關鍵字參數;
  • 可變位置參數:在元組中收集任意數量的位置參數;
  • 可變關鍵字參數:在字典中收集任意數量的關鍵字參數;
  • 僅限位置參數:只能作為位置參數傳遞;
  • 僅限關鍵字參數:只能作為關鍵字參數傳遞。

到目前為止,我們所看到的例子中,看到的所有參數都是普通的位置參數或關鍵字參數。也已經了解如何將它們作為位置參數和關鍵字參數傳遞。關於它們沒有太多要說的了,所以我們要來看看其他類別。在此之前,先來看看可選參數。

1.可選參數

除了我們在這裡看到的類別之外,參數還可以分為必選項和可選項。可選參數有預設值,其值在函數定義中指定。語法是格式為:name=value。範例如下:

# 定义参数有默认值的函数,调用时其为可选型参数
def func(a, b=4, c=88):
print(a, b, c)

func(1) # prints: 1 4 88
func(b=5, a=7, c=9) # prints: 7 5 9
func(42, c=9) # prints: 42 4 9
func(42, 43, 44) # prints: 42, 43, 44

這裡,a是必要傳遞參數項,而b的預設值是4,c的預設值是88,兩者是可選項。重要的是要注意,除了只有關鍵字的形參外,必需型形參必須始終位於函數定義中所有可選形參的左側。試著在上面的例子中刪除c的預設值,看看會發生什麼。

2.不定量位置參數

有時,可能不希望為函數指定位置參數的確切數量,而Python透過使用可變位置參數提供了實現這一點的能力。讓我們來看一個非常常見的用例,minimum()函數。

這是一個計算其輸入值最小值的函數,程式碼如下:

# 不定量位置参数
def minimum(*n):
# print(type(n)) # n 是个元组
if n: #
mn = n[0]
for value in n[1:]:
if value < mn:
mn = value
print(mn)

minimum(1, 3, -7, 9) # n = (1, 3, -7, 9) - prints: -7
minimum() # n = () - prints: nothing

如上所見,當我們定義一個帶有星號*的形參時,我們是在告訴Python,當函數被呼叫時,這個形參將收集數量不定的位置實參。在函數中,n是一個元組。可取消程式碼中的註解print(type(n))行,然後執行程式並看看輸出。

注意,一個函數最多只能有一個不定量位置參數-有更多的位置參數是沒有意義的。 Python將無法決定如何劃分它們之間的參數。您也無法為變數位置參數指定預設值。預設值總是一個空元組。

提示:

是否注意到程式碼中是如何用一個簡單的if n:檢查n是否為空的?這是因為在Python中,集合物件在非空時求值為True,否則為False。元組、集合、列表、字典等等都是如此。

另一件要注意的事情是,當呼叫不帶參數的函數時,可能想拋出一個錯誤,而不是靜默地什麼都不做。在這種情況下,我們不關心如何使這個函數健壯,而是要理解可變數位置參數。

另外,是否注意到,定義不定量位置形參的語法與可迭代解包的語法非常相似?這並非巧合。畢竟,這兩個特徵互為鏡像。它們也經常一起使用,因為不定量位置形參讓你不必擔心解包的可迭代物件的長度是否與函數定義中的形參數量相符。

3.不定量關鍵字參數

不定量關鍵字參數(Variable keyword parameters)非常類似於不定量位置參數。唯一的區別是語法(**而不是*)和它們以字典形式被收集的事實:

# 不定量关键字参数
def func(**kwargs):
print(kwargs)

func(a=1, b=42) # prints {'a': 1, 'b': 42}
func() # prints {}
func(a=1, b=46, c=99) # prints {'a': 1, 'b': 46, 'c': 99}

如上所示,在函數定義的參數名稱前面加上**告訴Python使用該名稱來收集數量不定的關鍵字參數。與不定量位置參數的情況一樣,每個函數最多只能有一個可變關鍵字參數,且不能指定預設值。

就像可變數位置參數類似可迭代解包一樣,可變關鍵字參數類似字典解包。字典解包也常用於將參數傳遞給具有可變數關鍵字形參的函數。

為什麼能夠傳遞可變數量的關鍵字參數如此重要,目前可能還不清楚,那麼透過如何使用這一能力的範例,你將能更真實的理解其重要性。

我們定義一個連接到資料庫的函數:我們希望透過不帶參數地呼叫這個函數來連接到預設資料庫。也希望透過向函數傳遞適當的參數來連接到任何其他資料庫。在你繼續讀下去之前,自己試著花幾分鐘自己想出一個解決方案:

# 可变量关键字参数
def connect(**options):
conn_params = {
'host': options.get('host', '127.0.0.1'),
'port': options.get('port', 5432),
'user': options.get('user', ''),
'pwd': options.get('pwd', ''),
}
print(conn_params)

# 然后连接数据库(注释掉的代码行)
# db.connect(**conn_params)
connect()
connect(host='127.0.0.42', port=5433)
connect(port=5431, user='admin', pwd='super')

#注意,在函數中,我們可以準備一個連接參數字典(conn_params)使用預設值作為回退,其允許在函數呼叫時提供以覆蓋它們。有更好的方法可以用更少的程式碼行來實現這一點,但我們現在不關心這一點。執行上述程式碼會得到以下結果:

{'a': 1, 'b': 46, 'c': 99}
{'host': '127.0.0.1', 'port': 5432, 'user': '', 'pwd': ''}
{'host': '127.0.0.42', 'port': 5433, 'user': '', 'pwd': ''}
{'host': '127.0.0.1', 'port': 5431, 'user': 'admin', 'pwd': 'super'}

注意函數呼叫和輸出之間的對應關係,以及如何根據傳遞給函數的內容重寫預設值。

4.仅限位置参数

从Python 3.8开始,PEP 570 (https://www.python.org/dev/peps/pep-0570/)引入了仅限位置的参数。有一种新的函数参数语法,/,表示一组函数形参必须在位置上指定,不能作为关键字参数传递。让我们看一个简单的例子:

# 仅限位置参数
def func(a, b, /, c):
print(a, b, c)

func(1, 2, 3) # prints: 1 2 3
func(1, 2, c=3) # prints 1 2 3

在上面的例子中,我们定义了一个函数func(),它指定了三个参数:a、b和c。函数签名中的/表示a和b必须按位置传递,也就是说,不能通过关键字传递。

示例中的最后两行显示,我们可以按位置传递所有三个参数来调用函数,或者可以按关键字传递c。这两种情况都可以正常工作,因为c定义在函数签名中的/之后。如果我们试图通过通过关键字传递a或b来调用函数,像这样:

func(1, b=2, c=3)

这将产生如下类似回溯跟踪信息:

Traceback (most recent call last):
File "……", line 9, in <module>
func(1, b=2, c=3)
TypeError: func() got some positional-only arguments passed as keyword arguments: 'b'

前面的例子告诉我们,Python现在反馈给我们调用func()的方式,其意思是:通过关键字传递了参数b,但不允许这样做。

仅限位置参数也可以是可选的,如下所示:

# 可选的仅限位置参数
def func(a, b=2, /):
print(a, b)

func(4, 5) # prints 4 5
func(3) # prints 3 2

通过一些从官方文档中借来的例子来看看这个特性给该语言带来了什么。一个优点是能够完全模拟现有C编码函数的行为:

def divmod(a, b, /):
"模拟内建函数 divmod()"
return (a // b, a % b)

另一个重要的用例是在形参名没有啥有意义的帮助的情况下排除关键字实参:

len(obj='hello')

在上面的例子中,obj关键字参数降低了可读性。此外,如果我们希望重构len函数的内部结构,并将obj重命名为the_object(或任何其他名称),更改保证不会破坏任何客户端代码,因为不会有任何对len()函数的调用,会涉及到现在已经过时的obj参数名称。

最后,使用仅限位置形参意味着/左边的任何值都可以在不定量关键字实参中使用,如下例所示:

def func_name(name, /, **kwargs):
print(name)
print(kwargs)

func_name('Positional-only name', name='Name in **kwargs')
# 打印输出为:
# Positional-only name
# {'name': 'Name in **kwargs'}

在函数签名中保留参数名以便在**kwargs中使用的能力可以生成更简单、更清晰的代码。

现在来研究一下仅限位置类似版:仅限关键字参数。

5.仅限关键字参数

Python 3引入了仅限关键字的参数。我们只简要地研究它们,因为它们的用例并不常见。有两种方法可以指定它们,要么在不定量位置参数之后,要么在不定的*之后。来看两个例子。代码如下:

# 仅限关键字参数
def kwo(*a, c):
print(a, c)

kwo(1, 2, 3, c=7) # prints: (1, 2, 3) 7
kwo(c=4) # prints: () 4
# kwo(1, 2) # 此行出问题——无效于法,并有如下错误
# TypeError: kwo() missing 1 required keyword-only argument: 'c'

def kwo2(a, b=42, *, c):
print(a, b, c)

kwo2(3, b=7, c=99) # prints: 3 7 99
kwo2(3, c=13) # prints: 3 42 13
# kwo2(3, 23) # 此行出问题——无效于法,并有如下错误
# TypeError: kwo2() missing 1 required keyword-only argument: 'c'

正如预期的那样,函数kwo()接受数量可变的位置参数(a)和一个只有关键字的关键字c。调用的结果很简单,你可以取消对第三个调用的注释,以查看Python返回什么错误。

同样的情况也适用于函数kwo2(),它与kwo的不同之处在于,它接受一个位置参数a、一个关键字参数b和一个只有关键字的参数c。你可以取消对第三个调用的注释,以查看产生的错误。

现在应已知道了如何指定不同类型的输入参数,接下来看看如何在函数定义中组合它们。

6.组合输入参数

可以在同一个函数中组合不同的参数类型(事实上,这样做通常非常有用)。就像在同一个函数调用中组合不同类型的实参一样,在顺序上有一些限制:

  • 仅限位置的参数放在前面,然后跟随一个斜杠“/”。
  • 普通参数在任何仅限位置参数之后。
  • 不定量位置参数在正常参数之后。
  • 只有关键字参数在不定量位置参数之后。
  • 不定量关键字参数总是排在最后。

对于仅限位置参数和普通参数,任何必需的参数必须在任何可选参数之前定义。这意味着,如果你有一个可选的仅限位置参数,那么所有常规参数也必须是可选的。该规则不影响仅限关键字的参数。

如果没有例子,这些规则可能会有点难以理解,所以来看几个示例:

# 定义个带有所有参数形式的函数
def func(a, b, c=7, *args, **kwargs):
print('a, b, c:', a, b, c)
print('args:', args)
print('kwargs:', kwargs)

func(1, 2, 3, 5, 7, 9, A='a', B='b')

注意函数定义中参数的顺序。执行该程序会得到以下结果:

a, b, c: 1 2 3
args: (5, 7, 9)
kwargs: {'A': 'a', 'B': 'b'}

现在再来看一个只有关键字参数的例子:

# 仅限观自在参数
def allparams(a, /, b, c=42, *args, d=256, e, **kwargs):
print('a, b, c:', a, b, c)
print('d, e:', d, e)
print('args:', args)
print('kwargs:', kwargs)

allparams(1, 2, 3, 4, 5, 6, e=7, f=9, g=10)

注意,在函数声明中有仅限位置形参和仅限关键字形参:a仅限位置形参,而d和e仅限关键字形参。他们是在*args可变量位置参数之后,如果它们紧跟在单个*的后面,也会是一样的(在这种情况下,将没有任何可变位置参数)。运行程序会得到以下结果:

a, b, c: 1 2 3
d, e: 256 7
args: (4, 5, 6)
kwargs: {'f': 9, 'g': 10}

另一件需要注意的事情是我们为可变量位置参数和关键字参数命名。你可以自由选择不同的名称,但请注意,args和kwargs是这些参数的常规名称,至少在一般情况下是这样。

7.更多的签名示例

为了简要回顾一下使用仅限位置和关键字说明符的函数签名,下面是一些进一步的示例。省略不定量位置和关键字参数,为简洁起见,我们只剩下以下语法:

def xxxFuncName(positional_only_parameters, /,
 positional_or_keyword_parameters, *,
 keyword_only_parameters):
# 函数体
pass

首先,我们有仅限位置的参数,然后是位置或关键字参数,最后是仅限关键字参数。

其他一些有效签名如下:

def xxxFuncName(p1, p2, /, p_or_kw, *, kw):
def xxxFuncName(p1, p2=None, /, p_or_kw=None, *, kw):
def xxxFuncName(p1, p2=None, /, *, kw):
def xxxFuncName(p1, p2=None, /):
def xxxFuncName(p1, p2, /, p_or_kw):
def xxxFuncName(p1, p2, /):

以上均为有效签名,下列为无效签名:

def xxxFuncName(p1, p2=None, /, p_or_kw, *, kw):
def xxxFuncName(p1=None, p2, /, p_or_kw=None, *, kw):
def xxxFuncName(p1=None, p2, /):

你可以在官方文档中阅读语法规范:

https://docs.python.org/3/reference/compound_stmts.html#functiondefinitions

提示:在这一点上,要很好的理解与掌握,一个有用的练习方法是实现上述示例签名中的任何一个,打印出这些参数的值,就像我们在前面的练习中所做的那样,并以不同的方式传递参数。

8.避免陷阱!可变默认值

要注意的一件事是,在Python中,默认值是在定义时创建的;因此,根据默认值的可变性,对同一函数的后续调用可能会有不同的行为。让我们看一个例子:

# 带有可变默认值参数函数
def func(a=[], b={}):
print(a)
print(b)
print('#' * 12)
a.append(len(a)) # 影响a的默认值
b[len(a)] = len(a) # 影响b的默认值

func()
func()
func()

两个参数都有可变的默认值。这意味着,如果执行中影响了这些对象,任何修改都将停留在后续的函数调用中。看看你是否能理解这些调用的输出:

[]
{}
############
[0]
{1: 1}
############
[0, 1]
{1: 1, 2: 2}
############

是不是很搞事?虽然这种行为一开始看起来很奇怪,但它实际上是有意义的,而且非常方便——例如,当使用“记忆”技术时,就有了天生之才的傲娇。更有趣的是,在调用之间,我们引入了一个不使用默认值的函数,比如:

# 中间调停者调用
func()
func(a=[1, 2, 3], b={'B': 1})
func()

运行代码输出内容如下所示:

[]
{}
############
[1, 2, 3]
{'B': 1}
############
[0]
{1: 1}
############

这个输出告诉我们,即使使用其他值调用函数,默认值也会被保留。我想到的一个问题是,如何每次都获得一个新的空值?惯例是这样的:

# 无陷阱可变缺省默认值
def func(a=None):
if a is None:
a = []
# 干些使用a的工作 ...

注意,通过使用前面的技术,如果调用函数时没有传递a,我们总是得到一个全新的空列表。

本文小结

本文详细介绍了函数的输入参数分类、示例和调用,都是围绕如下主题展开:

位置或关键字参数:同时允许位置和关键字参数;

可变位置参数:在元组中收集任意数量的位置参数;

可变关键字参数:在字典中收集任意数量的关键字参数;

仅限位置参数:只能作为位置参数传递;

•仅限关键字参数:只能作为关键字参数传递。​

以上是Python程式設計:定義函數輸入參數很簡單?這些規則掌握了沒?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:51cto.com。如有侵權,請聯絡admin@php.cn刪除