+关注
已关注

分类  

暂无分类

标签  

暂无标签

日期归档  

2019-08(47)

2019-09(90)

2019-10(13)

2019-11(9)

2019-12(19)

荐【Python】函数与控制流(二)

发布于2020-05-30 14:32     阅读(242)     评论(1)     点赞(21)     收藏(6)


函数是什么.

编程语言中定义的函数不同于离散数学(以及其他几乎所有数学学科中提到的函数),我们所说的函数是一段具有特定功能、可重用的语句组,用函数名作为标识符并调用。如果我们将y=F(x)中的x看作输入,y看作输出就很容易理解编程语言中的函数是什么意思了(绝大多数人应该都是先学的数学再接触编程的吧).每一次我们使用函数可以提供不同的实际参数作为输入,以实现对不同数据的处理功能;函数执行完成后,还可以反馈相应的处理结果。函数就像一个黑盒,它告诉你它可以完成哪些功能,但对你屏蔽了其内部的实现原理,事实上80%的情况下我们也没有必要细致了解这些原理,只需要知道函数的输入输出方式即可。一言以蔽之,函数是一种功能抽象。
那么我们使用函数的目的是什么呢?不是为了表明自己学过编程技术,而是为了代码重用、降低编程难度。函数可以在需要它的地方被调用执行,不同的函数完成各自不同的功能,而不是将所有的功能一锅乱炖。函数也使得同样的代码不需要在每个执行的地方被重复地编写。Python中使用保留字(reserved word,指在高级语言中已经定义过的字,使用者不能再将这些字作为变量名或过程名使用)def来定义函数,具体的语法形式如下:

def Function_Name(Parameter_List)
    Function_Body
    return Return_Value_List

函数名Function_Name可以是任意有效的Python标识符(注意不要重名即可);参数列表Parameter_List是调用函数时传递给它的值,可以有0个或多个,参数之间用逗号分隔,括号不可省略(定义与调用都是如此);Function_Body是函数的主体部分,是该函数被调用时执行的代码;当函数需要返回值时,可以使用保留字return和返回值列表Return_Value_List来完成返回功能,不需要返回值的函数可以没有return功能,自动返回None。函数体执行完毕的时候,被调用函数将控制权返回给施调函数。下面我们看一个简短的函数定义:

def SayHello(name):
    '''
    Say hello to people refered by "name".
    '''
    print("Hello, dear %s!"%name)
SayHello("Esperanto")

在这里插入图片描述
细致的人会注意到在函数体之前我们写了一段注释,这段注释有它的名称,叫做文档字符串,它可以为你自己或者别的人日后看到这段函数时更好更快地了解它的功能,下面的代码展示如何查看这段信息(因为不是所有的情况下你都拥有程序的源代码):

def SayHello(name):
    '''
    Say hello to people refered by "name".
    '''
    print("Hello, dear %s!"%name)
SayHello("Esperanto")
print(SayHello.__doc__)
#Note that 'SayHello.__doc__' has two line in each.

在这里插入图片描述
接触过至少一门高级语言的人对于函数的调用方式都不会陌生,所以这里我们就不再浪费时间,直接来看看Python中的函数有哪些特殊语法,语法的细节如何操作。

函数的参数传递.

这里又不得不跳出Python的范围,来说说参数传递的具体方式。一提到参数传递方式,又不得不提“臭名昭著”的C++.C++中可以按值传递,意思是函数收到的只是实际参数的副本,副本的值被函数进行计算等操作,而对于原变量的值并没有影响。我们可以看一段C++代码:

#include <iostream>
using namespace std;

void add(int x)
{
    x=x+1;
    cout<<"X now is "<<x<<endl;
}

int main()
{
    int a=9;
    add(a);
    cout<<"However a is still "<<a<<endl;
    return 0;
}

