"""
def 关键字用来定义一个函数。
function_name 是函数名,应遵循命名规范。
parameter1, parameter2, ... 是函数的参数列表,可以是任意数量和类型的参数。
函数体是用缩进(通常为4个空格)来表示的代码块。
return 语句用于返回函数的结果。
"""
# def function_name(parameter1, parameter2, ...):
# Function body
# return result
"""
function_name 是要调用的函数名。
argument1, argument2, ... 是传递给函数的参数列表。
函数执行后,将返回一个结果,可以将其赋值给一个变量。
"""
# result = function_name(argument1, argument2, ...)
"""
定义个函数计算两个数之和
"""
def add_numbers(num1,num2):# 定义函数
result = num1 +num2
return result
sum = add_numbers(100,200)# 调用函数
print(sum) # 输出结果为300
Python允许在函数定义中为参数提供默认值,这使得函数在调用时可以省略部分参数,从而提供了更大的灵活性。
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
的值,那么提供的值将覆盖默认值。
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!')#给出所有实参
默认值只计算一次。默认值为列表、字典或类实例等可变对象时,会产生与该规则不同的结果。
下面以列表、字典或类实例示例说明:
"""
列表作为默认参数
在这个例子中,期望每次调用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
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.如果默认参数的值是一个不可变对象(如字符串、数字或元组
),并且需要重复使用,可以考虑将该对象定义为函数的一个属性或静态变量。这样可以避免每次调用函数时都重新计算该对象的开销。但是需要注意避免该对象被修改导致的问题。
kwarg=value
形式的关键字参数 也可以用于调用函数。
在函数调用中前面带有标识符(例如 arg=)或者作为包含在前面带有**
的字典里的值传入。
如:3和5在以下对complex()方法
的调用中均属于关键字参数
complex(real=3, imag=5)
complex(**{'real': 3, 'imag': 5})
"""
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)
也有效)。不能对同一个参数多次赋值。
在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
"""
默认情况下,参数可以按位置或显式关键字传递给Python 函数
。为了让代码易读、高效,最好限制参数的传递方式,这样,开发者只需查看函数定义,即可确定参数项是仅按位置、按位置或关键字,还是仅按关键字传递。
函数定义如下:
/
和*
是可选的。这些符号表明形参如何把参数值传递给函数:位置
、位置或关键字
、关键字
。
函数定义中未使用/
和*
时,参数可以按位置或关键字传递给函数。
仅限位置时,形参的顺序很重要,且这些形参不能用关键字传递。仅限位置形参应放在/
(正斜杠)前。/
用于在逻辑上分割仅限位置形参与其它形参。如果函数定义中没有 /
,则表示没有仅限位置形参。
/
后可以是 位置或关键字 或 仅限关键字 形参。
把形参标记为 仅限关键字
,表明必须以关键字参数形式传递该形参,应在参数列表中第一个 仅限关键字
形参前添加*
。
"""
/ 和 * 在参数中使用的语法:
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