Python 函数定义详解(More on Defining Functions)- 默认参数/位置参数/关键字参数

在这里插入图片描述

1.函数的定义和调用方法

1.1函数定义方法

"""
    def 关键字用来定义一个函数。
    function_name 是函数名,应遵循命名规范。
    parameter1, parameter2, ... 是函数的参数列表,可以是任意数量和类型的参数。
    函数体是用缩进(通常为4个空格)来表示的代码块。
    return 语句用于返回函数的结果。
"""
# def function_name(parameter1, parameter2, ...):
    # Function body
    #    return result

1.2函数调用方法

"""
function_name 是要调用的函数名。
argument1, argument2, ... 是传递给函数的参数列表。
函数执行后,将返回一个结果,可以将其赋值给一个变量。
"""
# result = function_name(argument1, argument2, ...)

1.3示例:定义一个函数计算两个数之和

"""
    定义个函数计算两个数之和
"""
def add_numbers(num1,num2):# 定义函数
    result = num1 +num2
    return result

sum = add_numbers(100,200)# 调用函数

print(sum) # 输出结果为300

2.默认值参数(Default Argument Values)

  Python允许在函数定义中为参数提供默认值,这使得函数在调用时可以省略部分参数,从而提供了更大的灵活性。

2.1示例1:简单的示例函数,用于打招呼

def greet(name, greeting="Hello"):
    # 参数name是位置参数,必须提供值。
    # 参数greeting是默认值参数,如果不提供值,默认为"Hello"。
    return f"{greeting}, {name}!"

# 调用函数时可以只提供name参数,greeting参数会使用默认值。
result1 = greet("Alice")
print(result1)  # 输出 "Hello, Alice!"

# 也可以同时提供name和greeting参数的值,这将覆盖默认值。
result2 = greet("Bob", "Hi")
print(result2)  # 输出 "Hi, Bob!"

  greet函数接受两个参数,namegreeting,其中greeting是一个带有默认值的参数,默认值为"Hello"。当调用函数时,如果只提供了name参数,那么greeting参数将使用默认值。如果同时提供了name和greeting的值,那么提供的值将覆盖默认值。

2.2python官网示例

def ask_ok(prompt, retries=4, reminder='Please try again!'):
    # 定义一个函数ask_ok,接受三个参数:prompt、retries和reminder。
    # 默认情况下,retries被设置为4,reminder被设置为'Please try again!'。
    
    while True:
        # 进入一个无限循环,直到用户提供有效的回答或达到重试次数。
        
        ok = input(prompt)
        # 使用input()函数向用户提供一个提示,等待用户的输入,并将输入保存在ok变量中。
        
        if ok in ('y', 'ye', 'yes'):
            return True
            # 如果用户输入了'y', 'ye', 'yes'中的一个,函数返回True。
            
        if ok in ('n', 'no', 'nop', 'nope'):
            return False
            # 如果用户输入了'n', 'no', 'nop', 'nope'中的一个,函数返回False。
            
        retries = retries - 1
        # 如果用户输入既不是True也不是False,减少重试次数retries。
        
        if retries < 0:
            raise ValueError('invalid user response')
            # 如果重试次数小于0,引发一个ValueError异常,表示用户提供的回答无效。
            
        print(reminder)
        # 如果用户提供的回答不在有效选项内,打印提醒消息reminder,然后继续循环。

# 通过以下方式调用:
ask_ok('Do you really want to quit?')#只给出必选实参
ask_ok('OK to overwrite the file?', 2)#给出一个可选实参
ask_ok('OK to overwrite the file?', 2, 'Come on, only yes or no!')#给出所有实参

2.3重要提示

  默认值只计算一次。默认值为列表、字典或类实例等可变对象时,会产生与该规则不同的结果。
  下面以列表、字典或类实例示例说明:

"""
    列表作为默认参数
    在这个例子中,期望每次调用append_to函数时,列表都是新的,互不干扰。
    然而,由于默认参数的特性,得到的列表是共享的,所以每次调用都会影响这个列表。
"""
def append_to(num, target=[]):  
    target.append(num)  
    return target  
  
print(append_to(1))  # 输出:[1]  
print(append_to(2))  # 输出:[1, 2],而不是[1]  
print(append_to(3))  # 输出:[1, 2, 3],而不是[1, 2]
"""
    字典作为默认参数
    期望每次调用update_dict函数时,字典都是新的,但是因为默认参数的特性,得到的字典是共享的。
"""
def update_dict(key, target={}):  
    target[key] = key  
    return target  
  