在这里插入图片描述
这里为了让大家不至于过于迷惑,没有选择将形式参数的名字设置成和实际参数一样,当初夏大师讲C语言的时候,必然是一顿操作。OK,这里我们看到add()函数中接收到a作为参数,+1之后输出了10,而main()函数中输出的a依旧是9,说明了什么?x和a是两个世界的变量,x仅仅是复制了一份a的值用于自己的计算,而对于a变量本身,x或者说add()函数并没有办法改变它,或者我们说得专业点,add()函数没有途径访问到a变量(在add()函数中用指针胡乱修改,碰巧访问到a的情况就别说了)。这样的方式叫做按值传递(call by value).另一种方式叫做传地址,我们再看一段C++代码:

#include <iostream>
using namespace std;

void swap(int* x,int* y)
{
    int temp=*x;
    *x=*y;
    *y=temp;
}

int main()
{
    int a=9;
    int b=10;
    cout<<"a:"<<a<<"    b:"<<b<<endl;
    swap(&a,&b);
    cout<<"a:"<<a<<"    b:"<<b<<endl;
    return 0;
}

在这里插入图片描述
我们发现,这一次swap()函数和add()函数的行为不太一样,swap()函数似乎有权限来对a、b进行访问和修改。这其实不是权限的问题,而是参数传递方式的问题,这里我们将两个参数声明为int*,也就是指向int的指针类型。我们在Set集合类型里提到过,指针变量也是一种变量,它的值就是别人的地址,所以我们在调用swap()函数时,直接传过来a和b两个变量的地址。都已经有了地址,swap()能够访问到这两个变量是很合理的事。就像我已经有了你的地址,给你寄点土特产也是很合理的事。至于main()方法中swap(&a,&b);这一语句中&用于取变量地址的细节以及swap()函数中的实现细节,我们不再过多陈述。事实上,C++中除了传值,传地址(call by reference)之外,还可以按引用传递,传递引用的效果和传地址一致,区别我们稍后再说,先看代码:

#include <iostream>
using namespace std;

void swap(int& x,int& y)
{
    int temp=x;
    x=y;
    y=temp;
}

int main()
{
    int a=9;
    int b=10;
    int& r_a=a;
    int& r_b=b;
    cout<<"a:"<<a<<"    b:"<<b<<endl;
    swap(r_a,r_b);
    cout<<"a:"<<a<<"    b:"<<b<<endl;
    return 0;
}

在这里插入图片描述
可以看到,按引用传递确实可以做到对变量的访问。那么引用变量到底是什么呢,我们可以理解为引用变量是被引用变量的别名,对引用变量的操作就等价于对原变量的操作。所以,在swap()中能够改变a和b的值,也是很合理的。引用和指针的区别在于,引用真的只是引用一下,并没有事实上生成一个变量,而指针却是实在地生成了指针变量来存放地址值。至于C++中引用变量的细节,以及&运算符这里的作用似乎又不同于取得变量地址等等细节,都是属于C++的语法,有机会我们会细说。
至此我们已经说完了参数传递的几乎所有内容,虽然没有具体到每一行代码的细节,但肯定已经有了传值、传地址等概念。下面我们就来看看,Python中的传参方式究竟是怎样的。

def add(x):
    x=x+1
    print(x)
x=9
add(x)
print(x)

在这里插入图片描述
看这段代码的运行结果,似乎Python的传参方式是传地址。别急着下结论,我们再看一段代码。

def append(list,x):
    list.append(x)
    print(list)
list=[1,23,5]
x=9
append(list,x)
print(list)

在这里插入图片描述
当变量换成List列表类型,append()又能够访问并且修改它的内容了。所以到底Python的传参方式是怎样的。我们忽略了一件事情,Python中是有可变对象和不可变对象之分的,我们之前提到过可变对象和不可变对象在我们想要对它进行修改时的行为差别。并且我们曾经说过,Python的理念的是“一切都是对象”,所以Python的参数传递是传值。Python(以及Java)中的变量类似于C++指针、引用的工作方式,它们都是指向实际上数据的内存空间,而不是直接将数据放在自己的内存空间里。所以在参数是不可变对象时,我们把实际参数指向地内存空间告诉了形式参数,形式参数也指向了那片内存空间,但我们要修改的时候发现,这是个不可变对象,只能形式参数自己去寻找修改后的值并且把自己指向它,而原变量依旧是那个原变量;相应的,可变对象List的值——它的数据所在的内存空间告诉了形式参数之后,形式参数发现自己可以修改,所以修改成功也就顺利成章了。我知道这里听起来有些乱,因为它像是传地址的方式,但仔细想想,你在传递参数时,有取过变量的地址吗?没有吧。
我们看一段代码,或许可以清楚些。

