python散装笔记——33: 函数(1)


Parameter

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)
  • arg2kwarg2 是可选参数。如果要更改值,则适用与 arg1(位置或关键字)和 kwarg1(仅关键字)相同的规则。
  • *args 可以捕捉额外的位置参数。但请注意,必须提供 arg1arg2 作为位置参数,才能将参数传递给 *args:func(1, 1, 1, 1)
  • **kwargs 会捕获所有附加的关键字参数。在这种情况下,任何参数都不是 arg1arg2kwarg1kwarg2。例如: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 通常用于简短函数,方便在调用时定义(通常与 sortedfiltermap一起使用)。

例如,该行对字符串列表进行排序,忽略大小写,并忽略开头和结尾的空白:

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 语句 ”的唯一优势(即可以嵌入到更大的表达式中)。

相关文章

Python-1

在Jupyter 中显示txt文件的内容:linux ---> !cat tmp.txtwindowx ---> !more tmp.txt安装 Anacondaconda --->...

苹果M1电脑真实编程测试之python篇-60个项目最全测试

苹果M1 mac电脑发售有一段时间,已经有多个性能测试软件对M1进行了测试,苹果M1跑分不俗。在geekbench上M1 Mac电脑甚至已经登顶Mac单核性能榜首。那么在真实的编程环境中,M1 mac...

Python写每天进步1%的力量

离别了学生时代,步入了职场,你还记得你离上一次打开书本是在什么时候吗?认真上课,看学习视频,静下心来,虽唾手可得,却似乎离我们越来越远。每天忙着忙着,不知道自己忙什么,只是连坐下来停下思考5分钟的时间...

Python教程:第1篇 Python语言零基础从入门到高阶教程综述

目录前言本教程适合您吗?版权声明教程预告前言您好!欢迎您阅读本系列教程的第一篇文章。本教程是笔者准备写的系统化的、深入浅出的Python零基础从入门到高阶教程。零基础是指您可以没有任何编程经验,高阶是...

Python入门,一定要吃透这69个内置函数

内置函数就是Python预先定义的函数,这些内置函数使用方便,无需导入,直接调用,大大提高使用者的工作效率,也更便于程序的阅读。截止到Python版本3.9.1,Python一共提供了69个内置函数。...

使用 Python 编写 SolidWorks 脚本 01

大家好,欢迎来到我的频道。在本系列视频中,我将讨论使用Python编写Solidworks脚本。在开始之前,我将使用Solidworks2.21。对于Python IDE,我将使用Visual Stu...