print(update_dict(1))  # 输出:{1: 1}  
print(update_dict(2))  # 输出:{1: 1, 2: 2},而不是{1: 1}  
print(update_dict(3))  # 输出:{1: 1, 2: 2, 3: 3},而不是{1: 1, 2: 2}
"""
    类实例作为默认参数
    期望每次调用increment_by函数时,TestClass的实例都是新的,互不干扰。然而,由于默认参数的特性,得到的实例是共享的,所以每次调用都会影响这个实例的状态
"""
class TestClass:  
    def __init__(self):  
        self.value = 0  
  
    def increment(self, num):  
        self.value += num  
        return self.value  
  
def increment_by(num, target=TestClass()):  
    target.increment(num)  
    return target.value  
  
print(increment_by(1))  # 输出:1  
print(increment_by(2))  # 输出:3,而不是2  
print(increment_by(3))  # 输出:6,而不是5

2.4建议

  1.使用可变对象作为默认参数时,可以考虑使用None作为默认值,并在函数内部进行判断和处理。每次调用的时候,都会创建一个新的列表,避免了共享默认参数的问题。

def append_to(num, target=None):  
    if target is None:  
        target = []  
    target.append(num)  
    return target
    
print(append_to(1))
print(append_to(2))
print(append_to(3))

  2.对于字典或类实例等可变对象,可以考虑使用函数内部的局部变量作为默认值,而不是使用一个全局变量。每次调用的时候都会创建一个新的字典或实例,避免共享默认参数的问题。

def update_dict(key, target=None):  
    if target is None:  
        target = {}  
    target[key] = key  
    return target
def increment_by(num, target=None):  
    if target is None:  
        target = TestClass()  
    target.increment(num)  
    return target.value

  3.如果可能的话,尽量避免使用可变对象作为默认参数,可以避免潜在的共享问题。考虑使用不可变对象(如字符串、数字或元组)作为默认参数。
  4.如果默认参数的值是一个复杂对象(如列表、字典或类实例),并且需要重复使用,可以考虑将该对象定义为全局变量或属性,并在函数内部引用该全局变量。可以确保每次调用函数时都使用同一个对象,避免了重复创建对象的开销。但是需要注意避免全局变量导致的命名冲突和其他问题。
  5.如果默认参数的值是一个不可变对象(如字符串、数字或元组),并且需要重复使用,可以考虑将该对象定义为函数的一个属性或静态变量。这样可以避免每次调用函数时都重新计算该对象的开销。但是需要注意避免该对象被修改导致的问题。

