一、函数基础简单来说,函数就是程序中可以运行一次或多次的Python语句的组合。Python中的函数在其他语言中也称为过程或子例程,因此这些包装语句由函数名调用。有了函数,我们就可以大大减少复制粘贴代码的次数(相信很多人一开始都有这种体会)。我们可以提取相同的代码来制作一个函数,只需要在需要的地方调用它。那么,这就提高了代码的复用率,整体代码看起来更简洁,不那么臃肿。函数是Python中最基本的程序结构,用来最大限度的复用我们的代码;同时,函数可以将一个错综复杂的系统分割成易于管理的部分,简化编程,代码重用。接下来,让我们看看函数是什么以及它应该如何定义。有两种定义函数的方法,即def和lambda关键字。1.函数定义我们总结一下为什么要用函数?尽量减少代码重用,尽量减少冗余代码;过程分解(拆卸)。将一个复杂的任务分解成多个较小的任务。函数定义的语法是:根据上面的定义,可以简单描述为:Python中的函数是一个语句块,有0个或多个参数,几行语句和一个返回值(返回值是可选的)(注意缩进)。然后我们定义一个比较简单的函数,没有参数,进入ipython交互环境:调用(执行)函数:我们发现hello()函数没有return语句。在Python中,如果return不是显式执行的语句,则函数的返回值默认为None。我们说过定义函数有两种形式,另一种形式是使用lambda定义的。使用lambda定义的函数是匿名函数,后面会解释,这里暂时不展示。2.函数参数在定义函数时,我们确定了参数的名称和位置,函数的接口定义就完成了。对于函数的调用者来说,知道如何传递正确的参数以及函数将返回什么值就足够了。函数内部的复杂逻辑被封装了,调用者不需要知道。Python的函数定义很简单,但是很灵活。除了通常定义的强制参数外,还可以使用默认参数、可变参数和关键字参数,这样函数定义的接口不仅可以处理复杂的参数,还可以简化调用者的代码。1.默认参数默认参数使API简洁而不失灵活性。当一个参数有默认值时,调用时如果不传这个参数,就会使用默认值。默认参数有一个陷阱,就是非默认参数应该放在默认参数的前面(否则Python解释器会报语法错误)。允许有多个默认参数,但需要将默认参数放在参数列表的第一侧。这个函数有问题。(函数中的形参是全局变量?lst在append函数中叫做lst,但是在全局范围内,我们不知道lst叫什么名字。)修改后的函数是:一般来说,当defaultparameterisoptional更改时,我们需要特别注意作用域的问题。我们需要上述技巧(不可变数据类型按值传递,可变数据类型按引用传递。)。当前可变对象有list、dict、set、bytearray。默认参数很好用,但是如果使用不当,也会掉坑里。默认参数有个***坑,演示如下:当我们正常调用的时候,结果好像还不错。当我们用默认参数调用的时候,一开始结果也是正确的,但是当我们再次调用add_end()时,结果就错了。原因解释如下:Python函数定义时,会计算默认参数L的值,即[],因为默认参数L也是一个变量,指向对象[],每次调用函数时,如果改变了值如果改变了L的内容,则下次调用时默认参数的内容会改变,不再是函数定义时的[]。因此,在定义默认参数时要记住一件事:默认参数必须指向不可变对象!修改上面的例子,我们可以使用不变对象None,为什么要设计str和None这样的不变对象呢?因为不可变对象一旦创建,对象内部的数据就无法修改,减少了修改数据带来的错误。另外,由于对象没有变化,在多任务环境下读取对象时不需要加锁,同时读取也没有问题。我们在写程序的时候,如果可以设计一个不变对象,那就尽量把它设计成一个不变对象。2.位置参数我们先写一个计算x^2的函数:对于power(x)函数,参数x是一个位置参数。我们在调用power函数的时候,必须传入唯一的参数x:那么,如果我们要计算x^3呢?你可以再定义一个power3函数,但是如果你想计算x^4,x^5,x^n呢?我们不可能定义多个函数,我们可以将power(x)修改为power(x,n)来计算x^n,按我们说的写就可以了:3.关键字参数可变参数让我们可以通过Enter0或任意数量的参数,这些可变参数在调用函数时会自动组装成一个元组。关键字参数允许你传入0或者任何包含参数名的参数,这些关键字参数会在函数内部自动组装成一个dict。示例如下:除了必选参数name和age,函数person还接受关键字参数kwargs。调用这个函数时,可以只传入需要的参数:也可以传入任意多个关键字参数:关键字参数有什么用?它可以扩展函数的功能。比如在person函数中,我们保证能收到name和age这两个参数,但是如果调用方愿意提供更多的参数,我们也可以收到。假设您正在执行用户注册功能。除了用户名和年龄是必填项外,其他都是可选的。使用关键字参数定义该函数即可满足注册要求。和可变参数类似,也可以先组装一个dict,然后把dict转成关键字参数传入:4.位置参数和关键字参数。位置参数和关键字参数是调用函数时的概念。在将默认参数与关键字参数组合时很有用。关键字参数必须写在位置参数之后,否则会抛出语法错误。位置参数和关键字参数可以共存,但是关键字参数必须写在位置参数之后。5.可变位置参数可变位置参数用*定义,在函数体中,可变位置参数是一个元组。可变位置参数。在python函数中,也可以定义可变参数。可变参数是指传入的参数个数是可变的。6.可变关键字参数可变关键字参数使用**定义。在函数体中,变量关键字参数是一个字典。variable关键字参数的key均为字符串,符合标识符定义规范。可变位置参数只能作为位置参数调用可变关键字参数只能作为关键字参数调用可变位置参数必须在可变关键字参数之前一起出现7.参数组合在Python中定义函数,可以使用强制参数、默认参数、变量parameters和关键字参数,这4个参数可以一起使用,也可以只使用其中的一部分,但是请注意parameters的定义顺序必须是:强制参数,默认参数,可变参数,关键字参数。例如,如果你定义了一个包含以上四种类型参数的函数:当函数被调用时,Python解释器会根据参数位置和参数名称自动分配相应的参数。参数传入,最神奇的是通过一个tuple和dict,我们还可以调用函数:所以,对于任何一个函数,都可以通过类似func(*args,**kwargs)的形式调用,不管它的参数是如何定义的。8.参数解构参数解构发生在调用函数时,定义函数时出现变量参数。参数解构有两种形式,一种是位置参数解构,另一种是关键字参数解构。参数结构的两种形式:位置参数解构,使用星号。解构后的对象是一个可迭代对象,解构后的结果是一个位置参数。关键字参数解构,使用两个星号。解构后的对象是字典,解构后的结果是关键字参数。位置参数解构的一个例子:让我们看一下字典解构的例子:参数解构发生在调用函数时。解构时,线性结构的解构是位置参数,字典的解构是关键字参数。传参顺序:位置参数,线性结构解构;关键字参数,字典解构。除非你真的知道你在做什么,否则尽可能少地使用这两种解构。9.Python3中引入的Parameterslot(keyword-onlyparameter)。如果你想强制传入的参数是关键字参数:参数槽通常与默认参数一起使用。举几个例子:参数槽的坑:*非命名参数后面必须有一个参数。当非命名参数有默认值时,命名参数可以没有默认值。默认参数应该在每个参数的底部。使用参数槽时,不能使用可变位置参数,可变键参数必须放在命名参数之后3.高级用法1.递归函数在函数内部,可以调用其他函数。如果函数在内部调用自身,则该函数是递归的。递归函数使用递归函数的优点是逻辑简单明了,缺点是调用太深会导致栈溢出。针对尾递归优化的语言可以通过尾递归防止栈溢出。尾递归其实就相当于循环,没有循环语句的编程语言只能通过尾递归来实现循环。2.匿名函数lambdapython使用lambda创建匿名函数。lambda只是一个表达式,函数体比def要简单的多。lambda的主体是一个表达式,而不是代码块。lambda表达式只能封装有限的逻辑。lambda函数有自己的命名空间,不能访问自己的参数列表之外或全局命名空间中的参数。虽然看起来lambda函数只能写一行,但并不等同于C或C++的内联函数。后者的目的是通过在调用小函数时不占用栈内存来提高运行效率。例3.Python函数中的多态性一个操作的意义取决于被操作对象的类型:4.总结Python函数具有非常灵活的参数形式,可以实现简单的调用,也可以传入非常复杂的参数。默认参数必须使用不可变对象。如果是可变对象,运行就会出现逻辑错误!注意定义可变参数和关键字参数的语法:*args为可变参数,args接收一个元组;**kwargs是关键字参数,kwargs接收的是一个dict。以及调用函数时如何传入可变参数和关键字参数的语法:可以直接传入可变参数:func(1,2,3),也可以先组装一个list或tuple,再传入*参数:函数(*(1、2、3));关键字参数可以直接传入:func(a=1,b=2),也可以先组装,再通过kwargs传入:func({'a':1,'b':2})。使用*args和**kwargs是Python惯用的写法。当然也可以用其他的参数名,但是***用的是成语。
