1、定义函数(def)
定义函数使用关键字def,后跟函数名与括号内的形参列表。函数语句从下一行开始,并且必须缩进。
def 函数名(参数列表):
函数体
函数内的第一条语句是字符串时,该字符串就是文档字符串,也称为 docstring,详见 文档字符串。利用文档字符串可以自动生成在线文档或打印版文档,还可以让开发者在浏览代码时直接查阅文档;Python 开发者最好养成在代码中加入文档字符串的好习惯。
函数在 执行 时使用函数局部变量符号表,所有函数变量赋值都存在局部符号表中;引用变量时,首先,在局部符号表里查找变量,然后,是外层函数局部符号表,再是全局符号表,最后是内置名称符号表。因此,尽管可以引用全局变量和外层函数的变量,但最好不要在函数内直接赋值(除非是 global 语句定义的全局变量,或 nonlocal 语句定义的外层函数变量)。
在调用函数时会将实际参数(实参)引入到被调用函数的局部符号表中;因此,实参是使用 按值调用 来传递的(其中的 值 始终是对象的 引用 而不是对象的值)。 当一个函数调用另外一个函数时,会为该调用创建一个新的局部符号表。
特殊用法
函数定义在当前符号表中把函数名与函数对象关联在一起。解释器把函数名指向的对象作为用户自定义函数。还可以使用其他名称指向同一个函数对象,并访问访该函数:
def fib(n):
a, b = 0, 1
while i in range(n):
a = a + b
b = a
print(' '.join(a))
f = fib
f(100)
#输出结果:0 1 1 2 3 5 8 13 21 34 55 89
python函数return语句的用法:
python中的return语句会返回函数的值,如果return后面没有表达式参数则返回None值。函数执行完后返回的值也为None值(就是说如果没有return语句,返回的就是None)
2.函数定义详解
python的函数在调用时必须接受一个必选参数和(0-n)个可选参数。其中函数第一个参数必须是必选参数(位置参数)
2.1 默认参数
在Python中,函数的默认参数允许我们在定义函数时为参数提供默认值。当调用函数时,如果没有为该参数提供值,则使用默认值。默认参数可以有多个,但必须在非默认参数之后。在调用时可以输入比原来定义时更少的参数。
例:
def greet(name, message="Hello"):
print(message + ", " + name)
greet("Alice") # 输出:Hello, Alice
greet("Bob", "Hi") # 输出:Hi, Bob
在定义函数时,可以使用任何合法的表达式作为默认参数的值,包括其他函数的调用、变量、常量等。
需要注意的是,默认参数的值在函数定义时计算,并且只计算一次。这意味着如果默认参数是一个可变对象(如列表、字典等),并且在函数中被修改,那么这些修改将在后续函数调用中保持不变。
例:
def f(a, L=[]):
L.append(a)
return L
print(f(1))
print(f(2))
print(f(3))
#结果
[1]
[1, 2]
[1, 2, 3]
不想在后续调用之间共享默认值时,可以在函数内部创建一个新的可变对象。这样,每次调用函数时,都会使用新创建的对象作为默认值。
例:
def f(a, L=None):
if L is None:
L = []
L.append(a)
return L
2.2 关键字参数
关键字参数是在调用函数时使用参数名称来指定参数值的一种方式。我们调用函数时提供参数的值不必严格按照原来的定义的顺序,可以通过“参数名=值”的形式以任意顺序提供参数。这使得在函数调用时可以忽略参数的顺序,只需指定参数的名称即可。
注:函数调用时,关键字参数必须跟在位置参数后面。所有传递的关键字参数都必须匹配一个函数接受的参数。关键字参数的顺序并不重要。这也包括必选参数。
例:
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, "!")
#该函数接受一个必选参数(voltage)和三个可选参数(state, action 和 type)。该函数可用下列方式调用:
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
2.2.1命名关键字参数
命名关键字参数是在函数定义时,使用关键字来指定参数值,并且这些参数必须在函数调用时显式地指定。与位置参数和关键字参数不同,命名关键字参数必须按照指定的参数顺序提供参数,并且不能省略任何参数。
命名关键字参数的语法是在函数定义时,在参数列表中使用一个*
来分隔位置参数和命名关键字参数。
greet(name="Alice") # 输出:Hello, Alice
greet(name="Bob", message="Hi") # 输出:Hi, Bob
命名关键字参数的优点是:
- 显式地指定参数名可以提高代码的可读性,使函数调用更加清晰明确。
- 在函数定义时,可以为命名关键字参数提供默认值,从而使函数调用更加灵活。
- 在函数调用时,可以按照指定的参数顺序提供参数,避免参数顺序错误导致的bug。
2.3 特殊参数
def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
----------- ---------- ----------
| | |
| Positional or keyword |
| - Keyword only
-- Positional only
/
和 *
是可选的。这些符号表明形参如何把参数值传递给函数:位置、位置或关键字、关键字。关键字形参也叫作命名形参。
2.3.1 位置或关键字参数
函数定义中未使用 /
和 *
时,参数可以按位置或关键字传递给函数。
2.3.2 仅位置参数
此处再介绍一些细节,特定形参可以标记为 仅限位置。仅限位置 时,形参的顺序很重要,且这些形参不能用关键字传递。仅限位置形参应放在 /
(正斜杠)前。/
用于在逻辑上分割仅限位置形参与其它形参。如果函数定义中没有 /
,则表示没有仅限位置形参。
/
后可以是 位置或关键字 或 仅限关键字 形参。
2.3.3 仅限关键字参数
把形参标记为 仅限关键字,表明必须以关键字参数形式传递该形参,应在参数列表中第一个 仅限关键字 形参前添加 *
。
2.3.4 小结
可以按照以下的规范定义函数
def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
# 顺序:必选参数、默认参数、可变参数、关键字参数和命名关键字参数
说明:
-
使用仅限位置形参,可以让用户无法使用形参名。形参名没有实际意义时,强制调用函数的实参顺序时,或同时接收位置形参和关键字时,这种方式很有用。
-
当形参名有实际意义,且显式名称可以让函数定义更易理解时,阻止用户依赖传递实参的位置时,才使用关键字。
-
对于 API,使用仅限位置形参,可以防止未来修改形参名时造成破坏性的 API 变动。
2.4 任意实参列表(variadic 参数)
在Python中,可以使用*args
来接收任意数量的位置参数。*args
会将传入的位置参数打包成一个元组,可以在函数内部进行处理。
例:
def print_items(*args):
for item in args:
print(item)
print_items("apple", "banana", "orange")
在本例中,*args将传入的参数打包成为元组("apple", "banana", "orange"),并在函数内部进行迭代。
另外,如果想要接收任意数量的关键字参数,可以使用**kwargs
。**kwargs
会将传入的关键字参数打包成一个字典,可以在函数内部进行处理。
例:
def print_items(**kwargs):
for key, value in kwargs.items():
print(key + ": " + value)
print_items(fruit1="apple", fruit2="banana", fruit3="orange")
2.5 解包实参列表
定义:Python的解包实参列表是指将一个可迭代对象(如列表、元组、集合等)作为函数的参数,并使用 *
运算符将其解包为多个单独的参数传递给函数。
解释:Python函数调用要求独立的位置参数,但实参在列表或元组里时,要将元素进行解包拆分成单个元素例如,内置的 range() 函数要求独立的 start 和 stop 实参。如果这些参数不是独立的,则要在调用函数时,用 *
操作符把实参从列表或元组解包出来
def my_function(a, b, c):
print(a, b, c)
my_list = [1, 2, 3]
my_function(*my_list)
#输出结果
1 2 3
在上面的例子中,列表my_list
被解包为三个参数1、2和3,并作为实参传递给my_function
函数。函数my_function
会按照参数的顺序打印出这三个值。
2.6 Lambda表达式 (匿名函数)
格式:
lambda [arg1[,arg2],....argn]]:expression
在语法上,匿名函数只能是单个表达式。在语义上,它只是常规函数定义的语法糖。与嵌套函数定义一样,lambda 函数可以引用包含作用域中的变量:
def make_incrementor(n):
return lambda x: x + n
f = make_incrementor(42)
f(0) #输出42
f(1) #输出43
2.7 文档字符串
文档字符串(Docstring)是Python中的一种注释方式,用于描述函数、类、模块等代码元素的用途、功能和使用方法。文档字符串通常被放置在代码元素的定义之后,以三个连续的引号(''')或双引号(""")包围。
文档字符串可以包含多行文本,用于提供详细的描述和说明。它可以包括函数的参数、返回值、异常处理等信息,以及示例代码和用法示例。
例:
def greet(name):
"""
打印问候语
:param name: str,要问候的人的名字
:return: None
"""
print(f"Hello, {name}")
在这个示例中,文档字符串描述了函数的功能和参数,使用了参数的类型注解(type hinting)来指定参数的类型,以及返回值的类型。文档字符串还使用了冒号(:)和缩进来表示参数和返回值的说明。
文档字符串可以通过__doc__
属性来访问,例如:
print(greet.__doc__)
#输出
打印问候语
:param name: str,要问候的人的名字
:return: None
以下是文档字符串内容和格式的约定。
第一行应为对象用途的简短摘要。为保持简洁,不要在这里显式说明对象名或类型,因为可通过其他方式获取这些信息(除非该名称碰巧是描述函数操作的动词)。这一行应以大写字母开头,以句点结尾。
文档字符串为多行时,第二行应为空白行,在视觉上将摘要与其余描述分开。后面的行可包含若干段落,描述对象的调用约定、副作用等。
Python 解析器不会删除 Python 中多行字符串字面值的缩进,因此,文档处理工具应在必要时删除缩进。这项操作遵循以下约定:文档字符串第一行 之后 的第一个非空行决定了整个文档字符串的缩进量(第一行通常与字符串开头的引号相邻,其缩进在字符串中并不明显,因此,不能用第一行的缩进),然后,删除字符串中所有行开头处与此缩进“等价”的空白符。不能有比此缩进更少的行,但如果出现了缩进更少的行,应删除这些行的所有前导空白符。转化制表符后(通常为 8 个空格),应测试空白符的等效性。