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 +num2return resultsum = 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函数接受两个参数,name和greeting,其中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 targetprint(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 形式的关键字参数 也可以用于调用函数。
在函数调用中前面带有标识符(例如 name=)或者作为包含在前面带有 ** 的字典里的值传入。
如: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) 也有效)。不能对同一个参数多次赋值。
#%% md
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 LimburgerIt's very runny, sir.It's really very, VERY runny, sir.----------------------------------------shopkeeper : Michael Palinclient : John Cleesesketch : 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