python散装笔记——33: 函数(1)
| Details |
arg1, ..., argN | 常规参数 |
*args | 未命名的位置参数 |
kw1, ..., kwN | 仅关键词参数 |
**kwargs | 其余的关键字参数 |
Python 中的函数提供了有组织、可重用和模块化的代码来执行一系列特定的操作。函数简化了编码过程,避免了冗余逻辑,并使代码更容易遵循。本主题介绍 Python 中函数的声明和使用。
Python 有许多内置函数,如 print()、input() 和 len()。除了内置函数,您还可以创建自己的 这些函数被称为用户定义的函数。
1: 定义和调用简单函数
使用 def 语句是在 python 中定义函数最常用的方法。该语句是所谓的单子句复合语句,语法如下:
def function_name(parameters):
statement(s)
function_name 被称为函数的标识符。由于函数定义是一条可执行语句,它的执行将函数名与函数对象绑定,以后可以使用标识符调用函数。
parameters 是一个可选的标识符列表,当函数被调用时,这些标识符将与作为参数提供的值绑定。一个函数可以有任意数量的参数,参数之间用逗号隔开。
statement(s) 语句(也称为函数体)是每次调用函数时执行的非空语句序列。这意味着函数体不能为空,就像任何缩进块一样。
下面是一个简单函数定义的示例,其目的是在每次调用时打印 Hello:
def greet():
print("Hello")
现在让我们调用已定义的 greet() 函数:
greet()
# Out: Hello
这是另一个函数定义的例子,它只接受一个参数,每次调用函数时都会显示传入的值:
def greet_two(greeting):
print(greeting)
之后,必须调用带有参数的 greet_two() 函数:
greet_two("Howdy")
# Out: Howdy
此外,您还可以为该函数参数设置默认值:
def greet_two(greeting="Howdy"):
print(greeting)
现在,您可以调用函数,而无需给出值,但是如果调用时给出参数的值,这个给定的值会取代默认的值:
greet_two()
# Out: Howdy
您会注意到,与许多其它语言不同,您不需要显式地声明函数的返回类型。Python 函数可以通过关键字 return 返回任何类型的值。一个函数可以返回任意数量的不同类型!
def many_types(x):
if x < 0:
return "Hello!"
else:
return 0
print(many_types(1))
print(many_types(-1))
# Output:
0
Hello!
只要调用者处理得当,这就是完全有效的 Python 代码。
执行结束时没有返回语句的函数总是返回 None:
def do_nothing():
pass
print(do_nothing())
# Out: None
如前所述,函数定义必须有一个函数体,即一个非空的语句序列。因此,“pass ”语句被用作函数体,这是一个空操作--当它被执行时,什么也不会发生。它的作用是跳过。当语法上需要语句,但不需要执行代码时,它可以作为占位符。
2: 使用任意数量的参数定义函数
位置参数的任意个数:
定义一个可接受任意数量参数的函数时,可在其中一个参数前加上 “*”。
def func(*args):
# args 将是一个元组,包含中传递的所有数值
for i in args:
print(i)
func(1, 2, 3) # 使用 3 个参数调用
# Out: 1
# 2
# 3
list_of_arg_values = [1, 2, 3]
func(*list_of_arg_values) # 使用列表的值调用它,* 展开列表
# Out: 1
# 2
# 3
func() # 调用它时不带参数
# No Output
不能为 args 提供默认值,例如,func(*args=[1, 2, 3])会引发语法错误(甚至无法编译)。
在调用函数时,你不能通过名称提供这些参数,例如,func(*args=[1, 2, 3])将引发 TypeError。
但如果您已经将参数放入数组(或任何其他 Iterable),您可以像这样调用函数: func(*my_stuff)`。
这些参数(*args)可以通过索引访问,例如 args[0] 将返回第一个参数
任意数量的关键字参数
通过在定义参数时在参数前加上**(两个星号),可以使用任意数量的参数名:
def func(**kwargs):
# kwargs will be a dictionary containing the names as keys and the values as values
for name, value in kwargs.items():
print(name, value)
func(value1=1, value2=2, value3=3) # Calling it with 3 arguments
# Out: value1 1
# value2 2
# value3 3
func() # Calling it without arguments
# No Out put
my_dict = {'foo': 1, 'bar': 2}
func(**my_dict) # Calling it with a dictionary
# Out: foo 1
# bar 2
您不能提供没有名称的这些参数,例如,func(1, 2, 3) 会引发TypeError。
kwargs 是一个普通的本地 python 字典。例如,args['value1'] 将给出参数 value1 的值。请务必事先检查是否有这样一个参数,否则将引发 “KeyError”。
警告
您可以将这些参数与其他可选参数和必选参数混合使用,但定义中的顺序很重要。
- 位置/关键字**参数排在前面。(必填参数)。
- 然后是任意的 *arg 参数。(可选参数)。
- 然后是只包含关键字的参数。(必须参数)。
- 最后是任意关键字 **kwargs。(可选)。
# |-positional-|-optional-|---keyword-only--|-optional-|
def func(arg1, arg2=10 , *args, kwarg1, kwarg2=2, **kwargs):
pass
- 必须给出 arg1,否则将引发 TypeError。参数可以是位置参数(func(10))或关键字参数(func(arg1=10))。
- 还必须给出 kwarg1,但只能作为关键字参数:func(kwarg1=10)。
- arg2 和 kwarg2 是可选参数。如果要更改值,则适用与 arg1(位置或关键字)和 kwarg1(仅关键字)相同的规则。
- *args 可以捕捉额外的位置参数。但请注意,必须提供 arg1 和 arg2 作为位置参数,才能将参数传递给 *args:func(1, 1, 1, 1)。
- **kwargs 会捕获所有附加的关键字参数。在这种情况下,任何参数都不是 arg1、arg2、kwarg1 或 kwarg2。例如:func(kwarg3=10)。
- 在 Python 3 中,可以单独使用 * 来表示所有后续参数都必须指定为关键字。例如,Python 3.5 及更高版本中的 math.isclose 函数是用 def math.isclose (a, b, *, rel_tol=1e-09, abs_tol=0.0) 定义的,这意味着前两个参数可以按位置提供,但可选的第三和第四参数只能作为关键字参数提供。
Python 2.x 不支持仅关键字参数。这种行为可以用 kwargs 来模拟:
def func(arg1, arg2=10, **kwargs):
try:
kwarg1 = kwargs.pop("kwarg1")
except KeyError:
raise TypeError("missing required keyword-only argument: 'kwarg1'")
kwarg2 = kwargs.pop("kwarg2", 2)
# function body ...
命名注意事项
可选位置参数 args 和可选关键字参数 kwargs 的命名约定只是一种约定,您可以使用任何您喜欢的名称,但遵循约定是非常有用的,这样别人就知道您在做什么,甚至您自己以后也知道,所以请遵循约定。
关于唯一性的说明
任何函数都可以用个或一个*args和个或一个**kwargs来定义,但每种参数都不能超过一个。此外,*args 必须是最后一个位置参数,**kwargs 必须是最后一个参数。尝试使用多于一个的参数将导致语法错误异常。
带可选参数的函数嵌套注意事项
可以嵌套此类函数,通常的做法是删除代码已经处理过的项目,但如果要向下传递参数,则需要传递带*前缀的可选位置参数和带**前缀的可选关键字参数,否则 args 将以列表或元组的形式传递,kwargs 将以单个字典的形式传递:
def fn(**kwargs):
print(kwargs)
f1(**kwargs)
def f1(**kwargs):
print(len(kwargs))
fn(a=1, b=2)
# Out:
# {'a': 1, 'b': 2}
# 2
3: Lambda(内联/匿名)函数
lambda 关键字创建的内联函数包含一个表达式。这个表达式的值就是函数被调用时的返回值。
请看下面的函数
def greeting():
return "Hello"
当被调用为:
print(greeting())
打印结果:
Hello
这可以写成如下的 lambda 函数:
greet_me = lambda: "Hello"
请参阅本节底部关于将 lambdas 赋值给变量的说明。一般来说,不要这样做。
这会创建一个名为 greet_me 的内联函数,返回 Hello。请注意,使用 lambda 创建函数时不需要写入 return。在 : 后面的值会自动返回。
一旦赋值给变量,它就可以像普通函数一样使用:
print(greet_me())
打印结果:
Hello
lambda也可以接受参数:
strip_and_upper_case = lambda s: s.strip().upper()
strip_and_upper_case(" Hello ")
返回字符串:
HELLO
与普通函数一样,它们也可以接受任意数量的参数/关键字参数。
greeting = lambda x, *args, **kwargs: print(x, args, kwargs)
greeting('hello', 'world', world='world')
打印结果:
hello ('world',) {'world': 'world'}
lambdas 通常用于简短函数,方便在调用时定义(通常与 sorted、filter 和 map一起使用)。
例如,该行对字符串列表进行排序,忽略大小写,并忽略开头和结尾的空白:
sorted( [" foo ", " bAR", "BaZ "], key=lambda s: s.strip().upper())
# Out:
# [' bAR', 'BaZ ', ' foo ']
对列表进行排序,忽略空格:
sorted( [" foo ", " bAR", "BaZ "], key=lambda s: s.strip())
# Out:
# ['BaZ ', ' bAR', ' foo ']
map的示例:
sorted( map( lambda s: s.strip().upper(), [" foo ", " bAR", "BaZ "]))
# Out:
# ['BAR', 'BAZ', 'FOO']
sorted( map( lambda s: s.strip(), [" foo ", " bAR", "BaZ "]))
# Out:
# ['BaZ', 'bAR', 'foo']
数字列表的示例:
my_list = [3, -4, -2, 5, 1, 7]
sorted( my_list, key=lambda x: abs(x))
# Out:
# [1, -2, 3, -4, 5, 7]
list( filter( lambda x: x>0, my_list))
# Out:
# [3, 5, 1, 7]
list( map( lambda x: abs(x), my_list))
# Out:
[3, 4, 2, 5, 1, 7]
我们可以从 lambda 函数内部调用其他函数(带/不带参数均可)。
def foo(msg):
print(msg)
greet = lambda x = "hello world": foo(x)
greet()
打印结果:
hello world
这很有用,因为 lambda 可能只包含一个表达式,而使用附属函数可以运行多个语句。
注意
请记住,PEP-8(Python 官方风格指南)不推荐将 lambdas 赋值给变量(就像我们在前两个示例中所做的):
始终使用 def 语句,而不是直接将 lambda 表达式与标识符绑定的赋值语句。
正确:
def f(x): return 2*x
错误:
f = lambda x: 2*x
第一种形式表示生成的函数对象的名称是特定的 f,而不是通用的 <lambda>。这对一般的回溯和字符串表示更有用。赋值语句的使用消除了 lambda 表达式相对于显式 “def 语句 ”的唯一优势(即可以嵌入到更大的表达式中)。