Python自定义函数形式参中的*args、**kwargs、*和/

发布于:2025-08-31 ⋅ 阅读:(21) ⋅ 点赞:(0)

一、函数定义

1. 定义函数的语法

Python自定义函数的语法结构如下:

def 函数名(形式参数):
    函数体
    return 返回值

其中:def为自定义函数的关键词;函数名为自定义函数的名称,需遵循标识符定义规则和约定;形式参数(非必须),是函数接受外部信息的“接口”。return为返回结果的关键字,返回值(非必须),Python函数可以没有return语句返回结果,也可以只有return空语句。

说明:形式参数,简称形参,是函数接受外部数据的“接口”。形参不是必须的,当函数无需外部输入就可以得出结果时,可以没有形参,也称无参函数;当函数需要接受外部输入的数据(如计算、转换等)时,则必须用形参接受参数,也称有参函数。

从Python 3.5版起增加了标准模块在typing,用于支持类型提示(Type Hints)。它提供了:

  • 用于类型注释的工具
  • 泛型类型支持
  • 类型别名
  • 回调协议
  • 以及其他高级类型系统特性

2. 类型注释

Python 3.5版起导入模块typing模块(Python 3.8版起无需导入typing模块),可以使用“: 数据类型”进行类型注释,有“->数据类型”进行函数类型注释。如:

# 变量类型注释
name: str = "张三"
age: int = 120
is_student: bool = True

# 函数参数和返回值类型注释
def login(name: str, pword: int) -> str:
    if pword == 12345678:
        return f"欢迎{name}进入系统"
    else:
        return f" {name},您的密码不正确,不能进入系统"

# 以下用例Python 3.8版后仍需导入typing模块。from typing import List, Dict, Set, Tuple, Optional
# 容器类型
numbers: List[int] = [1, 2, 3]
person: Dict[str, str] = {"name": "张三", "email": "zhangsan@example.biz"}
unique_numbers: Set[int] = {1, 3, 5}
coordinates: Tuple[float, float, float] = (70.5, 95.5, 80.0)

# 可选类型
maybe_name: Optional[str] = None  # 等同于 Union[str, None]