3.关键字参数(Keyword Arguments

  kwarg=value形式的关键字参数 也可以用于调用函数。
  在函数调用中前面带有标识符(例如 arg=)或者作为包含在前面带有**的字典里的值传入。
  如:3和5在以下对complex()方法的调用中均属于关键字参数
  complex(real=3, imag=5)
  complex(**{'real': 3, 'imag': 5})

3.1官网示例

"""
    parrot()函数接受一个必选参数(voltage)和三个可选参数(state, action 和 type)
    其中voltage既可以是位置参数,也可以是关键字参数;
    state、action、type是关键字参数
"""
def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
    print("-- This parrot wouldn't", action, end=' ')
    print("if you put", voltage, "volts through it.")
    print("-- Lovely plumage, the", type)
    print("-- It's", state, "!")

# 可用下列方法进行调用
parrot(1000)                                          # 1 positional argument
parrot(voltage=1000)                                  # 1 keyword argument
parrot(voltage=1000000, action='VOOOOOM')             # 2 keyword arguments
parrot(action='VOOOOOM', voltage=1000000)             # 2 keyword arguments
parrot('a million', 'bereft of life', 'jump')         # 3 positional arguments
parrot('a thousand', state='pushing up the daisies')  # 1 positional, 1 keyword

# 以下调用函数的方式均无效
parrot()                     # required argument missing
parrot(voltage=5.0, 'dead')  # non-keyword argument after a keyword argument
parrot(110, voltage=220)     # duplicate value for the same argument
parrot(actor='John Cleese')  # unknown keyword argument

  函数调用时,关键字参数必须跟在位置参数后面。所有传递的关键字参数都必须匹配一个函数接受的参数(比如,actor不是函数 parrot 的有效参数),关键字参数的顺序并不重要。这也包括必选参数,(比如,parrot(voltage=1000) 也有效)。不能对同一个参数多次赋值。

3.2 ```*args和**kwargs

  在Python中,*args **kwargs 是特殊的参数,用于在函数定义中接收任意数量的位置参数和关键字参数。
  *args表示一个元组类型的参数,用于接收任意数量的位置参数。这些参数将按照它们在函数调用中的顺序依次赋值给*args参数。在函数体中,可以像处理普通元组一样处理*args参数。
  **kwargs表示一个字典类型的参数,用于接收任意数量的关键字参数。这些参数将作为字典的键值对赋值给 **kwargs参数。在函数体中,可以使用**kwargs参数来访问这些关键字参数。

"""
    *argument 形参接收一个元组,在它之前的为位置参数
    **keywords 形参接收一个字典,传参数数时必须带变量名
"""
def cheeseshop(kind, *arguments, **keywords):
    print("-- Do you have any", kind, "?")
    print("-- I'm sorry, we're all out of", kind)
    for arg in arguments:
        print(arg)
    print("-" * 40)
    for kw in keywords:
        print(kw, ":", keywords[kw])

#调用该函数
cheeseshop("Limburger", "It's very runny, sir.",
           "It's really very, VERY runny, sir.",
           shopkeeper="Michael Palin",
           client="John Cleese",
           sketch="Cheese Shop Sketch")

# 输出结果:
"""
    -- Do you have any Limburger ?
    -- I'm sorry, we're all out of Limburger
    It's very runny, sir.
    It's really very, VERY runny, sir.
    ----------------------------------------
    shopkeeper : Michael Palin
    client : John Cleese
    sketch : Cheese Shop Sketch
"""

4.特殊参数

  默认情况下,参数可以按位置或显式关键字传递给Python 函数。为了让代码易读、高效,最好限制参数的传递方式,这样,开发者只需查看函数定义,即可确定参数项是仅按位置、按位置或关键字,还是仅按关键字传递。
函数定义如下:
在这里插入图片描述

  / * 是可选的。这些符号表明形参如何把参数值传递给函数:位置位置或关键字关键字

4.1 位置或关键字参数

  函数定义中未使用/ * 时,参数可以按位置或关键字传递给函数。

4.2 仅位置参数

  仅限位置时,形参的顺序很重要,且这些形参不能用关键字传递。仅限位置形参应放在/ (正斜杠)前。/ 用于在逻辑上分割仅限位置形参与其它形参。如果函数定义中没有 /,则表示没有仅限位置形参。
/ 后可以是 位置或关键字 或 仅限关键字 形参。

4.3 仅限关键字参数

  把形参标记为 仅限关键字,表明必须以关键字参数形式传递该形参,应在参数列表中第一个 仅限关键字形参前添加*

"""
    / 和 * 在参数中使用的语法:
    def function(positional_or_keyword_parameters, *, keyword_only_parameters):
    def function(positional_only_parameters, /, positional_or_keyword_parameters,*, keyword_only_parameters):
    
    在“/”左边的参数被视为仅位置参数
    如果函数定义中“/”没有指定,则函数中所有参数都不是仅位置参数
    仅位置参数的可选值的逻辑与位置-关键字参数的逻辑相同。
    一旦使用默认值指定了仅位置参数,下面的仅位置参数和位置-关键字参数也需要具有默认值。
"""
# 有效的函数定义:
def fun1(positional1, positional2, /, positional_or_keyword, *, keywords):pass
def fun2(positional1, positional2=None, /, positional_or_keyword=None, *, keywords):pass
def fun3(positional1, positional2=None, /, *, keywords):pass
def fun4(positional1, positional2=None, /):pass
def fun5(positional1, positional2, /, positional_or_keyword):pass
def fun6(positional1, positional2, /):pass
def fun7(positional_or_keyword, *, keywords):pass
def fun8(*, keywords):pass

# 无效定义
def fun9(positional1, positional2=None, /, positional_or_keyword, *, keywords):pass
def fun10(positional1=None, positional2, /, positional_or_keyword=None, *, keywords):pass
def fun11(positional1=None, positional2, /):pass
  • 24
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

snail哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值