def add(x):
    print(id(x))
    x=x+1
    print(x)
    print(id(x))
x=9
add(x)
print(x)
print(id(x))

在这里插入图片描述
可以看到,当形式参数的值为9时,它的id和实际参数一致,而我们修改形式参数为10以后,它的id也相应的变了。印证了我们关于Python变量在函数中行为的描述。实际上C++的传地址也可以理解为一种传值,毕竟指针的值就是别人的地址。而&a这样的操作得到的就是指针常量(int整型可以有常量,指针当然也可以),所以&a是不能被赋值的(它没有右值,只有左值),int *p这样的就是指针变量,它可以被赋值,在不同的时间存放不同的地址。
下面我们谈谈Python中的调用函数时的具体情况:

def printInfo(name,score):
    print("Name:%s-Score:%d"%(name,score))
printInfo("Esperanto",5)

在这里插入图片描述
这就是最简单的按顺序传递参数,实际上还可以有默认参数,我们在定义时就给定某个参数的值,当用户没有传入这一参数时,就是用我们给定的默认值:

def printInfo(name,score=0):
    print("Name:%s-Score:%d"%(name,score))
printInfo("Esperanto")

在这里插入图片描述
由于Python很多第三方库的函数中,参数列表都长得令人头皮发麻,按名称对应参数传递就极其方便:

def printInfo(a,b,c,d,e):
    print(a)
    print(b)
    print(c)
    print(d)
    print(e)
printInfo(e=1,d=2,c=3,b=4,a=5)

在这里插入图片描述
不知道大家注意过没有,我们使用print()函数时,从来没有注意过它接受几个参数,实际上print()函数使用了不定长参数,可以给用户提供可变长度的参数选择。

def printInfo(a,*b):
    sum=0
    sum=sum+a
    for i in b:
        sum=sum+i
    print(sum)
    print(type(b))
printInfo(1,2)
printInfo(1,2,3)
printInfo(1,2,4,5)
printInfo(1,2,6,7,8,9,10,11)

在这里插入图片描述
可以看到我们的求和函数,对于不同长度的参数输入,都能够正确地完成任务。不定长参数传入之后,在被调用函数中是以元组的形式保存的,实际上我们也可以让不定长参数以字典的形式在被调用函数中被保存:

def printInfo(a,**c):
    print(a,c)
    sum=0
    sum=sum+a
    for key in c:
        sum=sum+c[key]
    print(sum)
    print(type(c))
printInfo(1,x=2)
printInfo(1,x=2,y=3)
printInfo(1,x=2,y=4,z=5)

在这里插入图片描述
使用不定长参数需要注意的就是,不定长参数的声明一定要在参数列表的最后,无论是*b表示的元组存放不定长参数还是**c表示的字典存放不定长参数。

函数的返回.

函数调用时由施调函数通过被调函数名向被调函数传入数据,而被调函数返回时通过return语句向施调函数返回数据。熟悉高级语言的人应该不会陌生return语句,所以我们就不赘述普通的return使用方法了。在Python中,元组类型的使用相当灵活,前面提到过变量的批量赋值,函数的不定长参数,以及我们接下来要说的多返回值return语句。

def deal(a,b):
    return a+b,a-b,a*b,a/b
print(deal(1,2))
print(type(deal(1,2)))

在这里插入图片描述
如我们所想,deal()函数的返回值包含多个数据项,并且是元组的形式。更加特殊的,函数还可以返回函数名,这一部分我们在接下来的闭包概念中,结合代码进行详细叙述。

原文链接:https://blog.csdn.net/weixin_44246009/article/details/106430780



所属网站分类: 技术文章 > 博客

作者:dfh8374

链接: https://www.pythonheidong.com/blog/article/397888/

来源: python黑洞网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

21 1
收藏该文
已收藏

评论内容:(最多支持255个字符)