例如:定义如图1所示的login()函数之后,当输入“login(”时,系统会给出形参及类型和函数类型提示(如图1所示)。

类型提示(Type Hints)

执行有类型提示(Type Hints)的函数与执行普通函数相同(如图2所示)。

2 执行有类型提示(Type Hints)的函数

注意:类型提示是可选的,它们不会强制类型检查,但它们可以被工具和IDLE(集成开发学习环境)用来提供更好的代码自动完成、错误检测和文档生成。

二、函数的参数(传参方式)

1. 形参与实参

形式参数(简称形参):函数定义时放在括号中的参数(变量);实际参数(简称实参):函数调用时传入的参数。

2. 默认参数

(1)默认参数的定义使用

函数定义时可以给参数设置默认值,这意味着在调用函数时,如果没有为这些参数传递值,它们将自动使用默认值。这对于编写灵活和可复用的代码非常有用。

例如:在定义函数的形参时用赋值号(=)给参数赋值,定义的就是默认参数。如图3所示的函数greet(),参数greeting默认为“你好”,如调用时不传入greeting,则默认为“你好”,如图3中的greet("张三"),调用时如传入greeting的值,将用传入的值,如图3中的greet("张三", "早上好")和greet("张三", "晚上好"),则greeting为传入的值。

3 函数的默认参数

(2)默认参数的注意事项

  • 默认参数必须是不可变对象:这是因为在函数定义时,默认参数只会被评估一次。如果你使用可变对象(如列表、字典等)作为默认参数,那么所有对该函数的后续调用都将引用相同的对象。这可能会导致意外的副作用。
  • 默认参数应该放在参数列表的末尾:必须将带有默认值的参数放在参数列表的末尾,否则将导致语法错误,无法使用默认参数。例如:图4所示的函数def temp(a, b=10, c)会导致语法错误。也就是一旦开始使用默认参数,后面所有参数都得使用默认参数。

4 函数使用默认参数注意事项

  • 使用默认参数的好处

简化函数调用:用户可以只提供必要的参数,而省略其他具有默认值的参数。

灵活性:允许函数提供更多的配置选项,而不需要创建多个重载的函数。

清晰性:通过明确的参数名和默认值,代码更易于理解和维护。

通过合理使用默认参数,可以编写出既灵活又易于使用的Python函数。

3. 传参方式

Python函数的传参方式可以分为位置参数(Positional Arguments)和关键字参数(Keyword Arguments)两种。理解这两种参数的传递方式对于编写清晰、易于维护的代码非常重要。

(1)位置参数

位置参数是根据函数定义时形式参数的顺序和个数来传递参数。这意味着,当你调用一个函数时,你必须按照函数定义时参数的顺序和个数来传递这些参数。例如:

def greet(name, message):
    print(f"你好,{name}:{message}")

# 使用位置参数调用
greet("张三", "多年不见,你身体可好?")

在这个例子中,name 和 message用位置参数传递,它们的值必须按照定义的顺序和个数来传递。

注意:当有默认参数时,默认参数可缺省,默认参数也按顺序赋值或缺省。此时参数传递个数可以少于定义个数。

(2)关键字参数

关键字参数允许在调用函数时通过“参数名=值”的方式来传递参数,这时就不需要按照函数定义时的顺序进行传递,但参数名必须正确。例如:

如图5所示函数,用关键字参数传递,必须使用正确的参数名(关键字),与次序无关。如传递关键字(形参)不存在,会引发TypeError,函数得到一个意外的关键字参数。

5 关键字参数传递

(3)混用位置参数和关键字参数

在Python中,函数的参数,既可以用位置参数传递,也可以用关键字参数传递,还可以混合使用位置参数和关键字参数传递。但必须位置参数传递在前、关键字参数传递在后。也就是一旦开始使用关键字参数传递,所有后续的参数都必须使用关键字参数形式传递。例如:

def complex_function(a, b, c, d=10, e=20):
    print(f"普通参数a: {a},b: {b},c: {c};默认参数d: {d},e: {e}")

# 调用函数,使用位置参数传递
complex_function(1, 2, 3, 4, 5) # 全部参数。结果:普通参数a: 1,b: 2,c: 3;默认参数d: 4,e: 5
complex_function(1, 2, 3, 4)   # 缺省默认参数e。结果:普通参数a: 1,b: 2,c: 3;默认参数d: 4,e: 20
complex_function(1, 2, 3)     # 缺省默认参数d、e。结果:普通参数a: 1,b: 2,c: 3;默认参数d: 10,e: 20
# 调用函数,使用关键字参数传递
complex_function(e=5, d=4, c=3, b=2, a=1) # 全部参数。结果:普通参数a: 1,b: 2,c: 3;默认参数d: 4,e: 5
complex_function(e=5, c=3, b=2, a=1) # 缺省默认参数d。结果:普通参数a: 1,b: 2,c: 3;默认参数d: 10,e: 5
complex_function(c=3, b=2, a=1) # 缺省默认参数d、e。结果:普通参数a: 1,b: 2,c: 3;默认参数d: 10,e: 20
# 调用函数,混合使用位置参数和关键字参数传递
complex_function(1, 2, c=3, e=5, d=4) # 全部参数。结果:普通参数a: 1,b: 2,c: 3;默认参数d: 4,e: 5
complex_function(1, 2, c=3, e=5) # 缺省默认参数d。结果:普通参数a: 1,b: 2,c: 3;默认参数d: 10,e: 5
complex_function(1, 2, c=3) # 缺省默认参数d、e。结果:普通参数a: 1,b: 2,c: 3;默认参数d: 10,e: 20

注意事项:(1)在调用函数时,如果使用了关键字参数传递,那么必须在所有位置参数之后使用关键字参数。(2)位置参数缺省(有默认参数)只能从右到左逐个进行,直到全部缺省;关键字参数可以缺省任意默认参数。

Python中的输入和输出函数有哪些呢

如何在Python中传递变量?

(4)不定长参数

不定长参数指参数个数不确定的参数,也称动态参数。

  • 不定长位置参数:在形参前加“*”号,以元组方式接受任何未被接受的位置参数,参数个数为0~n,如没有位置参数则为空元组。一般用*args表示,args‌:arguments的缩写,意为参数,表示位置参数。在形参前加“**”号,以字典方式接受任何未被接受的关键字参数,关键字以字符串方式作为字典的“键”,值为字典“键”对应的“值”,参数个数为0~n,如没有关键字参数则为空字典。一般用**kwargs表示,kwargs‌:keyword arguments的缩写,意为关键字参数。

①只定义*args

def func(*args):
    print(args)

func(1,2,'char',[3,4])#输出:(1, 2, 'char', [3, 4])

②只定义**kwargs

def func(**kwargs):
    print(kwargs)

func(key1 = 123, key2 = 'char')#输出:{'key1': 123, 'key2': 'char'}。键加引号为字符串

③同时定义*args、**kwargs

def func(*args, **kwargs):
    print(args, kwargs)

func(100,200,key1 = 1, key2 = 'char')#输出:(100, 200) {'key1': 1, 'key2': 'char'}

④定义部分形参和*args、**kwargs

def func(a, b, *args, **kwargs):
    print(a, b, args, kwargs)

对于④a, b既可以用位置参数调用,也可以用关键字参数调用,除a, b以外的位置参数会以元组方式被args接受,除a, b以外的关键字参数会以字典方式被kwargs接受。

func(100, 200, 300, 400, key1=123, key2='char')#输出:100 200 (300, 400) {'key1': 123, 'key2': 'char'}

func(100, b=200, c=300, d=400, e=123, f='char')#输出:100 200 () {'c': 300, 'd': 400, 'e': 123, 'f': 'char'}。无多余位置参数,所以args为空元组

在④中,需要接受a, b后,多余的位置参数才能被args接受,多余的关键字参数才能被kwargs接受。而a, b既可以用位置参数传递,也可以用关键字参数传递。

⑤位置参数与*args混用案例

def normal_tuple_invoke(x, y=0, *args):
    '''
        y=0: x的平均成绩=avg(args);y=1: x的最高成绩=max(args)
        y=2: x的最低成绩=min(args);y=3: x的总分=sum(args)
        其他: x的成绩单: args'''   # 函数功能说明
    result = ''
    if y == 0:
        result = x + '的平均成绩=%.2f' % (sum(args)/len(args))
    elif y == 1:
        result = x + '的最高成绩=' + str(max(args))
    elif y == 2:
        result = x + '的最低成绩=' + str(min(args))
    elif y == 3:
        result = x + '的总成绩=' + str(sum(args))
    else:
        result = x + '的成绩单:' + str(args)
    return result

程序中,默认为求平均成绩,输入的成绩按元组接收,可变长度(即成绩多少不限)。

>>> # 按位置+可变长参数(tuple)调用

>>> normal_tuple_invoke('张三',0,67,78,89,90,76,87,95)

'张三的平均成绩=83.14'

>>> normal_tuple_invoke('张三',3,67,78,89,90,76,87,95)

'张三的总成绩=582'

>>> normal_tuple_invoke('张三',4,67,78,89,90,76,87,95)

'张三的成绩单:(67, 78, 89, 90, 76, 87, 95)'

此例只知分数,不知课程。

⑥位置参数与*kwargs混用案例

def keyword_dict_invoke(x, y=0, **kwarg):
    '''结果为:
       y=0: x的平均成绩=sum(kwarg.values)/len(kwarg.values);
       y=1: x的最高成绩=max(kwarg.values);
       y=2: x的最低成绩=min(kwarg.values);
       y=3: x的总分=sum(kwarg.values)
       其它: x的成绩单:kwarg'''   # 函数功能说明
    result=''
    if y==0:
        result=x + '的平均成绩=%.2f' % (sum(kwarg.values())/len(kwarg.values()))
    elif y==1:
        result=x + '的最高成绩=' + str(max(kwarg.values()))
    elif y==2:
        result=x + '的最低成绩=' + str(min(kwarg.values()))
    elif y==3:
        result=x + '的总成绩=' + str(sum(kwarg.values()))
    else:
        result=x + '的成绩单:' + str(kwarg)
    return result

while True:
    lstC = input('姓名,计算方式,多个"课程名=成绩"对用逗号分隔\n')  # 输入时的提示
    if lstC == '' or lstC.lower() == 'q':       # 退出条件:直接回车或按'Q'
        break
    strExec="keyword_dict_invoke("+lstC+")"     # 生成关键字+可变长参数(dict)调用式
    print(eval(strExec))                        # 执行函数keyword_dict_invoke()

执行结果如下:

姓名,计算方式,多个"课程名=成绩"对用逗号分隔

'张三',0,政治经济学=67,生涯规划=78,Python程序设计=89,金属工艺学=90

张三的平均成绩=81.00

姓名,计算方式,多个"课程名=成绩"对用逗号分隔

'张三',1,政治经济学=67,生涯规划=78,Python程序设计=89,金属工艺学=90

张三的最高成绩=90

姓名,计算方式,多个"课程名=成绩"对用逗号分隔

'张三',2,政治经济学=67,生涯规划=78,Python程序设计=89,金属工艺学=90

张三的最低成绩=67

姓名,计算方式,多个"课程名=成绩"对用逗号分隔

'张三',3,政治经济学=67,生涯规划=78,Python程序设计=89,金属工艺学=90

张三的总成绩=324

姓名,计算方式,多个"课程名=成绩"对用逗号分隔

'张三',4,政治经济学=67,生涯规划=78,Python程序设计=89,金属工艺学=90

张三的成绩单:{'政治经济学': 67, '生涯规划': 78, 'Python程序设计': 89, '金属工艺学': 90}

姓名,计算方式,多个"课程名=成绩"对用逗号分隔

此例既知分数,又知课程。

(5)强制只接受位置参数和只接受关键字参数

Python3.8及之后在函数定义语法中引入了两个特殊参数(Special parameters)/和*,可强制分开函数的位置参数和关键字参数。参数/之前的参数为仅限位置参数(positional-only arguments),只接受位置参数,参数/之后的参数则既可接受位置参数,也可以接受关键字参数;参数*之后的参数为仅限关键字参数(keyword-only arguments),只接受关键字参数,参数/之前的参数则既可接受位置参数,也可以接受关键字参数。

至于/和*之间的参数,它们遵循默认的行为,既可以通过位置传入,也可以通过关键字传入。当然,在函数的定义中,既可以只用一个,也可以都不用,或者两个都用。

强制位置参数

所有/之前的参数都是仅限位置参数(positional-only arguments),这意味着它们只能作为位置参数传递给函数,而不能作为关键字参数传递。

一般,核心参数,或在参数顺序比参数名称更重要的情况下,或者参数的名字本身没什么语义(开发者希望在未来可以随时修改这个参数的名字),例如在处理图像或执行几何计算时,此功能非常有用。通过使用仅限位置参数,仅需确保以正确的顺序传参调用函数,从而提高代码的清晰度和可维护性。

def positional_only(a, b, c, /):  # 已知三边长求三角形面积
    s = (a + b + c) / 2  # 计算半周长
    # 使用海伦公式计算面积
    area = (s * (s - a) * (s - b) * (s - c)) ** 0.5
    return area

对于上面这个函数而言,调用positional_only函数时,参数a、b、c只能是位置参数,执行

positional_only(3,4,5)

6.0

正确。执行

positional_only(3,4,c=5)

Traceback (most recent call last):

  File "<pyshell#82>", line 1, in <module>

    positional_only(3,4,c=5)

TypeError: positional_only() got some positional-only arguments passed as keyword arguments: 'c'抛出错误。

6 仅限位置参数用例

强制关键字参数

所有*之后的参数都是仅限关键字参数(keyword-only arguments),它们只能作为关键字参数传递给函数,不能作为位置参数传递。

当您想要强制对某些参数(例如具有默认值的可选参数)使用关键字参数时,或者当您想要明确区分必需参数和可选参数时,又或者这些参数在未来的位置也随时可能发生变化(在前面加入新的参数之类),此功能非常有用。

def keyword_only(*, x, y, width, height):
    """ 根据左上角坐标、宽和高绘制矩形
    参数:
        x: 左上角x坐标
        y: 左上角y坐标
        width: 矩形宽度
        height: 矩形高度
    """
    tl.penup()    # 抬笔
    tl.goto(x, y) # 移动到左上角起点
    tl.pendown()  # 落笔

    # 绘制矩形(顺时针方向)
    for i in range(2):
        tl.forward(width)  # 向右画宽度
        tl.right(90)       # 右转90度
        tl.forward(height) # 向下画高度
        tl.right(90)       # 右转90度

    tl.done() # 保持绘图窗口打开

对于上面这个函数而言,调用keyword_only函数时,强制参数x,y,width,height只能是关键字参数(keyword-only arguments),这四个参数不能用位置参数传参。

7 仅限关键字参数用例

③/*都出现在函数参数中

以下哪个函数调用是合法的?(   )

def func(a, /, b, *, c, d=0):
    pass

A. func(1, 2, 3, 4)         B. func(a=1, 2, c=3)     C. func(1, 2, 3, d=4)          D. func(1, 2, c=3)

从上例函数定义可知,强制a是仅限位置参数,b(不做限制)是既可位置参数也可关键字参数,强制c、d为仅限关键字参数,且d为默认参数。

解析:答案D。选项A:c、d用位置参数调用仅限凑字参数,错误。选项B:a用关键字参数调用仅限位置参数,错误。选项C:c用位置参数调用仅限关键字参数,错误。选项D:a、b用位置参数调用,c用关键字参数调用,d为默认参数,可以缺省,正确。故选D。

8 同时使用仅限位置参数、仅限关键字参数用例

分析以下函数应该如何调用?

def func(a, b, /, c, d, *, e, f):
    print(a, b, c, d, e, f)

解析:参数a、b为仅限位置参数,必须强制用位置参数调用;参数e、f为仅限关键字参数调用,参数c、d则既可以用位置参数调用,也可以用关键字参数调用。所以:

func(1, 2, 3, 4, e=5, f=6)  # 正确调用

参数a、b用位置参数调用,参数c、d用位置参数调用,参数e、f用关键字参数调用,可以正常调用。

func(1, b=2, c=3, d=4, e=5, f=6)  # 这会引发TypeError,因为b不能作为关键字参数传递

参数b为仅限位置参数,用关键字参数调用,抛出TypeError,不能正确调用。


网站公告

今日签到

点亮在社区的每一天
去签到